updating to use version of next release.
[civicrm-core.git] / CRM / Upgrade / Incremental / php / FourFive.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License along with this program; if not, contact CiviCRM LLC |
21 | at info[AT]civicrm[DOT]org. If you have questions about the |
22 | GNU Affero General Public License or the licensing of CiviCRM, |
23 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
24 +--------------------------------------------------------------------+
25 */
26
27 /**
28 *
29 * @package CRM
30 * @copyright CiviCRM LLC (c) 2004-2014
31 * $Id$
32 *
33 */
34 class CRM_Upgrade_Incremental_php_FourFive {
35 const BATCH_SIZE = 5000;
36
37 /**
38 * @param $errors
39 *
40 * @return bool
41 */
42 function verifyPreDBstate(&$errors) {
43 return TRUE;
44 }
45
46 /**
47 * Compute any messages which should be displayed beforeupgrade
48 *
49 * Note: This function is called iteratively for each upcoming
50 * revision to the database.
51 *
52 * @param $preUpgradeMessage
53 * @param $rev string, a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'
54 * @param null $currentVer
55 *
56 * @internal param string $postUpgradeMessage , alterable
57 * @return void
58 */
59 function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
60 }
61
62 /**
63 * Compute any messages which should be displayed after upgrade
64 *
65 * @param $postUpgradeMessage string, alterable
66 * @param $rev string, an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs
67 * @return void
68 */
69 function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
70 if ($rev == '4.5.alpha1') {
71 $postUpgradeMessage .= '<br /><br />' . ts('Default versions of the following System Workflow Message Templates have been modified to handle new functionality: <ul><li>Contributions - Receipt (off-line)</li><li>Contributions - Receipt (on-line)</li><li>Contributions - Recurring Start and End Notification</li><li>Contributions - Recurring Updates</li><li>Memberships - Receipt (on-line)</li><li>Memberships - Signup and Renewal Receipts (off-line)</li><li>Pledges - Acknowledgement</li></ul> If you have modified these templates, please review the new default versions and implement updates as needed to your copies (Administer > Communications > Message Templates > System Workflow Messages). (<a href="%1">learn more...</a>)', array(1 => 'http://wiki.civicrm.org/confluence/display/CRMDOC/Updating+System+Workflow+Message+Templates+after+Upgrades+-+method+1+-+kdiff'));
72 $postUpgradeMessage .= '<br /><br />' . ts('This release allows you to view and edit multiple-record custom field sets in a table format which will be more usable in some cases. You can try out the format by navigating to Administer > Custom Data & Screens > Custom Fields. Click Settings for a custom field set and change Display Style to "Tab with Tables".');
73 $postUpgradeMessage .= '<br /><br />' . ts('This release changes the way that anonymous event registrations match participants with existing contacts. By default, all event participants will be matched with existing individuals using the Unsupervised rule, even if multiple registrations with the same email address are allowed. However, you can now select a different matching rule to use for each event. Please review your events to make sure you choose the appropriate matching rule and collect sufficient information for it to match contacts.');
74 }
75 if ($rev == '4.5.beta2') {
76 $postUpgradeMessage .= '<br /><br />' . ts('If you use CiviMail for newsletters or other communications, check out the new sample CiviMail templates which use responsive design to optimize display on mobile devices (Administer > Communications > Message Templates ).');
77 }
78 if ($rev == '4.5.1') {
79 $postUpgradeMessage .= '<br /><br />' . ts('WARNING: If you use CiviCase with v4.5.alpha*, v4.5.beta*, or v4.5.0, it is possible that previous upgrades corrupted some CiviCase metadata. If you have not already done so, please identify any custom field sets, smart groups, or reports which refer to CiviCase and ensure that they are properly configured.');
80 }
81 }
82
83 /**
84 * @param $rev
85 *
86 * @return bool
87 */
88 function upgrade_4_5_alpha1($rev) {
89 // task to process sql
90 $this->addTask(ts('Migrate honoree information to module_data'), 'migrateHonoreeInfo');
91 $this->addTask(ts('Upgrade DB to 4.5.alpha1: SQL'), 'task_4_5_x_runSql', $rev);
92 $this->addTask(ts('Set default for Individual name fields configuration'), 'addNameFieldOptions');
93
94 // CRM-14522 - The below schema checking is done as foreign key name
95 // for pdf_format_id column varies for different databases
96 // if DB is been into upgrade for 3.4.2 version, it would have pdf_format_id name for FK
97 // else FK_civicrm_msg_template_pdf_format_id
98 $config = CRM_Core_Config::singleton();
99 $dbUf = DB::parseDSN($config->dsn);
100 $query = "
101 SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
102 WHERE TABLE_NAME = 'civicrm_msg_template'
103 AND CONSTRAINT_TYPE = 'FOREIGN KEY'
104 AND TABLE_SCHEMA = %1
105 ";
106 $params = array(1 => array($dbUf['database'], 'String'));
107 $dao = CRM_Core_DAO::executeQuery($query, $params, TRUE, NULL, FALSE, FALSE);
108 if ($dao->fetch()) {
109 if ($dao->CONSTRAINT_NAME == 'FK_civicrm_msg_template_pdf_format_id' ||
110 $dao->CONSTRAINT_NAME == 'pdf_format_id') {
111 $sqlDropFK = "ALTER TABLE `civicrm_msg_template`
112 DROP FOREIGN KEY `{$dao->CONSTRAINT_NAME}`,
113 DROP KEY `{$dao->CONSTRAINT_NAME}`";
114 CRM_Core_DAO::executeQuery($sqlDropFK, CRM_Core_DAO::$_nullArray, TRUE, NULL, FALSE, FALSE);
115 }
116 }
117
118 return TRUE;
119 }
120
121 /**
122 * @param $rev
123 *
124 * @return bool
125 */
126 function upgrade_4_5_beta9($rev) {
127 $this->addTask(ts('Upgrade DB to 4.5.beta9: SQL'), 'task_4_5_x_runSql', $rev);
128
129 $entityTable = array(
130 'Participant' => 'civicrm_participant_payment',
131 'Contribution' => 'civicrm_contribution',
132 'Membership' => 'civicrm_membership',
133 );
134
135 foreach ($entityTable as $label => $tableName) {
136 list($minId, $maxId) = CRM_Core_DAO::executeQuery("SELECT coalesce(min(id),0), coalesce(max(id),0)
137 FROM {$tableName}")->getDatabaseResult()->fetchRow();
138 for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
139 $endId = $startId + self::BATCH_SIZE - 1;
140 $title = ts("Upgrade DB to 4.5.beta9: Fix line items for {$label} (%1 => %2)", array(1 => $startId, 2 => $endId));
141 $this->addTask($title, 'task_4_5_0_fixLineItem', $startId, $endId, $label);
142 }
143 }
144 return TRUE;
145 }
146
147 /**
148 * (Queue Task Callback)
149 *
150 * Function to update the line items
151 *
152 *
153 * @param CRM_Queue_TaskContext $ctx
154 * @param $startId int, the first/lowest entity ID to convert
155 * @param $endId int, the last/highest entity ID to convert
156 * @param
157 *
158 * @return bool
159 */
160 static function task_4_5_0_fixLineItem(CRM_Queue_TaskContext $ctx, $startId, $endId, $entityTable) {
161
162 $sqlParams = array(
163 1 => array($startId, 'Integer'),
164 2 => array($endId, 'Integer'),
165 );
166 switch ($entityTable) {
167 case 'Contribution':
168 // update all the line item entity_table and entity_id with contribution due to bug CRM-15055
169 CRM_Core_DAO::executeQuery("UPDATE civicrm_line_item li
170 INNER JOIN civicrm_contribution cc ON cc.id = li.contribution_id
171 SET entity_id = li.contribution_id, entity_table = 'civicrm_contribution'
172 WHERE li.contribution_id IS NOT NULL AND li.entity_table <> 'civicrm_participant' AND (cc.id BETWEEN %1 AND %2)", $sqlParams);
173
174 // update the civicrm_line_item.contribution_id
175 CRM_Core_DAO::executeQuery("UPDATE civicrm_line_item li
176 INNER JOIN civicrm_contribution cc ON cc.id = li.entity_id
177 SET contribution_id = entity_id
178 WHERE li.contribution_id IS NULL AND li.entity_table = 'civicrm_contribution' AND (cc.id BETWEEN %1 AND %2)", $sqlParams);
179 break;
180
181 case 'Participant':
182 // update the civicrm_line_item.contribution_id
183 CRM_Core_DAO::executeQuery("UPDATE civicrm_line_item li
184 INNER JOIN civicrm_participant_payment pp ON pp.participant_id = li.entity_id
185 SET li.contribution_id = pp.contribution_id
186 WHERE li.entity_table = 'civicrm_participant' AND li.contribution_id IS NULL AND (pp.id BETWEEN %1 AND %2)", $sqlParams);
187 break;
188
189 case 'Membership':
190 $upgrade = new CRM_Upgrade_Form();
191 // update the line item of membership
192 CRM_Core_DAO::executeQuery("UPDATE civicrm_line_item li
193 INNER JOIN civicrm_membership_payment mp ON mp.contribution_id = li.contribution_id
194 INNER JOIN civicrm_membership cm ON mp.membership_id = cm.id
195 INNER JOIN civicrm_price_field_value pv ON pv.id = li.price_field_value_id
196 SET li.entity_table = 'civicrm_membership', li.entity_id = mp.membership_id
197 WHERE li.entity_table = 'civicrm_contribution'
198 AND pv.membership_type_id IS NOT NULL AND cm.membership_type_id = pv.membership_type_id AND (cm.id BETWEEN %1 AND %2)", $sqlParams);
199
200 CRM_Core_DAO::executeQuery("UPDATE civicrm_line_item li
201 INNER JOIN civicrm_membership_payment mp ON mp.contribution_id = li.contribution_id
202 INNER JOIN civicrm_price_field_value pv ON pv.id = li.price_field_value_id
203 SET li.entity_table = 'civicrm_membership', li.entity_id = mp.membership_id
204 WHERE li.entity_table = 'civicrm_contribution'
205 AND pv.membership_type_id IS NOT NULL AND (mp.membership_id BETWEEN %1 AND %2)", $sqlParams);
206
207 CRM_Core_DAO::executeQuery("INSERT INTO civicrm_line_item (entity_table, entity_id, price_field_id, label,
208 qty, unit_price, line_total, price_field_value_id, financial_type_id)
209 SELECT 'civicrm_membership', cm.id, cpf.id price_field_id, cpfv.label, 1 as qty, cpfv.amount, cpfv.amount line_total,
210 cpfv.id price_field_value_id, cpfv.financial_type_id FROM civicrm_membership cm
211 LEFT JOIN civicrm_membership_payment cmp ON cmp.membership_id = cm.id
212 INNER JOIN civicrm_price_field_value cpfv ON cpfv.membership_type_id = cm.membership_type_id
213 INNER JOIN civicrm_price_field cpf ON cpf.id = cpfv.price_field_id
214 INNER JOIN civicrm_price_set cps ON cps.id = cpf.price_set_id
215 WHERE cmp.contribution_id IS NULL AND cps.name = 'default_membership_type_amount' AND (cm.id BETWEEN %1 AND %2)", $sqlParams);
216 break;
217 }
218 return TRUE;
219 }
220
221 /**
222 * Add defaults for the newly introduced name fields configuration in 'contact_edit_options' setting
223 *
224 * @param CRM_Queue_TaskContext $ctx
225 *
226 * @return bool TRUE for success
227 */
228 static function addNameFieldOptions(CRM_Queue_TaskContext $ctx) {
229 $query = "SELECT `value` FROM `civicrm_setting` WHERE `group_name` = 'CiviCRM Preferences' AND `name` = 'contact_edit_options'";
230 $dao = CRM_Core_DAO::executeQuery($query);
231 $dao->fetch();
232 $oldValue = unserialize($dao->value);
233
234 $newValue = $oldValue . '12\ 114\ 115\ 116\ 117\ 1';
235
236 $query = "UPDATE `civicrm_setting` SET `value` = %1 WHERE `group_name` = 'CiviCRM Preferences' AND `name` = 'contact_edit_options'";
237 $params = array(1 => array(serialize($newValue), 'String'));
238 CRM_Core_DAO::executeQuery($query, $params);
239
240 return TRUE;
241 }
242
243 /**
244 * Migrate honoree information to uf_join.module_data as honoree columns (text and title) will be dropped
245 * on DB upgrade
246 *
247 * @param CRM_Queue_TaskContext $ctx
248 *
249 * @return bool TRUE for success
250 */
251 static function migrateHonoreeInfo(CRM_Queue_TaskContext $ctx) {
252 $query = "ALTER TABLE `civicrm_uf_join`
253 ADD COLUMN `module_data` longtext COMMENT 'Json serialized array of data used by the ufjoin.module'";
254 CRM_Core_DAO::executeQuery($query);
255
256 $honorTypes = array_keys(CRM_Core_OptionGroup::values('honor_type'));
257 $ufGroupDAO = new CRM_Core_DAO_UFGroup();
258 $ufGroupDAO->name = 'new_individual';
259 $ufGroupDAO->find(TRUE);
260
261 $query = "SELECT * FROM civicrm_contribution_page";
262 $dao = CRM_Core_DAO::executeQuery($query);
263
264 if ($dao->N) {
265 $domain = new CRM_Core_DAO_Domain;
266 $domain->find(TRUE);
267 while ($dao->fetch()) {
268 $honorParams = array('soft_credit' => array('soft_credit_types' => $honorTypes));
269 if ($domain->locales) {
270 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
271 foreach ($locales as $locale) {
272 $honor_block_title = "honor_block_title_{$locale}";
273 $honor_block_text = "honor_block_text_{$locale}";
274 $honorParams['soft_credit'] += array(
275 $locale => array(
276 'honor_block_title' => $dao->$honor_block_title,
277 'honor_block_text' => $dao->$honor_block_text,
278 ),
279 );
280 }
281 }
282 else {
283 $honorParams['soft_credit'] += array(
284 'default' => array(
285 'honor_block_title' => $dao->honor_block_title,
286 'honor_block_text' => $dao->honor_block_text,
287 ),
288 );
289 }
290 $ufJoinParam = array(
291 'module' => 'soft_credit',
292 'entity_table' => 'civicrm_contribution_page',
293 'is_active' => $dao->honor_block_is_active,
294 'entity_id' => $dao->id,
295 'uf_group_id' => $ufGroupDAO->id,
296 'module_data' => json_encode($honorParams),
297 );
298 CRM_Core_BAO_UFJoin::create($ufJoinParam);
299 }
300 }
301
302 return TRUE;
303 }
304
305 /**
306 * Upgrade function.
307 *
308 * @param string $rev
309 */
310 public function upgrade_4_5_9($rev) {
311 // Task to process sql.
312 $this->addTask(ts('Upgrade DB to 4.5.9: Fix saved searches consisting of multi-choice custom field(s)'), 'updateSavedSearch');
313
314 return TRUE;
315 }
316
317 /**
318 * Update saved search for multi-select custom fields on DB upgrade
319 *
320 * @param CRM_Queue_TaskContext $ctx
321 *
322 * @return bool TRUE for success
323 */
324 static function updateSavedSearch(CRM_Queue_TaskContext $ctx) {
325 $sql = "SELECT id, form_values FROM civicrm_saved_search";
326 $dao = CRM_Core_DAO::executeQuery($sql);
327 while ($dao->fetch()) {
328 $copy = $formValues = unserialize($dao->form_values);
329 $update = FALSE;
330 foreach ($copy as $field => $data_value) {
331 if (preg_match('/^custom_/', $field) && is_array($data_value) && !array_key_exists("${field}_operator", $formValues)) {
332 // Now check for CiviCRM_OP_OR as either key or value in the data_value array.
333 // This is the conclusive evidence of an old-style data format.
334 if(array_key_exists('CiviCRM_OP_OR', $data_value) || FALSE !== array_search('CiviCRM_OP_OR', $data_value)) {
335 // We have old style data. Mark this record to be updated.
336 $update = TRUE;
337 $op = 'and';
338 if(!preg_match('/^custom_([0-9]+)/', $field, $matches)) {
339 // fatal error?
340 continue;
341 }
342 $fieldID= $matches[1];
343 if (array_key_exists('CiviCRM_OP_OR', $data_value)) {
344 // This indicates data structure identified by jamie in the form:
345 // value1 => 1, value2 => , value3 => 1.
346 $data_value = array_keys($data_value, 1);
347
348 // If CiviCRM_OP_OR - change OP from default to OR
349 if($data_value['CiviCRM_OP_OR'] == 1) {
350 $op = 'or';
351 }
352 unset($data_value['CiviCRM_OP_OR']);
353 }
354 else {
355 // The value is here, but it is not set as a key.
356 // This is using the style identified by Monish - the existence of the value
357 // indicates an OR search and values are set in the form of:
358 // 0 => value1, 1 => value1, 3 => value2.
359 $key = array_search('CiviCRM_OP_OR', $data_value);
360 $op = 'or';
361 unset($data_value[$key]);
362 }
363
364 $formValues[$field] = $data_value;
365 $formValues["${field}_operator"] = $op;
366 }
367 }
368 }
369
370 if($update) {
371 $sql = "UPDATE civicrm_saved_search SET form_values = %0 WHERE id = %1";
372 CRM_Core_DAO::executeQuery($sql,
373 array(
374 array(serialize($formValues), 'String'),
375 array($dao->id, 'Integer'),
376 )
377 );
378 }
379 }
380 return TRUE;
381 }
382
383
384 /**
385 * (Queue Task Callback)
386 */
387 static function task_4_5_x_runSql(CRM_Queue_TaskContext $ctx, $rev) {
388 $upgrade = new CRM_Upgrade_Form();
389 $upgrade->processSQL($rev);
390
391 return TRUE;
392 }
393
394 /**
395 * Syntactic sugar for adding a task which (a) is in this class and (b) has
396 * a high priority.
397 *
398 * After passing the $funcName, you can also pass parameters that will go to
399 * the function. Note that all params must be serializable.
400 */
401 protected function addTask($title, $funcName) {
402 $queue = CRM_Queue_Service::singleton()->load(array(
403 'type' => 'Sql',
404 'name' => CRM_Upgrade_Form::QUEUE_NAME,
405 ));
406
407 $args = func_get_args();
408 $title = array_shift($args);
409 $funcName = array_shift($args);
410 $task = new CRM_Queue_Task(
411 array(get_class($this), $funcName),
412 $args,
413 $title
414 );
415 $queue->createItem($task, array('weight' => -1));
416 }
417 }