Merge pull request #9528 from colemanw/CRM-19723
[civicrm-core.git] / CRM / Upgrade / Incremental / php / FourSeven.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2016 |
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 * Upgrade logic for 4.7
29 */
30 class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_Base {
31
32 /**
33 * Compute any messages which should be displayed beforeupgrade.
34 *
35 * Note: This function is called iteratively for each upcoming
36 * revision to the database.
37 *
38 * @param string $preUpgradeMessage
39 * @param string $rev
40 * a version number, e.g. '4.4.alpha1', '4.4.beta3', '4.4.0'.
41 * @param null $currentVer
42 */
43 public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) {
44 if ($rev == '4.7.alpha1') {
45 // CRM-16478 Remove custom fatal error template path option
46 $config = CRM_Core_Config::singleton();
47 if (!empty($config->fatalErrorTemplate) && $config->fatalErrorTemplate != 'CRM/common/fatal.tpl') {
48 $preUpgradeMessage .= '<p>' . ts('The custom fatal error template setting will be removed during the upgrade. You are currently using this custom template: %1 . Following the upgrade you will need to use the standard approach to overriding template files, as described in the documentation.', array(1 => $config->fatalErrorTemplate)) . '</p>';
49 }
50 }
51 if ($rev == '4.7.alpha4') {
52 // CRM-17004 Warn of Moneris removal
53 $count = 1;
54 // Query only works in 4.3+
55 if (version_compare($currentVer, "4.3.0") > 0) {
56 $count = CRM_Core_DAO::singleValueQuery("SELECT COUNT(id) FROM civicrm_payment_processor WHERE payment_processor_type_id IN (SELECT id FROM civicrm_payment_processor_type WHERE name = 'Moneris')");
57 }
58 if ($count && !function_exists('moneris_civicrm_managed')) {
59 $preUpgradeMessage .= '<p>' . ts('The %1 payment processor is no longer bundled with CiviCRM. After upgrading you will need to install the extension to continue using it.', array(1 => 'Moneris')) . '</p>';
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 */
72 public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) {
73 if ($rev == '4.7.alpha1') {
74 $config = CRM_Core_Config::singleton();
75 // FIXME: Performing an upgrade step during postUpgrade message phase is probably bad
76 $editor_id = self::updateWysiwyg();
77 $msg = NULL;
78 $ext_href = 'href="' . CRM_Utils_System::url('civicrm/admin/extensions', 'reset=1') . '"';
79 $dsp_href = 'href="' . CRM_Utils_System::url('civicrm/admin/setting/preferences/display', 'reset=1') . '"';
80 $blog_href = 'href="https://civicrm.org/blogs/colemanw/big-changes-wysiwyg-editing-47"';
81 switch ($editor_id) {
82 // TinyMCE
83 case 1:
84 $msg = ts('Your configured editor "TinyMCE" is no longer part of the main CiviCRM download. To continue using it, visit the <a %1>Manage Extensions</a> page to download and install the TinyMCE extension.', array(1 => $ext_href));
85 break;
86
87 // Drupal/Joomla editor
88 case 3:
89 case 4:
90 $msg = ts('CiviCRM no longer integrates with the "%1 Default Editor." Your wysiwyg setting has been reset to the built-in CKEditor. <a %2>Learn more...</a>', array(1 => $config->userFramework, 2 => $blog_href));
91 break;
92 }
93 if ($msg) {
94 $postUpgradeMessage .= '<p>' . $msg . '</p>';
95 }
96 $postUpgradeMessage .= '<p>' . ts('CiviCRM now includes the easy-to-use CKEditor Configurator. To customize the features and display of your wysiwyg editor, visit the <a %1>Display Preferences</a> page. <a %2>Learn more...</a>', array(1 => $dsp_href, 2 => $blog_href)) . '</p>';
97
98 $postUpgradeMessage .= '<br /><br />' . ts('Default version of the following System Workflow Message Templates have been modified: <ul><li>Personal Campaign Pages - Owner Notification</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).');
99
100 $postUpgradeMessage .= '<p>' . ts('The custom fatal error template setting has been removed.') . '</p>';
101 }
102 //if ($rev == '4.7.11') {
103 // $postUpgradeMessage .= '<br /><br />' . ts("WARNING: For increased security, profile submissions embedded in remote sites are no longer allowed to create or edit data by default. If you need to allow users to submit profiles from external sites, you can restore this at Administer > System Settings > Misc (Undelete, PDFs, Limits, Logging, Captcha, etc.) > 'Accept profile submissions from external sites'");
104 //}
105 if ($rev == '4.7.11') {
106 $postUpgradeMessage .= '<br /><br />' . ts("By default, CiviCRM now disables the ability to import directly from SQL. To use this feature, you must explicitly grant permission 'import SQL datasource'.");
107 }
108 if ($rev == '4.7.14') {
109 $ck_href = 'href="' . CRM_Utils_System::url('civicrm/admin/ckeditor') . '"';
110 $postUpgradeMessage .= '<p>' . ts('CiviMail no longer forces CKEditor to add html/head/body tags to email content because some sites place these in the message header/footer. This was added in 4.7.5 and is now disabled by default.')
111 . '<br />' . ts('You can re-enable it by visitng the <a %1>CKEditor Config</a> screen and setting "fullPage = true" under the Advanced Options of the CiviMail preset.', array(1 => $ck_href))
112 . '</p>';
113 }
114 }
115
116 /**
117 * Upgrade function.
118 *
119 * @param string $rev
120 */
121 public function upgrade_4_7_alpha1($rev) {
122 $this->addTask('Migrate \'on behalf of\' information to module_data', 'migrateOnBehalfOfInfo');
123 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
124 $this->addTask(ts('Migrate Settings to %1', array(1 => $rev)), 'migrateSettings', $rev);
125 $this->addTask('Add Getting Started dashlet', 'addGettingStartedDashlet', $rev);
126 }
127
128 /**
129 * Upgrade function.
130 *
131 * @param string $rev
132 */
133 public function upgrade_4_7_alpha4($rev) {
134 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
135 $this->addTask(ts('Remove %1', array(1 => 'Moneris')), 'removePaymentProcessorType', 'Moneris');
136 $this->addTask('Update Smart Groups', 'fixContactTypeInSmartGroups');
137 }
138
139 /**
140 * Upgrade function.
141 *
142 * @param string $rev
143 */
144 public function upgrade_4_7_beta2($rev) {
145 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
146 $this->addTask('Delete unused file', 'deleteVersionCheckCacheFile');
147 }
148
149 /**
150 * Upgrade function.
151 *
152 * @param string $rev
153 */
154 public function upgrade_4_7_beta6($rev) {
155 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
156 $this->addTask('Disable flexible jobs extension', 'disableFlexibleJobsExtension');
157 $this->addTask('Add Index to financial_trxn trxn_id field', 'addIndexFinancialTrxnTrxnID');
158 }
159
160 /**
161 * Upgrade function.
162 *
163 * @param string $rev
164 */
165 public function upgrade_4_7_1($rev) {
166 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
167 $this->addTask('Add Index to civicrm_contribution creditnote_id field', 'addIndexContributionCreditNoteID');
168 }
169
170 /**
171 * Upgrade function.
172 *
173 * @param string $rev
174 */
175 public function upgrade_4_7_2($rev) {
176 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
177 $this->addTask('Fix Index on civicrm_financial_item combined entity_id + entity_table', 'addCombinedIndexFinancialItemEntityIDEntityType');
178 $this->addTask('enable financial account relationships for chargeback & refund', 'addRefundAndChargeBackAccountsIfNotExist');
179 $this->addTask('Add Index to civicrm_contribution.source', 'addIndexContributionSource');
180 }
181
182 /**
183 * Upgrade function.
184 *
185 * @param string $rev
186 */
187 public function upgrade_4_7_3($rev) {
188 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
189 $this->addTask('Add Index to civicrm_contribution.total_amount', 'addIndexContributionAmount');
190 }
191
192 /**
193 * Upgrade function.
194 *
195 * @param string $rev
196 */
197 public function upgrade_4_7_4($rev) {
198 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
199 $this->addTask('Add Contact Deleted by Merge Activity Type', 'addDeletedByMergeActivityType');
200 }
201
202 /**
203 * Upgrade function.
204 *
205 * @param string $rev
206 */
207 public function upgrade_4_7_7($rev) {
208 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
209 // https://issues.civicrm.org/jira/browse/CRM-18006
210 if (CRM_Core_DAO::checkTableExists('civicrm_install_canary')) {
211 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_install_canary ENGINE=InnoDB');
212 }
213 }
214
215 /**
216 * Upgrade function.
217 *
218 * @param string $rev
219 */
220 public function upgrade_4_7_8($rev) {
221 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
222 $this->addTask('Upgrade mailing foreign key constraints', 'upgradeMailingFKs');
223 }
224
225 /**
226 * Upgrade function.
227 *
228 * @param string $rev
229 */
230 public function upgrade_4_7_10($rev) {
231 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
232 $this->addTask('Upgrade Add Help Pre and Post Fields to price value table', 'addHelpPreAndHelpPostFieldsPriceFieldValue');
233 $this->addTask('Alter index and type for image URL', 'alterIndexAndTypeForImageURL');
234 }
235
236 /**
237 * Upgrade function.
238 *
239 * @param string $rev
240 */
241 public function upgrade_4_7_11($rev) {
242 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
243 $this->addTask('Dashboard schema updates', 'dashboardSchemaUpdate');
244 $this->addTask('Fill in setting "remote_profile_submissions"', 'migrateRemoteSubmissionsSetting');
245 }
246
247 /**
248 * Upgrade function.
249 *
250 * @param string $rev
251 */
252 public function upgrade_4_7_12($rev) {
253 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
254 $this->addTask('Add Data Type column to civicrm_option_group', 'addDataTypeColumnToOptionGroupTable');
255 }
256 /**
257 * Upgrade function.
258 *
259 * @param string $rev
260 */
261 public function upgrade_4_7_13($rev) {
262 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
263 $this->addTask('Add column to allow for payment processors to set what card types are accepted', 'addAcceptedCardTypesField');
264 }
265
266 /**
267 * Upgrade function.
268 *
269 * @param string $rev
270 */
271 public function upgrade_4_7_14($rev) {
272 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
273 $this->addTask('Add WYSIWYG Editor Presets', 'addWysiwygPresets');
274 }
275
276 /**
277 * Upgrade function.
278 *
279 * @param string $rev
280 */
281 public function upgrade_4_7_15($rev) {
282 $this->addTask('Add icon column to civicrm_option_value', 'addColumn',
283 'civicrm_option_value', 'icon', "varchar(255) COMMENT 'crm-i icon class' DEFAULT NULL");
284 $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
285 }
286
287 /*
288 * Important! All upgrade functions MUST add a 'runSql' task.
289 * Uncomment and use the following template for a new upgrade version
290 * (change the x in the function name):
291 */
292
293 // /**
294 // * Upgrade function.
295 // *
296 // * @param string $rev
297 // */
298 // public function upgrade_4_7_x($rev) {
299 // $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
300 // // Additional tasks here...
301 // // Note: do not use ts() in the addTask description because it adds unnecessary strings to transifex.
302 // // The above is an exception because 'Upgrade DB to %1: SQL' is generic & reusable.
303 // }
304
305 /**
306 * CRM-16354
307 *
308 * @return int
309 */
310 public static function updateWysiwyg() {
311 $editorID = Civi::settings()->get('editor_id');
312 // Previously a numeric value indicated one of 4 wysiwyg editors shipped in core, and no value indicated 'Textarea'
313 // Now the options are "Textarea", "CKEditor", and the rest have been dropped from core.
314 $newEditor = $editorID ? "CKEditor" : "Textarea";
315 Civi::settings()->set('editor_id', $newEditor);
316
317 return $editorID;
318 }
319
320 /**
321 * Migrate any last remaining options from `civicrm_domain.config_backend` to `civicrm_setting`.
322 * Cleanup setting schema.
323 *
324 * @param CRM_Queue_TaskContext $ctx
325 * @return bool
326 */
327 public static function migrateSettings(CRM_Queue_TaskContext $ctx) {
328 // Tip: If there are problems with adding the new uniqueness index, try inspecting:
329 // SELECT name, domain_id, contact_id, count(*) AS dupes FROM civicrm_setting cs GROUP BY name, domain_id, contact_id HAVING dupes > 1;
330
331 // Nav records are expendable. https://forum.civicrm.org/index.php?topic=36933.0
332 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_setting WHERE contact_id IS NOT NULL AND name = "navigation"');
333
334 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting DROP INDEX index_group_name');
335 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting DROP COLUMN group_name');
336
337 // Handle Strange activity_tab_filter settings.
338 CRM_Core_DAO::executeQuery('CREATE TABLE civicrm_activity_setting LIKE civicrm_setting');
339 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_activity_setting ADD UNIQUE INDEX index_domain_contact_name (domain_id, contact_id, name)');
340 CRM_Core_DAO::executeQuery('INSERT INTO civicrm_activity_setting (name, contact_id, domain_id, value)
341 SELECT DISTINCT name, contact_id, domain_id, value
342 FROM civicrm_setting
343 WHERE name = "activity_tab_filter"
344 AND value is not NULL');
345 CRM_Core_DAO::executeQuery('DELETE FROM civicrm_setting WHERE name = "activity_tab_filter"');
346
347 $date = CRM_Utils_Time::getTime('Y-m-d H:i:s');
348 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_setting ADD UNIQUE INDEX index_domain_contact_name (domain_id, contact_id, name)');
349 CRM_Core_DAO::executeQuery("INSERT INTO civicrm_setting (name, contact_id, domain_id, value, is_domain, created_id, created_date)
350 SELECT name, contact_id, domain_id, value, 0, contact_id,'$date'
351 FROM civicrm_activity_setting
352 WHERE name = 'activity_tab_filter'
353 AND value is not NULL"
354 );
355 CRM_Core_DAO::executeQuery('DROP TABLE civicrm_activity_setting');
356
357 $domainDao = CRM_Core_DAO::executeQuery('SELECT id, config_backend FROM civicrm_domain');
358 while ($domainDao->fetch()) {
359 $settings = CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($domainDao->id, $domainDao->config_backend);
360 CRM_Core_Error::debug_var('convertBackendToSettings', array(
361 'domainId' => $domainDao->id,
362 'backend' => $domainDao->config_backend,
363 'settings' => $settings,
364 ));
365
366 foreach ($settings as $name => $value) {
367 $rowParams = array(
368 1 => array($domainDao->id, 'Positive'),
369 2 => array($name, 'String'),
370 3 => array(serialize($value), 'String'),
371 );
372 $settingId = CRM_Core_DAO::singleValueQuery(
373 'SELECT id FROM civicrm_setting WHERE domain_id = %1 AND name = %2',
374 $rowParams);
375 if (!$settingId) {
376 CRM_Core_DAO::executeQuery(
377 'INSERT INTO civicrm_setting (domain_id, name, value, is_domain) VALUES (%1,%2,%3,1)',
378 $rowParams);
379 }
380 }
381 }
382
383 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_domain DROP COLUMN config_backend');
384
385 return TRUE;
386 }
387
388 /**
389 * Take a config_backend blob and produce an equivalent list of settings.
390 *
391 * @param int $domainId
392 * Domain ID.
393 * @param string $config_backend
394 * Serialized blob.
395 * @return array
396 */
397 public static function convertBackendToSettings($domainId, $config_backend) {
398 if (!$config_backend) {
399 return array();
400 }
401
402 $backend = unserialize($config_backend);
403 if (!$backend) {
404 return array();
405 }
406
407 $mappings = \CRM_Core_Config_MagicMerge::getPropertyMap();
408 $settings = array();
409 foreach ($backend as $propertyName => $propertyValue) {
410 if (isset($mappings[$propertyName][0]) && preg_match('/^setting/', $mappings[$propertyName][0])) {
411 // $mapping format: $propertyName => Array(0 => $type, 1 => $setting|NULL).
412 $settingName = isset($mappings[$propertyName][1]) ? $mappings[$propertyName][1] : $propertyName;
413 $settings[$settingName] = $propertyValue;
414 }
415 }
416
417 return $settings;
418 }
419
420 /**
421 * Add Getting Started dashlet to dashboard
422 *
423 * @param \CRM_Queue_TaskContext $ctx
424 *
425 * @return bool
426 */
427 public static function addGettingStartedDashlet(CRM_Queue_TaskContext $ctx) {
428 $sql = "SELECT count(*) FROM civicrm_dashboard WHERE name='getting-started'";
429 $res = CRM_Core_DAO::singleValueQuery($sql);
430 $domainId = CRM_Core_Config::domainID();
431 if ($res <= 0) {
432 $sql = "INSERT INTO `civicrm_dashboard`
433 ( `domain_id`, `name`, `label`, `url`, `permission`, `permission_operator`, `column_no`, `is_minimized`, `is_active`, `weight`, `fullscreen_url`, `is_fullscreen`, `is_reserved`) VALUES ( {$domainId}, 'getting-started', 'Getting Started', 'civicrm/dashlet/getting-started?reset=1&snippet=5', 'access CiviCRM', NULL, 0, 0, 1, 0, 'civicrm/dashlet/getting-started?reset=1&snippet=5&context=dashletFullscreen', 1, 1)";
434 CRM_Core_DAO::executeQuery($sql);
435 // Add default position for Getting Started Dashlet ( left column)
436 $sql = "INSERT INTO `civicrm_dashboard_contact` (dashboard_id, contact_id, column_no, is_active)
437 SELECT (SELECT MAX(id) FROM `civicrm_dashboard`), contact_id, 0, IF (SUM(is_active) > 0, 1, 0)
438 FROM `civicrm_dashboard_contact` JOIN `civicrm_contact` WHERE civicrm_dashboard_contact.contact_id = civicrm_contact.id GROUP BY contact_id";
439 CRM_Core_DAO::executeQuery($sql);
440 }
441 return TRUE;
442 }
443
444 /**
445 * Migrate on-behalf information to uf_join.module_data as on-behalf columns will be dropped
446 * on DB upgrade
447 *
448 * @param CRM_Queue_TaskContext $ctx
449 *
450 * @return bool
451 * TRUE for success
452 */
453 public static function migrateOnBehalfOfInfo(CRM_Queue_TaskContext $ctx) {
454 $domain = new CRM_Core_DAO_Domain();
455 $domain->find(TRUE);
456
457 // fetch onBehalf entry in UFJoin table
458 $ufGroupDAO = new CRM_Core_DAO_UFJoin();
459 $ufGroupDAO->module = 'OnBehalf';
460 $ufGroupDAO->find(TRUE);
461
462 $forOrgColums = array('is_for_organization');
463 if ($domain->locales) {
464 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
465 foreach ($locales as $locale) {
466 $forOrgColums[] = "for_organization_{$locale}";
467 }
468 }
469 else {
470 $forOrgColums[] = "for_organization";
471 }
472
473 $query = "
474 SELECT " . implode(", ", $forOrgColums) . ", uj.id as join_id, uj.uf_group_id as uf_group_id
475 FROM civicrm_contribution_page cp
476 INNER JOIN civicrm_uf_join uj ON uj.entity_id = cp.id AND uj.module = 'OnBehalf'";
477 $dao = CRM_Core_DAO::executeQuery($query, array(), TRUE, NULL, FALSE, FALSE);
478
479 if ($dao->N) {
480 while ($dao->fetch()) {
481 $onBehalfParams['on_behalf'] = array('is_for_organization' => $dao->is_for_organization);
482 if ($domain->locales) {
483 foreach ($locales as $locale) {
484 $for_organization = "for_organization_{$locale}";
485 $onBehalfParams['on_behalf'] += array(
486 $locale => array(
487 'for_organization' => $dao->$for_organization,
488 ),
489 );
490 }
491 }
492 else {
493 $onBehalfParams['on_behalf'] += array(
494 'default' => array(
495 'for_organization' => $dao->for_organization,
496 ),
497 );
498 }
499 $ufJoinParam = array(
500 'id' => $dao->join_id,
501 'module' => 'on_behalf',
502 'uf_group_id' => $dao->uf_group_id,
503 'module_data' => json_encode($onBehalfParams),
504 );
505 CRM_Core_BAO_UFJoin::create($ufJoinParam);
506 }
507 }
508
509 return TRUE;
510 }
511
512 /**
513 * v4.7.11 adds a new setting "remote_profile_submissions". This is
514 * long-standing feature that existing sites may be using; however, it's
515 * a bit prone to abuse. For new sites, the default is to disable it
516 * (since that is more secure). For existing sites, the default is to
517 * enable it (since that is more compatible).
518 *
519 * @param \CRM_Queue_TaskContext $ctx
520 *
521 * @return bool
522 */
523 public static function migrateRemoteSubmissionsSetting(CRM_Queue_TaskContext $ctx) {
524 $domains = CRM_Core_DAO::executeQuery("SELECT DISTINCT d.id FROM civicrm_domain d LEFT JOIN civicrm_setting s ON d.id=s.domain_id AND s.name = 'remote_profile_submissions' WHERE s.id IS NULL");
525 while ($domains->fetch()) {
526 CRM_Core_DAO::executeQuery(
527 "INSERT INTO civicrm_setting (`name`, `value`, `domain_id`, `is_domain`, `contact_id`, `component_id`, `created_date`, `created_id`)
528 VALUES (%2, %3, %4, %5, NULL, NULL, %6, NULL)",
529 array(
530 2 => array('remote_profile_submissions', 'String'),
531 3 => array('s:1:"1";', 'String'),
532 4 => array($domains->id, 'Integer'),
533 5 => array(1, 'Integer'),
534 6 => array(date('Y-m-d H:i:s'), 'String'),
535 )
536 );
537 }
538 return TRUE;
539 }
540
541 /**
542 * CRM-11782 - Get rid of VALUE_SEPARATOR character in saved search form values
543 *
544 * @param \CRM_Queue_TaskContext $ctx
545 *
546 * @return bool
547 */
548 public static function fixContactTypeInSmartGroups(CRM_Queue_TaskContext $ctx) {
549 $sep = CRM_Core_DAO::VALUE_SEPARATOR;
550 $dao = CRM_Core_DAO::executeQuery("SELECT id, form_values FROM civicrm_saved_search WHERE form_values LIKE '%$sep%'");
551 while ($dao->fetch()) {
552 $formValues = unserialize($dao->form_values);
553 if (isset($formValues['contact_type']) && is_array($formValues['contact_type'])) {
554 $newVals = array();
555 foreach ($formValues['contact_type'] as $key => $val) {
556 $newVals[str_replace($sep, '__', $key)] = is_string($val) ? str_replace($sep, '__', $val) : $val;
557 }
558 $formValues['contact_type'] = $newVals;
559 }
560 CRM_Core_DAO::executeQuery("UPDATE civicrm_saved_search SET form_values = %1 WHERE id = {$dao->id}", array(1 => array(serialize($formValues), 'String')));
561 }
562
563 return TRUE;
564 }
565
566 /**
567 * CRM-17637 - Ths file location has been moved; delete the old one
568 *
569 * @param \CRM_Queue_TaskContext $ctx
570 *
571 * @return bool
572 */
573 public static function deleteVersionCheckCacheFile(CRM_Queue_TaskContext $ctx) {
574 $config = CRM_Core_Config::singleton();
575 $cacheFile = $config->uploadDir . 'version-info-cache.json';
576 if (file_exists($cacheFile)) {
577 unlink($cacheFile);
578 }
579 return TRUE;
580 }
581
582 /**
583 * CRM-17669 and CRM-17686, make scheduled jobs more flexible, disable the 4.6 extension if installed
584 *
585 * @param \CRM_Queue_TaskContext $ctx
586 *
587 * @return bool
588 */
589 public static function disableFlexibleJobsExtension(CRM_Queue_TaskContext $ctx) {
590 try {
591 civicrm_api3('Extension', 'disable', array('key' => 'com.klangsoft.flexiblejobs'));
592 }
593 catch (CiviCRM_API3_Exception $e) {
594 // just ignore if the extension isn't installed
595 }
596
597 return TRUE;
598 }
599
600 /**
601 * CRM-17752 add index to civicrm_financial_trxn.trxn_id (deliberately non-unique).
602 *
603 * @param \CRM_Queue_TaskContext $ctx
604 *
605 * @return bool
606 */
607 public static function addIndexFinancialTrxnTrxnID(CRM_Queue_TaskContext $ctx) {
608 $tables = array('civicrm_financial_trxn' => array('trxn_id'));
609 CRM_Core_BAO_SchemaHandler::createIndexes($tables);
610 return TRUE;
611 }
612
613 /**
614 * CRM-17882 Add index to civicrm_contribution.credit_note_id.
615 *
616 * @param \CRM_Queue_TaskContext $ctx
617 *
618 * @return bool
619 */
620 public static function addIndexContributionCreditNoteID(CRM_Queue_TaskContext $ctx) {
621 $tables = array('civicrm_contribution' => array('creditnote_id'));
622 CRM_Core_BAO_SchemaHandler::createIndexes($tables);
623 return TRUE;
624 }
625
626 /**
627 * CRM-17775 Add correct index for table civicrm_financial_item.
628 *
629 * Note that the entity ID should always precede the entity_table as
630 * it is more unique. This is better for performance and does not cause fallback
631 * to no index if table it omitted.
632 *
633 * @return bool
634 */
635 public static function addCombinedIndexFinancialItemEntityIDEntityType() {
636 CRM_Core_BAO_SchemaHandler::dropIndexIfExists('civicrm_financial_item', 'UI_id');
637 CRM_Core_BAO_SchemaHandler::dropIndexIfExists('civicrm_financial_item', 'IX_Entity');
638 CRM_Core_BAO_SchemaHandler::createIndexes(array(
639 'civicrm_financial_item' => array(array('entity_id', 'entity_table')),
640 ));
641 return TRUE;
642 }
643
644 /**
645 * CRM-17951 Add accounts option values for refund and chargeback.
646 *
647 * Add Chargeback contribution status and Chargeback and Contra account relationships,
648 * checking first if one exists.
649 */
650 public static function addRefundAndChargeBackAccountsIfNotExist() {
651 // First we enable and edit the record for Credit contra - this exists but is disabled for most sites.
652 // Using the ensure function (below) will not enabled a disabled option (by design).
653 CRM_Core_DAO::executeQuery("UPDATE civicrm_option_value v
654 INNER JOIN civicrm_option_group g on v.option_group_id=g.id and g.name='account_relationship'
655 SET v.is_active=1, v.label='Credit/Contra Revenue Account is', v.name='Credit/Contra Revenue Account is', v.description='Credit/Contra Revenue Account is'
656 WHERE v.name = 'Credit/Contra Account is';");
657
658 CRM_Core_BAO_OptionValue::ensureOptionValueExists(array(
659 'option_group_id' => 'account_relationship',
660 'name' => 'Chargeback Account is',
661 'label' => ts('Chargeback Account is'),
662 'is_active' => TRUE,
663 'component_id' => 'CiviContribute',
664 ));
665
666 CRM_Core_BAO_OptionValue::ensureOptionValueExists(array(
667 'option_group_id' => 'contribution_status',
668 'name' => 'Chargeback',
669 'label' => ts('Chargeback'),
670 'is_active' => TRUE,
671 'component_id' => 'CiviContribute',
672 ));
673 return TRUE;
674 }
675
676 /**
677 * CRM-17999 Add index to civicrm_contribution.source.
678 *
679 * @param \CRM_Queue_TaskContext $ctx
680 *
681 * @return bool
682 */
683 public static function addIndexContributionSource(CRM_Queue_TaskContext $ctx) {
684 CRM_Core_BAO_SchemaHandler::createIndexes(array('civicrm_contribution' => array('source')));
685 return TRUE;
686 }
687
688 /**
689 * CRM-18124 Add index to civicrm_contribution.total_amount.
690 *
691 * Note that I made this a combined index with receive_date because the issue included
692 * both criteria and they seemed likely to be used in conjunction to me in other cases.
693 *
694 * @param \CRM_Queue_TaskContext $ctx
695 *
696 * @return bool
697 */
698 public static function addIndexContributionAmount(CRM_Queue_TaskContext $ctx) {
699 CRM_Core_BAO_SchemaHandler::createIndexes(array(
700 'civicrm_contribution' => array(array('total_amount', 'receive_date')),
701 ));
702 return TRUE;
703 }
704
705 /**
706 * CRM-18124 Add index to civicrm_contribution.total_amount.
707 *
708 * Note that I made this a combined index with receive_date because the issue included
709 * both criteria and they seemed likely to be used in conjunction to me in other cases.
710 *
711 * @param \CRM_Queue_TaskContext $ctx
712 *
713 * @return bool
714 */
715 public static function addDeletedByMergeActivityType(CRM_Queue_TaskContext $ctx) {
716 CRM_Core_BAO_OptionValue::ensureOptionValueExists(array(
717 'option_group_id' => 'activity_type',
718 'name' => 'Contact Deleted by Merge',
719 'label' => ts('Contact Deleted by Merge'),
720 'description' => ts('Contact was merged into another contact'),
721 'is_active' => TRUE,
722 'filter' => 1,
723 ));
724 return TRUE;
725 }
726
727 /**
728 * CRM-12252 Add Help Pre and Help Post Fields for Price Field Value Table.
729 *
730 * @param \CRM_Queue_TaskContext $ctx
731 *
732 * @return bool
733 */
734 public static function addHelpPreAndHelpPostFieldsPriceFieldValue(CRM_Queue_TaskContext $ctx) {
735 $domain = new CRM_Core_DAO_Domain();
736 $domain->find(TRUE);
737 if ($domain->locales) {
738 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
739 foreach ($locales as $locale) {
740 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists("civicrm_price_field_value", "help_pre_{$locale}")) {
741 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_price_field_value`
742 ADD COLUMN `help_pre_{$locale}` text COLLATE utf8_unicode_ci COMMENT 'Price field option pre help text.'", array(), TRUE, NULL, FALSE, FALSE);
743 }
744 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists("civicrm_price_field_value", "help_post_{$locale}")) {
745 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_price_field_value`
746 ADD COLUMN `help_post_{$locale}` text COLLATE utf8_unicode_ci COMMENT 'Price field option post help text.'", array(), TRUE, NULL, FALSE, FALSE);
747 }
748 }
749 CRM_Core_I18n_Schema::rebuildMultilingualSchema($locales, NULL);
750 }
751 else {
752 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_price_field_value', 'help_pre')) {
753 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_price_field_value`
754 ADD COLUMN `help_pre` text COLLATE utf8_unicode_ci COMMENT 'Price field option pre help text.'");
755 }
756 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_price_field_value', 'help_post')) {
757 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_price_field_value`
758 ADD COLUMN `help_post` text COLLATE utf8_unicode_ci COMMENT 'Price field option post help text.'");
759 }
760 }
761 return TRUE;
762 }
763
764 /**
765 * CRM-18345 Don't delete mailing data on email/phone deletion
766 * Implemented here in CRM-18526
767 *
768 * @param \CRM_Queue_TaskContext $ctx
769 *
770 * @return bool
771 */
772 public static function upgradeMailingFKs(CRM_Queue_TaskContext $ctx) {
773
774 // Safely drop the foreign keys
775 CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_mailing_event_queue', 'FK_civicrm_mailing_event_queue_email_id');
776 CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_mailing_event_queue', 'FK_civicrm_mailing_event_queue_phone_id');
777 CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_mailing_recipients', 'FK_civicrm_mailing_recipients_email_id');
778 CRM_Core_BAO_SchemaHandler::safeRemoveFK('civicrm_mailing_recipients', 'FK_civicrm_mailing_recipients_phone_id');
779
780 // Set up the new foreign keys
781 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 0;");
782
783 CRM_Core_DAO::executeQuery("
784 ALTER TABLE `civicrm_mailing_event_queue`
785 ADD CONSTRAINT `FK_civicrm_mailing_event_queue_email_id`
786 FOREIGN KEY (`email_id`)
787 REFERENCES `civicrm_email`(`id`)
788 ON DELETE SET NULL
789 ON UPDATE RESTRICT;
790 ");
791
792 CRM_Core_DAO::executeQuery("
793 ALTER TABLE `civicrm_mailing_event_queue`
794 ADD CONSTRAINT `FK_civicrm_mailing_event_queue_phone_id`
795 FOREIGN KEY (`phone_id`)
796 REFERENCES `civicrm_phone`(`id`)
797 ON DELETE SET NULL
798 ON UPDATE RESTRICT;
799 ");
800
801 CRM_Core_DAO::executeQuery("
802 ALTER TABLE `civicrm_mailing_recipients`
803 ADD CONSTRAINT `FK_civicrm_mailing_recipients_email_id`
804 FOREIGN KEY (`email_id`)
805 REFERENCES `civicrm_email`(`id`)
806 ON DELETE SET NULL
807 ON UPDATE RESTRICT;
808 ");
809
810 CRM_Core_DAO::executeQuery("
811 ALTER TABLE `civicrm_mailing_recipients`
812 ADD CONSTRAINT `FK_civicrm_mailing_recipients_phone_id`
813 FOREIGN KEY (`phone_id`)
814 REFERENCES `civicrm_phone`(`id`)
815 ON DELETE SET NULL
816 ON UPDATE RESTRICT;
817 ");
818
819 CRM_Core_DAO::executeQuery("SET FOREIGN_KEY_CHECKS = 1;");
820
821 return TRUE;
822 }
823
824 /**
825 * CRM-17663 - Dashboard schema changes
826 *
827 * @param \CRM_Queue_TaskContext $ctx
828 *
829 * @return bool
830 */
831 public static function dashboardSchemaUpdate(CRM_Queue_TaskContext $ctx) {
832 if (!CRM_Core_BAO_SchemaHandler::checkIfIndexExists('civicrm_dashboard_contact', 'index_dashboard_id_contact_id')) {
833 // Delete any stray duplicate rows and add unique index to prevent new dupes and enable INSERT/UPDATE combo query
834 CRM_Core_DAO::executeQuery('DELETE c1 FROM civicrm_dashboard_contact c1, civicrm_dashboard_contact c2 WHERE c1.contact_id = c2.contact_id AND c1.dashboard_id = c2.dashboard_id AND c1.id > c2.id');
835 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_dashboard_contact ADD UNIQUE INDEX index_dashboard_id_contact_id (dashboard_id, contact_id);');
836 }
837 $domain = new CRM_Core_DAO_Domain();
838 $domain->find(TRUE);
839 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard_contact', 'content');
840 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard_contact', 'is_minimized');
841 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard_contact', 'is_fullscreen');
842 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard_contact', 'created_date');
843 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard', 'is_fullscreen');
844 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard', 'is_minimized');
845 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard', 'column_no');
846 CRM_Core_BAO_SchemaHandler::dropColumn('civicrm_dashboard', 'weight');
847
848 CRM_Core_DAO::executeQuery('UPDATE civicrm_dashboard SET url = REPLACE(url, "&snippet=5", ""), fullscreen_url = REPLACE(fullscreen_url, "&snippet=5", "")');
849
850 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_dashboard', 'cache_minutes')) {
851 CRM_Core_DAO::executeQuery('ALTER TABLE civicrm_dashboard ADD COLUMN cache_minutes int unsigned NOT NULL DEFAULT 60 COMMENT "Number of minutes to cache dashlet content in browser localStorage."',
852 array(), TRUE, NULL, FALSE, FALSE);
853 }
854 if ($domain->locales) {
855 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
856 CRM_Core_I18n_Schema::rebuildMultilingualSchema($locales, NULL);
857 }
858
859 CRM_Core_DAO::executeQuery('UPDATE civicrm_dashboard SET cache_minutes = 1440 WHERE name = "blog"');
860 CRM_Core_DAO::executeQuery('UPDATE civicrm_dashboard SET cache_minutes = 7200 WHERE name IN ("activity","getting-started")');
861 return TRUE;
862 }
863
864 /**
865 * CRM-19100 - Alter Index and Type for Image URL
866 * @return bool
867 */
868 public static function alterIndexAndTypeForImageURL() {
869 $length = array();
870 CRM_Core_BAO_SchemaHandler::dropIndexIfExists('civicrm_contact', 'index_image_url');
871 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_contact` CHANGE `image_URL` `image_URL` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT 'optional URL for preferred image (photo, logo, etc.) to display for this contact.'");
872
873 $length['civicrm_contact']['image_URL'] = 128;
874 CRM_Core_BAO_SchemaHandler::createIndexes(array('civicrm_contact' => array('image_URL')), 'index', $length);
875
876 return TRUE;
877 }
878
879 /**
880 * CRM-18651 Add DataType column to Option Group Table
881 * @return bool
882 */
883 public static function addDataTypeColumnToOptionGroupTable() {
884 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_option_group', 'data_type')) {
885 CRM_Core_DAO::executeQuery("ALTER TABLE `civicrm_option_group` ADD COLUMN `data_type` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL comment 'Data Type of Option Group.'",
886 array(), TRUE, NULL, FALSE, FALSE);
887 }
888 $domain = new CRM_Core_DAO_Domain();
889 $domain->find(TRUE);
890 if ($domain->locales) {
891 $locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
892 CRM_Core_I18n_Schema::rebuildMultilingualSchema($locales, NULL);
893 }
894
895 CRM_Core_DAO::executeQuery("UPDATE `civicrm_option_group` SET `data_type` = 'Integer'
896 WHERE name IN ('activity_type', 'gender', 'payment_instrument', 'participant_role', 'event_type')");
897 return TRUE;
898 }
899
900 /**
901 * CRM-19372 Add field to store accepted credit credit cards for a payment processor.
902 * @return bool
903 */
904 public static function addAcceptedCardTypesField() {
905 if (!CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_payment_processor', 'accepted_credit_cards')) {
906 CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_payment_processor ADD COLUMN `accepted_credit_cards` text DEFAULT NULL COMMENT 'array of accepted credit card types'");
907 }
908 return TRUE;
909 }
910
911 /**
912 * CRM-19372 Add field to store accepted credit credit cards for a payment processor.
913 * @return bool
914 */
915 public static function addWysiwygPresets() {
916 CRM_Core_BAO_OptionGroup::ensureOptionGroupExists(array(
917 'name' => 'wysiwyg_presets',
918 'title' => ts('WYSIWYG Editor Presets'),
919 'is_reserved' => 1,
920 ));
921 $values = array(
922 'default' => array('label' => ts('Default'), 'is_default' => 1),
923 'civimail' => array('label' => ts('CiviMail'), 'component_id' => 'CiviMail'),
924 'civievent' => array('label' => ts('CiviEvent'), 'component_id' => 'CiviEvent'),
925 );
926 foreach ($values as $name => $value) {
927 CRM_Core_BAO_OptionValue::ensureOptionValueExists($value + array(
928 'name' => $name,
929 'option_group_id' => 'wysiwyg_presets',
930 ));
931 }
932 $fileName = Civi::paths()->getPath('[civicrm.files]/persist/crm-ckeditor-config.js');
933 // Ensure the config file contains the allowedContent setting
934 if (file_exists($fileName)) {
935 $config = file_get_contents($fileName);
936 $pos = strrpos($config, '};');
937 $setting = "\n\tconfig.allowedContent = true;\n";
938 $config = substr_replace($config, $setting, $pos, 0);
939 unlink($fileName);
940 $newFileName = Civi::paths()->getPath('[civicrm.files]/persist/crm-ckeditor-default.js');
941 file_put_contents($newFileName, $config);
942 }
943 return TRUE;
944 }
945
946 }