Merge pull request #6431 from JMAConsulting/CRM-16526
[civicrm-core.git] / CRM / Upgrade / Incremental / php / FourFour.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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 and the CiviCRM Licensing Exception. |
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 and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 * Upgrade logic for 4.4
30 */
31 class CRM_Upgrade_Incremental_php_FourFour extends CRM_Upgrade_Incremental_Base {
32 const MAX_WORD_REPLACEMENT_SIZE = 255;
33
34 /**
35 * Compute any messages which should be displayed beforeupgrade.
36 *
37 * Note: This function is called iteratively for each upcoming
38 * revision to the database.
39 *
40 * @param $preUpgradeMessage
41 * @param string $rev
42 * a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
43 * @param null $currentVer
44 *
45 * @return void
46 */
47 public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
48 if ($rev == '4.4.beta1') {
49 $apiCalls = self::getConfigArraysAsAPIParams(FALSE);
50 $oversizedEntries = 0;
51 foreach ($apiCalls as $params) {
52 if (!self::isValidWordReplacement($params)) {
53 $oversizedEntries++;
54 }
55 }
56 if ($oversizedEntries > 0) {
57 $preUpgradeMessage .= '<br/>' . ts("WARNING: There are %1 word-replacement entries which will not be valid in v4.4+ (eg with over 255 characters). They will be dropped during upgrade. For details, consult the CiviCRM log.", array(
58 1 => $oversizedEntries,
59 ));
60 }
61 }
62 }
63
64 /**
65 * Compute any messages which should be displayed after upgrade.
66 *
67 * @param string $postUpgradeMessage
68 * alterable.
69 * @param string $rev
70 * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs.
71 * @return void
72 */
73 public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
74 if ($rev == '4.4.1') {
75 $config = CRM_Core_Config::singleton();
76 if (!empty($config->useIDS)) {
77 $postUpgradeMessage .= '<br />' . ts("The setting to skip IDS check has been removed. Your site has this configured in civicrm.settings.php but it will no longer work. Instead, use the new permission 'skip IDS check' to bypass the IDS system.");
78 }
79 }
80 if ($rev == '4.4.3') {
81 $postUpgradeMessage .= '<br /><br />' . ts('Default versions of the following System Workflow Message Templates have been modified to handle new functionality: <ul><li>Events - Registration Confirmation and Receipt (on-line)</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).');
82 }
83 if ($rev == '4.4.3') {
84 $query = "SELECT cft.id financial_trxn
85 FROM civicrm_financial_trxn cft
86 LEFT JOIN civicrm_entity_financial_trxn ceft ON ceft.financial_trxn_id = cft.id
87 LEFT JOIN civicrm_contribution cc ON ceft.entity_id = cc.id
88 WHERE ceft.entity_table = 'civicrm_contribution' AND cft.payment_instrument_id IS NULL;";
89 $dao = CRM_Core_DAO::executeQuery($query);
90 if ($dao->N) {
91 $postUpgradeMessage .= '<br /><br /><strong>' . ts('Your database contains %1 financial transaction records with no payment instrument (Paid By is empty). If you use the Accounting Batches feature this may result in unbalanced transactions. If you do not use this feature, you can ignore the condition (although you will be required to select a Paid By value for new transactions). <a href="%2" target="_blank">You can review steps to correct transactions with missing payment instruments on the wiki.</a>', array(
92 1 => $dao->N,
93 2 => 'http://wiki.civicrm.org/confluence/display/CRMDOC/Fixing+Transactions+Missing+a+Payment+Instrument+-+4.4.3+Upgrades',
94 )) . '</strong>';
95 }
96 }
97 if ($rev == '4.4.6') {
98 $postUpgradeMessage .= '<br /><br /><strong>' . ts('Your contact image urls have been upgraded. If your contact image urls did not follow the standard format for image Urls they have not been upgraded. Please check the log to see image urls that were not upgraded.');
99 }
100 }
101
102 /**
103 * @param $rev
104 *
105 * @return bool
106 */
107 public function upgrade_4_4_alpha1($rev) {
108 // task to process sql
109 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.4.alpha1')), 'runSql', $rev);
110
111 // Consolidate activity contacts CRM-12274.
112 $this->addTask('Consolidate activity contacts', 'activityContacts');
113
114 return TRUE;
115 }
116
117 /**
118 * @param $rev
119 */
120 public function upgrade_4_4_beta1($rev) {
121 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.4.beta1')), 'runSql', $rev);
122
123 // add new 'data' column in civicrm_batch
124 $query = 'ALTER TABLE civicrm_batch ADD data LONGTEXT NULL COMMENT "cache entered data"';
125 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
126
127 // check if batch entry data exists in civicrm_cache table
128 $query = 'SELECT path, data FROM civicrm_cache WHERE group_name = "batch entry"';
129 $dao = CRM_Core_DAO::executeQuery($query);
130 while ($dao->fetch()) {
131 // get batch id $batchId[2]
132 $batchId = explode('-', $dao->path);
133 $data = unserialize($dao->data);
134
135 // move the data to civicrm_batch table
136 CRM_Core_DAO::setFieldValue('CRM_Batch_DAO_Batch', $batchId[2], 'data', json_encode(array('values' => $data)));
137 }
138
139 // delete entries from civicrm_cache table
140 $query = 'DELETE FROM civicrm_cache WHERE group_name = "batch entry"';
141 CRM_Core_DAO::executeQuery($query);
142
143 $this->addTask('Migrate custom word-replacements', 'wordReplacements');
144 }
145
146 /**
147 * @param $rev
148 */
149 public function upgrade_4_4_1($rev) {
150 $config = CRM_Core_Config::singleton();
151 // CRM-13327 upgrade handling for the newly added name badges
152 $ogID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'name_badge', 'id', 'name');
153 $nameBadges = array_flip(array_values(CRM_Core_BAO_OptionValue::getOptionValuesAssocArrayFromName('name_badge')));
154 unset($nameBadges['Avery 5395']);
155 if (!empty($nameBadges)) {
156 $dimension = '{"paper-size":"a4","orientation":"portrait","font-name":"times","font-size":6,"font-style":"","NX":2,"NY":4,"metric":"mm","lMargin":6,"tMargin":19,"SpaceX":0,"SpaceY":0,"width":100,"height":65,"lPadding":0,"tPadding":0}';
157 $query = "UPDATE civicrm_option_value
158 SET value = '{$dimension}'
159 WHERE option_group_id = %1 AND name = 'Fattorini Name Badge 100x65'";
160
161 CRM_Core_DAO::executeQuery($query, array(1 => array($ogID, 'Integer')));
162 }
163 else {
164 $dimensions = array(
165 1 => '{"paper-size":"a4","orientation":"landscape","font-name":"times","font-size":6,"font-style":"","NX":2,"NY":1,"metric":"mm","lMargin":25,"tMargin":27,"SpaceX":0,"SpaceY":35,"width":106,"height":150,"lPadding":5,"tPadding":5}',
166 2 => '{"paper-size":"a4","orientation":"portrait","font-name":"times","font-size":6,"font-style":"","NX":2,"NY":4,"metric":"mm","lMargin":6,"tMargin":19,"SpaceX":0,"SpaceY":0,"width":100,"height":65,"lPadding":0,"tPadding":0}',
167 3 => '{"paper-size":"a4","orientation":"portrait","font-name":"times","font-size":6,"font-style":"","NX":2,"NY":2,"metric":"mm","lMargin":10,"tMargin":28,"SpaceX":0,"SpaceY":0,"width":96,"height":121,"lPadding":5,"tPadding":5}',
168 );
169 $insertStatements = array(
170 1 => "($ogID, %1, '{$dimensions[1]}', %1, NULL, 0, NULL, 2, NULL, 0, 0, 1, NULL, NULL)",
171 2 => "($ogID, %2, '{$dimensions[2]}', %2, NULL, 0, NULL, 3, NULL, 0, 0, 1, NULL, NULL)",
172 3 => "($ogID, %3, '{$dimensions[3]}', %3, NULL, 0, NULL, 4, NULL, 0, 0, 1, NULL, NULL)",
173 );
174
175 $queryParams = array(
176 1 => array('A6 Badge Portrait 150x106', 'String'),
177 2 => array('Fattorini Name Badge 100x65', 'String'),
178 3 => array('Hanging Badge 3-3/4" x 4-3"/4', 'String'),
179 );
180
181 foreach ($insertStatements as $values) {
182 $query = 'INSERT INTO civicrm_option_value (`option_group_id`, `label`, `value`, `name`, `grouping`, `filter`, `is_default`, `weight`, `description`, `is_optgroup`, `is_reserved`, `is_active`, `component_id`, `visibility_id`) VALUES' . $values;
183 CRM_Core_DAO::executeQuery($query, $queryParams);
184 }
185 }
186
187 // CRM-12578 - Prior to this version a CSS file under drupal would disable core css
188 if (!empty($config->customCSSURL) && strpos($config->userFramework, 'Drupal') === 0) {
189 // The new setting doesn't exist yet - need to create it first
190 $sql = '
191 INSERT INTO civicrm_setting (group_name, name , value , domain_id , is_domain , created_date)
192 VALUES (%1, %2, %3, %4, %5, now())';
193 CRM_Core_DAO::executeQuery($sql, array(
194 1 => array(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'String'),
195 2 => array('disable_core_css', 'String'),
196 3 => array(serialize(1), 'String'),
197 4 => array(CRM_Core_Config::domainID(), 'Positive'),
198 5 => array(1, 'Int'),
199 ));
200 Civi::service('settings_manager')->flush();
201 }
202
203 // CRM-13701 - Fix $config->timeInputFormat
204 $sql = "
205 SELECT time_format
206 FROM civicrm_preferences_date
207 WHERE time_format IS NOT NULL
208 AND time_format <> ''
209 LIMIT 1
210 ";
211 $timeInputFormat = CRM_Core_DAO::singleValueQuery($sql);
212 if ($timeInputFormat && $timeInputFormat != $config->timeInputFormat) {
213 $params = array('timeInputFormat' => $timeInputFormat);
214 CRM_Core_BAO_ConfigSetting::add($params);
215 }
216
217 // CRM-13698 - add 'Available' and 'No-show' activity statuses
218 $insertStatus = array();
219 $nsinc = $avinc = $inc = 0;
220 if (!CRM_Core_OptionGroup::getValue('activity_status', 'Available', 'name')) {
221 $insertStatus[] = "(%1, 'Available', %2, 'Available', NULL, 0, NULL, %3, 0, 0, 1, NULL, NULL)";
222 $avinc = $inc = 1;
223 }
224 if (!CRM_Core_OptionGroup::getValue('activity_status', 'No_show', 'name')) {
225 $insertStatus[] = "(%1, 'No-show', %4, 'No_show', NULL, 0, NULL, %5, 0, 0, 1, NULL, NULL)";
226 $nsinc = $inc + 1;
227 }
228 if (!empty($insertStatus)) {
229 $acOptionGroupID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'activity_status', 'id', 'name');
230 $maxVal = CRM_Core_DAO::singleValueQuery("SELECT MAX(ROUND(op.value)) FROM civicrm_option_value op WHERE op.option_group_id = $acOptionGroupID");
231 $maxWeight = CRM_Core_DAO::singleValueQuery("SELECT MAX(weight) FROM civicrm_option_value WHERE option_group_id = $acOptionGroupID");
232
233 $p[1] = array($acOptionGroupID, 'Integer');
234 if ($avinc) {
235 $p[2] = array($avinc + $maxVal, 'Integer');
236 $p[3] = array($avinc + $maxWeight, 'Integer');
237 }
238 if ($nsinc) {
239 $p[4] = array($nsinc + $maxVal, 'Integer');
240 $p[5] = array($nsinc + $maxWeight, 'Integer');
241 }
242 $insertStatus = implode(',', $insertStatus);
243
244 $sql = "
245 INSERT INTO
246 civicrm_option_value (`option_group_id`, label, `value`, `name`, `grouping`, `filter`, `is_default`, `weight`, `is_optgroup`, `is_reserved`, `is_active`, `component_id`, `visibility_id`)
247 VALUES {$insertStatus}";
248 CRM_Core_DAO::executeQuery($sql, $p);
249 }
250
251 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.4.1')), 'runSql', $rev);
252 $this->addTask('Patch word-replacement schema', 'wordReplacements_patch', $rev);
253 }
254
255 /**
256 * @param $rev
257 *
258 * @return bool
259 */
260 public function upgrade_4_4_4($rev) {
261 $fkConstraint = array();
262 if (!CRM_Core_DAO::checkFKConstraintInFormat('civicrm_activity_contact', 'activity_id')) {
263 $fkConstraint[] = "ADD CONSTRAINT `FK_civicrm_activity_contact_activity_id` FOREIGN KEY (`activity_id`) REFERENCES `civicrm_activity` (`id`) ON DELETE CASCADE";
264 }
265 if (!CRM_Core_DAO::checkFKConstraintInFormat('civicrm_activity_contact', 'contact_id')) {
266 $fkConstraint[] = "ADD CONSTRAINT `FK_civicrm_activity_contact_contact_id` FOREIGN KEY (`contact_id`) REFERENCES `civicrm_contact` (`id`) ON DELETE CASCADE;
267 ";
268 }
269
270 if (!empty($fkConstraint)) {
271 $fkConstraint = implode(',', $fkConstraint);
272 $sql = "ALTER TABLE `civicrm_activity_contact`
273 {$fkConstraint}
274 ";
275 // CRM-14036 : delete entries of un-mapped contacts
276 CRM_Core_DAO::executeQuery("DELETE ac FROM civicrm_activity_contact ac
277 LEFT JOIN civicrm_contact c
278 ON c.id = ac.contact_id
279 WHERE c.id IS NULL;
280 ");
281 // delete entries of un-mapped activities
282 CRM_Core_DAO::executeQuery("DELETE ac FROM civicrm_activity_contact ac
283 LEFT JOIN civicrm_activity a
284 ON a.id = ac.activity_id
285 WHERE a.id IS NULL;
286 ");
287
288 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS=0;");
289 CRM_Core_DAO::executeQuery($sql);
290 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS=1;");
291 }
292
293 // task to process sql
294 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => '4.4.4')), 'runSql', $rev);
295
296 // CRM-13892 : add `name` column to dashboard schema
297 $query = "
298 ALTER TABLE civicrm_dashboard
299 ADD name varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Internal name of dashlet.' AFTER domain_id ";
300 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
301
302 $dashboard = new CRM_Core_DAO_Dashboard();
303 $dashboard->find();
304 while ($dashboard->fetch()) {
305 $urlElements = explode('/', $dashboard->url);
306 if ($urlElements[1] == 'dashlet') {
307 $url = explode('&', $urlElements[2]);
308 $name = $url[0];
309 }
310 elseif ($urlElements[1] == 'report') {
311 $url = explode('&', $urlElements[3]);
312 $name = 'report/' . $url[0];
313 }
314 $values .= "
315 WHEN {$dashboard->id} THEN '{$name}'
316 ";
317 }
318
319 $query = "
320 UPDATE civicrm_dashboard
321 SET name = CASE id
322 {$values}
323 END;
324 ";
325 CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
326
327 // CRM-13998 : missing alter statements for civicrm_report_instance
328 $this->addTask(ts('Confirm civicrm_report_instance sql table for upgrades'), 'updateReportInstanceTable');
329
330 return TRUE;
331 }
332
333 /**
334 * @param $rev
335 */
336 public function upgrade_4_4_6($rev) {
337 $sql = "SELECT count(*) AS count FROM INFORMATION_SCHEMA.STATISTICS where " .
338 "TABLE_SCHEMA = database() AND INDEX_NAME = 'index_image_url' AND TABLE_NAME = 'civicrm_contact';";
339 $dao = CRM_Core_DAO::executeQuery($sql);
340 $dao->fetch();
341 if ($dao->count < 1) {
342 $sql = "CREATE INDEX index_image_url ON civicrm_contact (image_url);";
343 $dao = CRM_Core_DAO::executeQuery($sql);
344 }
345 $minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM civicrm_contact WHERE image_URL IS NOT NULL');
346 $maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM civicrm_contact WHERE image_URL IS NOT NULL');
347 for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
348 $endId = $startId + self::BATCH_SIZE - 1;
349 $title = ts('Upgrade image_urls (%1 => %2)', array(1 => $startId, 2 => $endId));
350 $this->addTask($title, 'upgradeImageUrls', $startId, $endId);
351 }
352 }
353
354 /**
355 * @param $rev
356 * @param $originalVer
357 * @param $latestVer
358 *
359 * @return void
360 */
361 public function upgrade_4_4_7($rev, $originalVer, $latestVer) {
362 // For WordPress/Joomla(?), cleanup broken image_URL from 4.4.6 upgrades - https://issues.civicrm.org/jira/browse/CRM-14971
363 $exBackendUrl = CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=XXX', TRUE); // URL formula from 4.4.6 upgrade
364 $exFrontendUrl = CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=XXX', TRUE, NULL, TRUE, TRUE);
365 if ($originalVer == '4.4.6' && $exBackendUrl != $exFrontendUrl) {
366 $minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM civicrm_contact WHERE image_URL IS NOT NULL');
367 $maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM civicrm_contact WHERE image_URL IS NOT NULL');
368 for ($startId = $minId; $startId <= $maxId; $startId += self::BATCH_SIZE) {
369 $endId = $startId + self::BATCH_SIZE - 1;
370 $title = ts('Upgrade image_urls (%1 => %2)', array(1 => $startId, 2 => $endId));
371 $this->addTask($title, 'cleanupBackendImageUrls', $startId, $endId);
372 }
373 }
374 $this->addTask(ts('Update saved search information'), 'changeSavedSearch');
375 }
376
377 /**
378 * Upgrade image URLs.
379 *
380 * @param \CRM_Queue_TaskContext $ctx
381 * @param $startId
382 * @param $endId
383 *
384 * @return bool
385 */
386 public static function upgradeImageUrls(CRM_Queue_TaskContext $ctx, $startId, $endId) {
387 $dao = self::findContactImageUrls($startId, $endId);
388 $failures = array();
389 $config = CRM_Core_Config::singleton();
390 while ($dao->fetch()) {
391 $imageURL = $dao->image_url;
392 $baseurl = CIVICRM_UF_BASEURL;
393 //CRM-15897 - gross hack for joomla to remove the administrator/
394 if ($config->userFramework == 'Joomla') {
395 $baseurl = str_replace("/administrator/", "/", $baseurl);
396 }
397 $baselen = strlen($baseurl);
398 if (substr($imageURL, 0, $baselen) == $baseurl) {
399 $photo = basename($dao->image_url);
400 $fullpath = $config->customFileUploadDir . $photo;
401 if (file_exists($fullpath)) {
402 // For anyone who upgraded 4.4.6 release (eg 4.4.0=>4.4.6), the $newImageUrl incorrectly used backend URLs.
403 // For anyone who skipped 4.4.6 (eg 4.4.0=>4.4.7), the $newImageUrl correctly uses frontend URLs
404 self::setContactImageUrl($dao->id,
405 CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=' . $photo, TRUE, NULL, TRUE, TRUE));
406 }
407 else {
408 $failures[$dao->id] = $dao->image_url;
409 }
410 }
411 else {
412 $failures[$dao->id] = $dao->image_url;
413 }
414 }
415 CRM_Core_Error::debug_var('imageUrlsNotUpgraded', $failures);
416 return TRUE;
417 }
418
419 /**
420 * Change saved search.
421 *
422 * @param \CRM_Queue_TaskContext $ctx
423 *
424 * @return bool
425 */
426 public static function changeSavedSearch(CRM_Queue_TaskContext $ctx) {
427 $membershipStatuses = array_flip(CRM_Member_PseudoConstant::membershipStatus());
428
429 $dao = new CRM_Contact_DAO_SavedSearch();
430 $dao->find();
431 while ($dao->fetch()) {
432 $formValues = CRM_Contact_BAO_SavedSearch::getFormValues($dao->id);
433 if (!empty($formValues['mapper'])) {
434 foreach ($formValues['mapper'] as $key => $value) {
435 foreach ($value as $k => $v) {
436 if ($v[0] == 'Membership' && in_array($v[1], array('membership_status', 'membership_status_id'))) {
437 $value = $formValues['value'][$key][$k];
438 $op = $formValues['operator'][$key][$k];
439 if ($op == 'IN') {
440 $value = trim($value);
441 $value = str_replace('(', '', $value);
442 $value = str_replace(')', '', $value);
443
444 $v = explode(',', $value);
445 $value = array();
446 foreach ($v as $k1 => $v2) {
447 if (is_numeric($v2)) {
448 break 2;
449 }
450 $value[$k1] = $membershipStatuses[$v2];
451 }
452 $formValues['value'][$key][$k] = "(" . implode(',', $value) . ")";
453 }
454 elseif (in_array($op, array('=', '!='))) {
455 if (is_numeric($value)) {
456 break;
457 }
458 $formValues['value'][$key][$k] = $membershipStatuses[$value];
459 }
460 }
461 }
462 }
463 $dao->form_values = serialize($formValues);
464 $dao->save();
465 }
466 }
467
468 return TRUE;
469 }
470
471 /**
472 * For WordPress/Joomla(?) sites which upgraded to 4.4.6, find back-end image_URLs
473 * (e.g. "http://example.com/wp-admin/admin.php?page=CiviCRM&amp;q=civicrm/contact/imagefile&amp;photo=123.jpg")
474 * and convert them to front-end URLs
475 * (e.g. "http://example.com/?page=CiviCRM&amp;q=civicrm/contact/imagefile&amp;photo=123.jpg").
476 *
477 * @param CRM_Queue_TaskContext $ctx
478 * @param int $startId
479 * @param int $endId
480 * @return bool
481 */
482 public static function cleanupBackendImageUrls(CRM_Queue_TaskContext $ctx, $startId, $endId) {
483 $dao = self::findContactImageUrls($startId, $endId);
484 while ($dao->fetch()) {
485 $imageUrl = str_replace('&amp;', '&', $dao->image_url);
486 if (preg_match(":civicrm/contact/imagefile.*photo=:", $imageUrl)) {
487 // looks like one of ours
488 $imageUrlParts = parse_url($imageUrl);
489 parse_str($imageUrlParts['query'], $imageUrlQuery);
490 self::setContactImageUrl($dao->id,
491 CRM_Utils_System::url('civicrm/contact/imagefile', 'photo=' . $imageUrlQuery['photo'], TRUE, NULL, TRUE, TRUE));
492 }
493 }
494 return TRUE;
495 }
496
497 /**
498 * @param int $startId
499 * @param int $endId
500 * @return CRM_Core_DAO
501 * columns include "id" and "image_URL"
502 */
503 public static function findContactImageUrls($startId, $endId) {
504 $sql = "
505 SELECT id, image_url
506 FROM civicrm_contact
507 WHERE 1
508 AND id BETWEEN %1 AND %2
509 AND image_URL IS NOT NULL
510 ";
511
512 $params = array(
513 1 => array($startId, 'Integer'),
514 2 => array($endId, 'Integer'),
515 );
516 $dao = CRM_Core_DAO::executeQuery($sql, $params, TRUE, NULL, FALSE, FALSE);
517 return $dao;
518 }
519
520 /**
521 * @param int $cid
522 * @param string $newImageUrl
523 */
524 public static function setContactImageUrl($cid, $newImageUrl) {
525 $sql = 'UPDATE civicrm_contact SET image_url=%1 WHERE id=%2';
526 $params = array(
527 1 => array($newImageUrl, 'String'),
528 2 => array($cid, 'Integer'),
529 );
530 $updatedao = CRM_Core_DAO::executeQuery($sql, $params);
531 }
532
533 /**
534 * Update activity contacts CRM-12274
535 *
536 * @param CRM_Queue_TaskContext $ctx
537 *
538 * @return bool
539 * TRUE for success
540 */
541 public static function activityContacts(CRM_Queue_TaskContext $ctx) {
542 $upgrade = new CRM_Upgrade_Form();
543
544 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
545 $ovValue[] = $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
546 $ovValue[] = $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
547 $ovValue[] = $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
548
549 $optionGroupID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', 'activity_contacts', 'id', 'name');
550 if (!empty($ovValue)) {
551 $ovValues = implode(', ', $ovValue);
552 $query = "
553 UPDATE civicrm_option_value
554 SET is_reserved = 1
555 WHERE option_group_id = {$optionGroupID} AND value IN ($ovValues)";
556
557 $dao = CRM_Core_DAO::executeQuery($query);
558 }
559
560 if (!$assigneeID) {
561 $assigneeID = 1;
562 $value[] = "({$optionGroupID}, 'Activity Assignees', 1, 'Activity Assignees', 1, 1, 1)";
563 }
564 if (!$sourceID) {
565 $sourceID = 2;
566 $value[] = "({$optionGroupID}, 'Activity Source', 2, 'Activity Source', 2, 1, 1)";
567 }
568 if (!$targetID) {
569 $targetID = 3;
570 $value[] = "({$optionGroupID}, 'Activity Targets', 3, 'Activity Targets', 3, 1, 1)";
571 }
572
573 if (!$assigneeID || !$sourceID || !$targetID) {
574 $insert = "
575 INSERT INTO civicrm_option_value
576 (option_group_id, label, value, name, weight, is_reserved, is_active)
577 VALUES
578
579 ";
580 $values = implode(', ', $value);
581 $query = $insert . $values;
582 $dao = CRM_Core_DAO::executeQuery($query);
583 }
584
585 // sometimes an user does not make a clean backup and the above table
586 // already exists, so lets delete this table - CRM-13665
587 $query = "DROP TABLE IF EXISTS civicrm_activity_contact";
588 $dao = CRM_Core_DAO::executeQuery($query);
589
590 $query = "
591 CREATE TABLE IF NOT EXISTS civicrm_activity_contact (
592 id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Activity contact id',
593 activity_id int(10) unsigned NOT NULL COMMENT 'Foreign key to the activity for this record.',
594 contact_id int(10) unsigned NOT NULL COMMENT 'Foreign key to the contact for this record.',
595 record_type_id int(10) unsigned DEFAULT NULL COMMENT 'The record type id for this row',
596 PRIMARY KEY (id),
597 UNIQUE KEY UI_activity_contact (contact_id,activity_id,record_type_id),
598 KEY FK_civicrm_activity_contact_activity_id (activity_id)
599 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
600 ";
601
602 $dao = CRM_Core_DAO::executeQuery($query);
603
604 $query = "
605 INSERT INTO civicrm_activity_contact (activity_id, contact_id, record_type_id)
606 SELECT activity_id, target_contact_id, {$targetID} as record_type_id
607 FROM civicrm_activity_target";
608
609 $dao = CRM_Core_DAO::executeQuery($query);
610
611 $query = "
612 INSERT INTO civicrm_activity_contact (activity_id, contact_id, record_type_id)
613 SELECT activity_id, assignee_contact_id, {$assigneeID} as record_type_id
614 FROM civicrm_activity_assignment";
615 $dao = CRM_Core_DAO::executeQuery($query);
616
617 $query = "
618 INSERT INTO civicrm_activity_contact (activity_id, contact_id, record_type_id)
619 SELECT id, source_contact_id, {$sourceID} as record_type_id
620 FROM civicrm_activity
621 WHERE source_contact_id IS NOT NULL";
622
623 $dao = CRM_Core_DAO::executeQuery($query);
624
625 $query = "DROP TABLE civicrm_activity_target";
626 $dao = CRM_Core_DAO::executeQuery($query);
627
628 $query = "DROP TABLE civicrm_activity_assignment";
629 $dao = CRM_Core_DAO::executeQuery($query);
630
631 $query = "ALTER TABLE civicrm_activity
632 DROP FOREIGN KEY FK_civicrm_activity_source_contact_id";
633
634 $dao = CRM_Core_DAO::executeQuery($query);
635
636 $query = "ALTER TABLE civicrm_activity DROP COLUMN source_contact_id";
637 $dao = CRM_Core_DAO::executeQuery($query);
638
639 return TRUE;
640 }
641
642 /**
643 * Migrate word-replacements from $config to civicrm_word_replacement
644 *
645 * @param CRM_Queue_TaskContext $ctx
646 *
647 * @return bool
648 * TRUE for success
649 * @see http://issues.civicrm.org/jira/browse/CRM-13187
650 */
651 public static function wordReplacements(CRM_Queue_TaskContext $ctx) {
652 $query = "
653 CREATE TABLE IF NOT EXISTS `civicrm_word_replacement` (
654 `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'Word replacement ID',
655 `find_word` varchar(255) COLLATE utf8_bin COMMENT 'Word which need to be replaced',
656 `replace_word` varchar(255) COLLATE utf8_bin COMMENT 'Word which will replace the word in find',
657 `is_active` tinyint COMMENT 'Is this entry active?',
658 `match_type` enum('wildcardMatch', 'exactMatch') DEFAULT 'wildcardMatch',
659 `domain_id` int unsigned COMMENT 'FK to Domain ID. This is for Domain specific word replacement',
660 PRIMARY KEY ( `id` ),
661 UNIQUE INDEX `UI_domain_find` (domain_id, find_word),
662 CONSTRAINT FK_civicrm_word_replacement_domain_id FOREIGN KEY (`domain_id`) REFERENCES `civicrm_domain`(`id`)
663 ) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ;
664 ";
665 $dao = CRM_Core_DAO::executeQuery($query);
666
667 self::rebuildWordReplacementTable();
668 return TRUE;
669 }
670
671 /**
672 * Fix misconfigured constraints created in 4.4.0. To distinguish the good
673 * and bad configurations, we change the constraint name from "UI_find"
674 * (the original name in 4.4.0) to "UI_domain_find" (the new name in
675 * 4.4.1).
676 *
677 * @param CRM_Queue_TaskContext $ctx
678 * @param $rev
679 *
680 * @return bool
681 * TRUE for success
682 * @see http://issues.civicrm.org/jira/browse/CRM-13655
683 */
684 public static function wordReplacements_patch(CRM_Queue_TaskContext $ctx, $rev) {
685 if (CRM_Core_DAO::checkConstraintExists('civicrm_word_replacement', 'UI_find')) {
686 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement DROP FOREIGN KEY FK_civicrm_word_replacement_domain_id;");
687 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement DROP KEY FK_civicrm_word_replacement_domain_id;");
688 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement DROP KEY UI_find;");
689 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement MODIFY COLUMN `find_word` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT 'Word which need to be replaced';");
690 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement MODIFY COLUMN `replace_word` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT 'Word which will replace the word in find';");
691 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement ADD CONSTRAINT UI_domain_find UNIQUE KEY `UI_domain_find` (`domain_id`,`find_word`);");
692 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_word_replacement ADD CONSTRAINT FK_civicrm_word_replacement_domain_id FOREIGN KEY (`domain_id`) REFERENCES `civicrm_domain` (`id`);");
693 }
694 return TRUE;
695 }
696
697 /**
698 * Syntatic sugar for adding a task which (a) is in this class and (b) has
699 * a high priority.
700 *
701 * After passing the $funcName, you can also pass parameters that will go to
702 * the function. Note that all params must be serializable.
703 */
704 protected function addTask($title, $funcName) {
705 $queue = CRM_Queue_Service::singleton()->load(array(
706 'type' => 'Sql',
707 'name' => CRM_Upgrade_Form::QUEUE_NAME,
708 ));
709
710 $args = func_get_args();
711 $title = array_shift($args);
712 $funcName = array_shift($args);
713 $task = new CRM_Queue_Task(
714 array(get_class($this), $funcName),
715 $args,
716 $title
717 );
718 $queue->createItem($task, array('weight' => -1));
719 }
720
721 /**
722 * Get all the word-replacements stored in config-arrays
723 * and convert them to params for the WordReplacement.create API.
724 *
725 * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and
726 * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade
727 * step behaves consistently even as the BAO evolves in future versions.
728 * However, if there's a bug in here prior to 4.4.0, we should apply the
729 * bugfix in both places.
730 *
731 * @param bool $rebuildEach
732 * Whether to perform rebuild after each individual API call.
733 * @return array
734 * Each item is $params for WordReplacement.create
735 * @see CRM_Core_BAO_WordReplacement::convertConfigArraysToAPIParams
736 */
737 public static function getConfigArraysAsAPIParams($rebuildEach) {
738 $wordReplacementCreateParams = array();
739 // get all domains
740 $result = civicrm_api3('domain', 'get', array(
741 'return' => array('locale_custom_strings'),
742 ));
743 if (!empty($result["values"])) {
744 foreach ($result["values"] as $value) {
745 $params = array();
746 $params["domain_id"] = $value["id"];
747 $params["options"] = array('wp-rebuild' => $rebuildEach);
748 // unserialize word match string
749 $localeCustomArray = array();
750 if (!empty($value["locale_custom_strings"])) {
751 $localeCustomArray = unserialize($value["locale_custom_strings"]);
752 }
753 if (!empty($localeCustomArray)) {
754 $wordMatchArray = array();
755 // Traverse Language array
756 foreach ($localeCustomArray as $localCustomData) {
757 // Traverse status array "enabled" "disabled"
758 foreach ($localCustomData as $status => $matchTypes) {
759 $params["is_active"] = ($status == "enabled") ? TRUE : FALSE;
760 // Traverse Match Type array "wildcardMatch" "exactMatch"
761 foreach ($matchTypes as $matchType => $words) {
762 $params["match_type"] = $matchType;
763 foreach ($words as $word => $replace) {
764 $params["find_word"] = $word;
765 $params["replace_word"] = $replace;
766 $wordReplacementCreateParams[] = $params;
767 }
768 }
769 }
770 }
771 }
772 }
773 }
774 return $wordReplacementCreateParams;
775 }
776
777 /**
778 * Get all the word-replacements stored in config-arrays
779 * and write them out as records in civicrm_word_replacement.
780 *
781 * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and
782 * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade
783 * step behaves consistently even as the BAO evolves in future versions.
784 * However, if there's a bug in here prior to 4.4.0, we should apply the
785 * bugfix in both places.
786 */
787 public static function rebuildWordReplacementTable() {
788 civicrm_api3('word_replacement', 'replace', array(
789 'options' => array('match' => array('domain_id', 'find_word')),
790 'values' => array_filter(self::getConfigArraysAsAPIParams(FALSE), array(__CLASS__, 'isValidWordReplacement')),
791 ));
792 CRM_Core_BAO_WordReplacement::rebuild();
793 }
794
795
796 /**
797 * CRM-13998 missing alter statements for civicrm_report_instance
798 */
799 public function updateReportInstanceTable() {
800
801 // add civicrm_report_instance.name
802
803 $sql = "SELECT count(*) FROM information_schema.columns "
804 . "WHERE table_schema = database() AND table_name = 'civicrm_report_instance' AND COLUMN_NAME = 'name' ";
805
806 $res = CRM_Core_DAO::singleValueQuery($sql);
807
808 if ($res <= 0) {
809 $sql = "ALTER TABLE civicrm_report_instance ADD `name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'when combined with report_id/template uniquely identifies the instance'";
810 $res = CRM_Core_DAO::executeQuery($sql);
811 }
812
813 // add civicrm_report_instance args
814
815 $sql = "SELECT count(*) FROM information_schema.columns WHERE table_schema = database() AND table_name = 'civicrm_report_instance' AND COLUMN_NAME = 'args' ";
816
817 $res = CRM_Core_DAO::singleValueQuery($sql);
818
819 if ($res <= 0) {
820 $sql = "ALTER TABLE civicrm_report_instance ADD `args` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'arguments that are passed in the url when invoking the instance'";
821
822 $res = CRM_Core_DAO::executeQuery($sql);
823 }
824
825 return TRUE;
826 }
827
828 /**
829 * @param array $params
830 * @return bool
831 * TRUE if $params is valid
832 */
833 public static function isValidWordReplacement($params) {
834 $result = strlen($params['find_word']) <= self::MAX_WORD_REPLACEMENT_SIZE && strlen($params['replace_word']) <= self::MAX_WORD_REPLACEMENT_SIZE;
835 if (!$result) {
836 CRM_Core_Error::debug_var('invalidWordReplacement', $params);
837 }
838 return $result;
839 }
840
841 }