Merge pull request #16835 from colemanw/removeOldStuff
[civicrm-core.git] / CRM / Upgrade / Form.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 * $Id$
17 *
18 */
19class CRM_Upgrade_Form extends CRM_Core_Form {
7da04cde 20 const QUEUE_NAME = 'CRM_Upgrade';
6a488035
TO
21
22 /**
23 * Minimum size of MySQL's thread_stack option
24 *
25 * @see install/index.php MINIMUM_THREAD_STACK
26 */
27 const MINIMUM_THREAD_STACK = 192;
28
304c1a5a
CW
29 /**
30 * Minimum previous CiviCRM version we can directly upgrade from
31 */
e32e8f31 32 const MINIMUM_UPGRADABLE_VERSION = '4.2.9';
304c1a5a
CW
33
34 /**
cc1f4988 35 * Minimum php version required to run (equal to or lower than the minimum install version)
96298d46 36 *
7cd811db 37 * As of Civi 5.16, using PHP 5.x will lead to a hard crash during bootstrap.
dc3ace93
TO
38 *
39 * Tip: Keep in sync with composer.json ("config => platform => php")
304c1a5a 40 */
a1400a8e 41 const MINIMUM_PHP_VERSION = '7.1.0';
304c1a5a 42
3655bea4
SL
43 /**
44 * @var \CRM_Core_Config
45 */
6a488035
TO
46 protected $_config;
47
6a488035 48 /**
fe482240 49 * Upgrade for multilingual.
6a488035 50 *
b67daa72 51 * @var bool
6a488035
TO
52 */
53 public $multilingual = FALSE;
54
55 /**
fe482240 56 * Locales available for multilingual upgrade.
6a488035
TO
57 *
58 * @var array
6a488035
TO
59 */
60 public $locales;
61
624e56fa 62 /**
fe482240 63 * Constructor for the basic form page.
624e56fa
EM
64 *
65 * We should not use QuickForm directly. This class provides a lot
66 * of default convenient functions, rules and buttons
67 *
c68f8bfa
TO
68 * @param object $state
69 * State associated with this form.
e8e8f3ad 70 * @param const|\enum|int $action The mode the form is operating in (None/Create/View/Update/Delete)
c68f8bfa
TO
71 * @param string $method
72 * The type of http method used (GET/POST).
73 * @param string $name
74 * The name of the form if different from class name.
624e56fa 75 */
ae5ffbb7 76 public function __construct(
4e66d748 77 $state = NULL,
6a488035
TO
78 $action = CRM_Core_Action::NONE,
79 $method = 'post',
e418776c 80 $name = NULL
6a488035
TO
81 ) {
82 $this->_config = CRM_Core_Config::singleton();
83
6a488035
TO
84 $domain = new CRM_Core_DAO_Domain();
85 $domain->find(TRUE);
86
87 $this->multilingual = (bool) $domain->locales;
88 $this->locales = explode(CRM_Core_DAO::VALUE_SEPARATOR, $domain->locales);
89
90 $smarty = CRM_Core_Smarty::singleton();
635f0b86 91 //$smarty->compile_dir = $this->_config->templateCompileDir;
6a488035
TO
92 $smarty->assign('multilingual', $this->multilingual);
93 $smarty->assign('locales', $this->locales);
94
95 // we didn't call CRM_Core_BAO_ConfigSetting::retrieve(), so we need to set $dbLocale by hand
96 if ($this->multilingual) {
97 global $dbLocale;
98 $dbLocale = "_{$this->_config->lcMessages}";
99 }
100
101 parent::__construct($state, $action, $method, $name);
102 }
103
624e56fa
EM
104 /**
105 * @param $version
106 *
107 * @return mixed
108 */
00be9182 109 public static function &incrementalPhpObject($version) {
be2fb01f 110 static $incrementalPhpObject = [];
6a488035
TO
111
112 $versionParts = explode('.', $version);
0ae8b1af 113 $versionName = CRM_Utils_EnglishNumber::toCamelCase($versionParts[0]) . CRM_Utils_EnglishNumber::toCamelCase($versionParts[1]);
6a488035
TO
114
115 if (!array_key_exists($versionName, $incrementalPhpObject)) {
0e6e8724 116 $className = "CRM_Upgrade_Incremental_php_{$versionName}";
e8cb3963 117 $incrementalPhpObject[$versionName] = new $className();
6a488035
TO
118 }
119 return $incrementalPhpObject[$versionName];
120 }
121
624e56fa
EM
122 /**
123 * @param $version
124 * @param $release
125 *
126 * @return bool
127 */
00be9182 128 public function checkVersionRelease($version, $release) {
6a488035 129 $versionParts = explode('.', $version);
bf6a5362 130 return ($versionParts[2] == $release);
6a488035
TO
131 }
132
624e56fa
EM
133 /**
134 * @param $constraints
135 *
136 * @return array
137 */
00be9182 138 public function checkSQLConstraints(&$constraints) {
6a488035
TO
139 $pass = $fail = 0;
140 foreach ($constraints as $constraint) {
141 if ($this->checkSQLConstraint($constraint)) {
142 $pass++;
143 }
144 else {
145 $fail++;
146 }
be2fb01f 147 return [$pass, $fail];
6a488035
TO
148 }
149 }
150
624e56fa
EM
151 /**
152 * @param $constraint
153 *
154 * @return bool
155 */
00be9182 156 public function checkSQLConstraint($constraint) {
6a488035
TO
157 // check constraint here
158 return TRUE;
159 }
160
624e56fa 161 /**
100fef9d 162 * @param string $fileName
624e56fa
EM
163 * @param bool $isQueryString
164 */
00be9182 165 public function source($fileName, $isQueryString = FALSE) {
c0e4c31d
JK
166 if ($isQueryString) {
167 CRM_Utils_File::runSqlQuery($this->_config->dsn,
168 $fileName, NULL
169 );
170 }
171 else {
172 CRM_Utils_File::sourceSQLFile($this->_config->dsn,
173 $fileName, NULL
174 );
175 }
6a488035
TO
176 }
177
00be9182 178 public function preProcess() {
6a488035
TO
179 CRM_Utils_System::setTitle($this->getTitle());
180 if (!$this->verifyPreDBState($errorMessage)) {
181 if (!isset($errorMessage)) {
182 $errorMessage = 'pre-condition failed for current upgrade step';
183 }
184 CRM_Core_Error::fatal($errorMessage);
185 }
186 $this->assign('recentlyViewed', FALSE);
187 }
188
00be9182 189 public function buildQuickForm() {
6a488035
TO
190 $this->addDefaultButtons($this->getButtonTitle(),
191 'next',
192 NULL,
193 TRUE
194 );
195 }
196
624e56fa 197 /**
100fef9d 198 * Getter function for title. Should be over-ridden by derived class
624e56fa
EM
199 *
200 * @return string
624e56fa 201 */
3655bea4 202
624e56fa
EM
203 /**
204 * @return string
205 */
00be9182 206 public function getTitle() {
6a488035
TO
207 return ts('Title not Set');
208 }
209
624e56fa
EM
210 /**
211 * @return string
212 */
00be9182 213 public function getFieldsetTitle() {
ba8f6a69 214 return '';
6a488035
TO
215 }
216
624e56fa
EM
217 /**
218 * @return string
219 */
00be9182 220 public function getButtonTitle() {
6a488035
TO
221 return ts('Continue');
222 }
223
624e56fa 224 /**
fe482240 225 * Use the form name to create the tpl file name.
624e56fa
EM
226 *
227 * @return string
624e56fa 228 */
3655bea4 229
624e56fa
EM
230 /**
231 * @return string
232 */
00be9182 233 public function getTemplateFileName() {
6a488035
TO
234 $this->assign('title',
235 $this->getFieldsetTitle()
236 );
237 $this->assign('message',
238 $this->getTemplateMessage()
239 );
240 return 'CRM/Upgrade/Base.tpl';
241 }
242
00be9182 243 public function postProcess() {
6a488035
TO
244 $this->upgrade();
245
246 if (!$this->verifyPostDBState($errorMessage)) {
247 if (!isset($errorMessage)) {
248 $errorMessage = 'post-condition failed for current upgrade step';
249 }
250 CRM_Core_Error::fatal($errorMessage);
251 }
252 }
253
624e56fa
EM
254 /**
255 * @param $query
256 *
257 * @return Object
258 */
00be9182 259 public function runQuery($query) {
e03e1641 260 return CRM_Core_DAO::executeQuery($query);
6a488035
TO
261 }
262
624e56fa
EM
263 /**
264 * @param $version
265 *
266 * @return Object
267 */
00be9182 268 public function setVersion($version) {
6a488035
TO
269 $this->logVersion($version);
270
271 $query = "
272UPDATE civicrm_domain
273SET version = '$version'
274";
275 return $this->runQuery($query);
276 }
277
624e56fa
EM
278 /**
279 * @param $newVersion
280 *
281 * @return bool
282 */
00be9182 283 public function logVersion($newVersion) {
6a488035
TO
284 if ($newVersion) {
285 $oldVersion = CRM_Core_BAO_Domain::version();
286
287 $session = CRM_Core_Session::singleton();
be2fb01f 288 $logParams = [
6a488035
TO
289 'entity_table' => 'civicrm_domain',
290 'entity_id' => 1,
291 'data' => "upgrade:{$oldVersion}->{$newVersion}",
292 // lets skip 'modified_id' for now, as it causes FK issues And
293 // is not very important for now.
294 'modified_date' => date('YmdHis'),
be2fb01f 295 ];
6a488035
TO
296 CRM_Core_BAO_Log::add($logParams);
297 return TRUE;
298 }
299
300 return FALSE;
301 }
302
624e56fa
EM
303 /**
304 * @param $version
305 *
306 * @return bool
307 */
00be9182 308 public function checkVersion($version) {
6a488035
TO
309 $domainID = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Domain',
310 $version, 'id',
311 'version'
312 );
313 return $domainID ? TRUE : FALSE;
314 }
315
624e56fa
EM
316 /**
317 * @return array
318 * @throws Exception
319 */
00be9182 320 public function getRevisionSequence() {
be2fb01f 321 $revList = [];
6a488035 322 $sqlDir = implode(DIRECTORY_SEPARATOR,
be2fb01f 323 [dirname(__FILE__), 'Incremental', 'sql']
6a488035
TO
324 );
325 $sqlFiles = scandir($sqlDir);
326
327 $sqlFilePattern = '/^((\d{1,2}\.\d{1,2})\.(\d{1,2}\.)?(\d{1,2}|\w{4,7}))\.(my)?sql(\.tpl)?$/i';
328 foreach ($sqlFiles as $file) {
329 if (preg_match($sqlFilePattern, $file, $matches)) {
6a488035
TO
330 if (!in_array($matches[1], $revList)) {
331 $revList[] = $matches[1];
332 }
333 }
334 }
335
6a488035
TO
336 usort($revList, 'version_compare');
337 return $revList;
338 }
339
624e56fa
EM
340 /**
341 * @param $rev
342 * @param int $index
343 *
344 * @return null
345 */
00be9182 346 public static function getRevisionPart($rev, $index = 1) {
6a488035
TO
347 $revPattern = '/^((\d{1,2})\.\d{1,2})\.(\d{1,2}|\w{4,7})?$/i';
348 preg_match($revPattern, $rev, $matches);
349
350 return array_key_exists($index, $matches) ? $matches[$index] : NULL;
351 }
352
624e56fa
EM
353 /**
354 * @param $tplFile
355 * @param $rev
356 *
357 * @return bool
358 */
00be9182 359 public function processLocales($tplFile, $rev) {
6a488035
TO
360 $smarty = CRM_Core_Smarty::singleton();
361 $smarty->assign('domainID', CRM_Core_Config::domainID());
362
363 $this->source($smarty->fetch($tplFile), TRUE);
364
365 if ($this->multilingual) {
366 CRM_Core_I18n_Schema::rebuildMultilingualSchema($this->locales, $rev);
367 }
368 return $this->multilingual;
369 }
370
624e56fa
EM
371 /**
372 * @param $rev
373 */
00be9182 374 public function setSchemaStructureTables($rev) {
6a488035
TO
375 if ($this->multilingual) {
376 CRM_Core_I18n_Schema::schemaStructureTables($rev, TRUE);
377 }
378 }
379
624e56fa
EM
380 /**
381 * @param $rev
382 *
383 * @throws Exception
384 */
00be9182 385 public function processSQL($rev) {
6a488035 386 $sqlFile = implode(DIRECTORY_SEPARATOR,
be2fb01f 387 [
353ffa53
TO
388 dirname(__FILE__),
389 'Incremental',
390 'sql',
391 $rev . '.mysql',
be2fb01f 392 ]
6a488035
TO
393 );
394 $tplFile = "$sqlFile.tpl";
395
396 if (file_exists($tplFile)) {
397 $this->processLocales($tplFile, $rev);
398 }
399 else {
400 if (!file_exists($sqlFile)) {
401 CRM_Core_Error::fatal("sqlfile - $rev.mysql not found.");
402 }
403 $this->source($sqlFile);
404 }
405 }
406
407 /**
fe482240 408 * Determine the start and end version of the upgrade process.
6a488035
TO
409 *
410 * @return array(0=>$currentVer, 1=>$latestVer)
411 */
00be9182 412 public function getUpgradeVersions() {
6a488035 413 $latestVer = CRM_Utils_System::version();
e418776c 414 $currentVer = CRM_Core_BAO_Domain::version(TRUE);
6a488035
TO
415 if (!$currentVer) {
416 CRM_Core_Error::fatal(ts('Version information missing in civicrm database.'));
417 }
418 elseif (stripos($currentVer, 'upgrade')) {
419 CRM_Core_Error::fatal(ts('Database check failed - the database looks to have been partially upgraded. You may want to reload the database with the backup and try the upgrade process again.'));
420 }
421 if (!$latestVer) {
422 CRM_Core_Error::fatal(ts('Version information missing in civicrm codebase.'));
423 }
424
be2fb01f 425 return [$currentVer, $latestVer];
6a488035
TO
426 }
427
428 /**
429 * Determine if $currentVer can be upgraded to $latestVer
430 *
77b97be7
EM
431 * @param $currentVer
432 * @param $latestVer
433 *
6a488035
TO
434 * @return mixed, a string error message or boolean 'false' if OK
435 */
00be9182 436 public function checkUpgradeableVersion($currentVer, $latestVer) {
6a488035
TO
437 $error = FALSE;
438 // since version is suppose to be in valid format at this point, especially after conversion ($convertVer),
439 // lets do a pattern check -
440 if (!CRM_Utils_System::isVersionFormatValid($currentVer)) {
441 $error = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
442 }
443 elseif (version_compare($currentVer, $latestVer) > 0) {
444 // DB version number is higher than codebase being upgraded to. This is unexpected condition-fatal error.
445 $error = ts('Your database is marked with an unexpected version number: %1. The automated upgrade to version %2 can not be run - and the %2 codebase may not be compatible with your database state. You will need to determine the correct version corresponding to your current database state. You may want to revert to the codebase you were using prior to beginning this upgrade until you resolve this problem.',
be2fb01f 446 [1 => $currentVer, 2 => $latestVer]
6a488035
TO
447 );
448 }
449 elseif (version_compare($currentVer, $latestVer) == 0) {
450 $error = ts('Your database has already been upgraded to CiviCRM %1',
be2fb01f 451 [1 => $latestVer]
6a488035
TO
452 );
453 }
304c1a5a
CW
454 elseif (version_compare($currentVer, self::MINIMUM_UPGRADABLE_VERSION) < 0) {
455 $error = ts('CiviCRM versions prior to %1 cannot be upgraded directly to %2. This upgrade will need to be done in stages. First download an intermediate version (the LTS may be a good choice) and upgrade to that before proceeding to this version.',
be2fb01f 456 [1 => self::MINIMUM_UPGRADABLE_VERSION, 2 => $latestVer]
304c1a5a
CW
457 );
458 }
6a488035 459
304c1a5a 460 if (version_compare(phpversion(), self::MINIMUM_PHP_VERSION) < 0) {
6a488035 461 $error = ts('CiviCRM %3 requires PHP version %1 (or newer), but the current system uses %2 ',
be2fb01f 462 [
304c1a5a
CW
463 1 => self::MINIMUM_PHP_VERSION,
464 2 => phpversion(),
353ffa53 465 3 => $latestVer,
be2fb01f 466 ]);
6a488035
TO
467 }
468
469 // check for mysql trigger privileges
509e50b7 470 if (!\Civi::settings()->get('logging_no_trigger_permission') && !CRM_Core_DAO::checkTriggerViewPermission(FALSE, TRUE)) {
6a488035 471 $error = ts('CiviCRM %1 requires MySQL trigger privileges.',
be2fb01f 472 [1 => $latestVer]);
6a488035 473 }
032c9d10 474
e418776c 475 if (CRM_Core_DAO::getGlobalSetting('thread_stack', 0) < (1024 * self::MINIMUM_THREAD_STACK)) {
be2fb01f 476 $error = ts('CiviCRM %1 requires MySQL thread stack >= %2k', [
6a488035 477 1 => $latestVer,
21dfd5f5 478 2 => self::MINIMUM_THREAD_STACK,
be2fb01f 479 ]);
6a488035
TO
480 }
481
482 return $error;
483 }
484
485 /**
486 * Determine if $currentver already matches $latestVer
487 *
77b97be7
EM
488 * @param $currentVer
489 * @param $latestVer
490 *
6a488035
TO
491 * @return mixed, a string error message or boolean 'false' if OK
492 */
00be9182 493 public function checkCurrentVersion($currentVer, $latestVer) {
6a488035
TO
494 $error = FALSE;
495
496 // since version is suppose to be in valid format at this point, especially after conversion ($convertVer),
497 // lets do a pattern check -
498 if (!CRM_Utils_System::isVersionFormatValid($currentVer)) {
499 $error = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
500 }
501 elseif (version_compare($currentVer, $latestVer) != 0) {
502 $error = ts('Your database is not configured for version %1',
be2fb01f 503 [1 => $latestVer]
6a488035
TO
504 );
505 }
506 return $error;
507 }
508
509 /**
fe482240 510 * Fill the queue with upgrade tasks.
6a488035 511 *
5a4f6742
CW
512 * @param string $currentVer
513 * the original revision.
514 * @param string $latestVer
515 * the target (final) revision.
516 * @param string $postUpgradeMessageFile
517 * path of a modifiable file which lists the post-upgrade messages.
6a488035 518 *
bf6a5362 519 * @return CRM_Queue_Service
6a488035 520 */
00be9182 521 public static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFile) {
6a488035
TO
522 $upgrade = new CRM_Upgrade_Form();
523
6a488035
TO
524 // Ensure that queue can be created
525 if (!CRM_Queue_BAO_QueueItem::findCreateTable()) {
526 CRM_Core_Error::fatal(ts('Failed to find or create queueing table'));
527 }
be2fb01f 528 $queue = CRM_Queue_Service::singleton()->create([
353ffa53
TO
529 'name' => self::QUEUE_NAME,
530 'type' => 'Sql',
531 'reset' => TRUE,
be2fb01f 532 ]);
6a488035 533
9e799b1d 534 $task = new CRM_Queue_Task(
be2fb01f
CW
535 ['CRM_Upgrade_Form', 'doFileCleanup'],
536 [$postUpgradeMessageFile],
9e799b1d
TO
537 "Cleanup old files"
538 );
539 $queue->createItem($task);
540
e4c4f267 541 $task = new CRM_Queue_Task(
be2fb01f
CW
542 ['CRM_Upgrade_Form', 'disableOldExtensions'],
543 [$postUpgradeMessageFile],
e4c4f267
CW
544 "Checking extensions"
545 );
546 $queue->createItem($task);
547
6a488035
TO
548 $revisions = $upgrade->getRevisionSequence();
549 foreach ($revisions as $rev) {
550 // proceed only if $currentVer < $rev
551 if (version_compare($currentVer, $rev) < 0) {
552 $beginTask = new CRM_Queue_Task(
353ffa53 553 // callback
be2fb01f 554 ['CRM_Upgrade_Form', 'doIncrementalUpgradeStart'],
6a488035 555 // arguments
be2fb01f 556 [$rev],
6a488035
TO
557 "Begin Upgrade to $rev"
558 );
559 $queue->createItem($beginTask);
560
561 $task = new CRM_Queue_Task(
353ffa53 562 // callback
be2fb01f 563 ['CRM_Upgrade_Form', 'doIncrementalUpgradeStep'],
6a488035 564 // arguments
be2fb01f 565 [$rev, $currentVer, $latestVer, $postUpgradeMessageFile],
6a488035
TO
566 "Upgrade DB to $rev"
567 );
568 $queue->createItem($task);
569
570 $task = new CRM_Queue_Task(
353ffa53 571 // callback
be2fb01f 572 ['CRM_Upgrade_Form', 'doIncrementalUpgradeFinish'],
6a488035 573 // arguments
be2fb01f 574 [$rev, $currentVer, $latestVer, $postUpgradeMessageFile],
6a488035
TO
575 "Finish Upgrade DB to $rev"
576 );
577 $queue->createItem($task);
578 }
579 }
580
581 return $queue;
582 }
583
9e799b1d
TO
584 /**
585 * Find any old, orphaned files that should have been deleted.
586 *
587 * These files can get left behind, eg, if you use the Joomla
588 * upgrade procedure.
589 *
590 * The earlier we can do this, the better - don't want upgrade logic
591 * to inadvertently rely on old/relocated files.
592 *
593 * @param \CRM_Queue_TaskContext $ctx
594 * @param string $postUpgradeMessageFile
595 * @return bool
596 */
597 public static function doFileCleanup(CRM_Queue_TaskContext $ctx, $postUpgradeMessageFile) {
598 $source = new CRM_Utils_Check_Component_Source();
599 $files = $source->findOrphanedFiles();
be2fb01f 600 $errors = [];
9e799b1d
TO
601 foreach ($files as $file) {
602 if (is_dir($file['path'])) {
603 @rmdir($file['path']);
604 }
605 else {
606 @unlink($file['path']);
607 }
608
609 if (file_exists($file['path'])) {
610 $errors[] = sprintf("<li>%s</li>", htmlentities($file['path']));
611 }
612 }
613
614 if (!empty($errors)) {
615 file_put_contents($postUpgradeMessageFile,
616 '<br/><br/>' . ts('Some old files could not be removed. Please remove them.')
617 . '<ul>' . implode("\n", $errors) . '</ul>',
618 FILE_APPEND
619 );
620 }
621
622 return TRUE;
623 }
624
e4c4f267 625 /**
df7a1988 626 * Disable/uninstall any extensions not compatible with this new version.
e4c4f267
CW
627 *
628 * @param \CRM_Queue_TaskContext $ctx
629 * @param string $postUpgradeMessageFile
630 * @return bool
631 */
632 public static function disableOldExtensions(CRM_Queue_TaskContext $ctx, $postUpgradeMessageFile) {
df7a1988 633 $messages = [];
e4c4f267 634 $manager = CRM_Extension_System::singleton()->getManager();
df7a1988
CW
635 foreach ($manager->getStatuses() as $key => $status) {
636 $obsolete = $manager->isIncompatible($key);
637 if ($obsolete) {
638 if (!empty($obsolete['disable']) && in_array($status, [$manager::STATUS_INSTALLED, $manager::STATUS_INSTALLED_MISSING])) {
639 try {
640 $manager->disable($key);
641 // Update the status for the sake of uninstall below.
642 $status = $status == $manager::STATUS_INSTALLED ? $manager::STATUS_DISABLED : $manager::STATUS_DISABLED_MISSING;
643 // This message is intentionally overwritten by uninstall below as it would be redundant
644 $messages[$key] = ts('The extension %1 is now obsolete and has been disabled.', [1 => $key]);
645 }
646 catch (CRM_Extension_Exception $e) {
647 $messages[] = ts('The obsolete extension %1 could not be removed due to an error. It is recommended to remove this extension manually.', [1 => $key]);
648 }
649 }
650 if (!empty($obsolete['uninstall']) && in_array($status, [$manager::STATUS_DISABLED, $manager::STATUS_DISABLED_MISSING])) {
651 try {
652 $manager->uninstall($key);
653 $messages[$key] = ts('The extension %1 is now obsolete and has been uninstalled.', [1 => $key]);
654 if ($status == $manager::STATUS_DISABLED) {
655 $messages[$key] .= ' ' . ts('You can remove it from your extensions directory.');
656 }
657 }
658 catch (CRM_Extension_Exception $e) {
659 $messages[] = ts('The obsolete extension %1 could not be removed due to an error. It is recommended to remove this extension manually.', [1 => $key]);
660 }
661 }
8a0199a3
TO
662 if (!empty($obsolete['force-uninstall'])) {
663 CRM_Core_DAO::executeQuery('UPDATE civicrm_extension SET is_active = 0 WHERE full_name = %1', [
664 1 => [$key, 'String'],
665 ]);
666 }
e4c4f267
CW
667 }
668 }
df7a1988 669 if ($messages) {
e4c4f267 670 file_put_contents($postUpgradeMessageFile,
df7a1988 671 '<br/><br/><ul><li>' . implode("</li>\n<li>", $messages) . '</li></ul>',
e4c4f267
CW
672 FILE_APPEND
673 );
674 }
675
676 return TRUE;
677 }
678
6a488035 679 /**
fe482240 680 * Perform an incremental version update.
6a488035 681 *
77b97be7 682 * @param CRM_Queue_TaskContext $ctx
5a4f6742
CW
683 * @param string $rev
684 * the target (intermediate) revision e.g '3.2.alpha1'.
77b97be7
EM
685 *
686 * @return bool
6a488035 687 */
00be9182 688 public static function doIncrementalUpgradeStart(CRM_Queue_TaskContext $ctx, $rev) {
6a488035
TO
689 $upgrade = new CRM_Upgrade_Form();
690
691 // as soon as we start doing anything we append ".upgrade" to version.
692 // this also helps detect any partial upgrade issues
693 $upgrade->setVersion($rev . '.upgrade');
694
695 return TRUE;
696 }
697
698 /**
fe482240 699 * Perform an incremental version update.
6a488035 700 *
77b97be7 701 * @param CRM_Queue_TaskContext $ctx
5a4f6742
CW
702 * @param string $rev
703 * the target (intermediate) revision e.g '3.2.alpha1'.
704 * @param string $originalVer
705 * the original revision.
706 * @param string $latestVer
707 * the target (final) revision.
708 * @param string $postUpgradeMessageFile
709 * path of a modifiable file which lists the post-upgrade messages.
77b97be7
EM
710 *
711 * @return bool
6a488035 712 */
e418776c 713 public static function doIncrementalUpgradeStep(CRM_Queue_TaskContext $ctx, $rev, $originalVer, $latestVer, $postUpgradeMessageFile) {
6a488035
TO
714 $upgrade = new CRM_Upgrade_Form();
715
716 $phpFunctionName = 'upgrade_' . str_replace('.', '_', $rev);
717
bd00780f
CW
718 $versionObject = $upgrade->incrementalPhpObject($rev);
719
720 // pre-db check for major release.
721 if ($upgrade->checkVersionRelease($rev, 'alpha1')) {
be2fb01f 722 if (!(is_callable([$versionObject, 'verifyPreDBstate']))) {
bd00780f 723 CRM_Core_Error::fatal("verifyPreDBstate method was not found for $rev");
6a488035 724 }
6a488035 725
bd00780f
CW
726 $error = NULL;
727 if (!($versionObject->verifyPreDBstate($error))) {
728 if (!isset($error)) {
729 $error = "post-condition failed for current upgrade for $rev";
6a488035 730 }
bd00780f 731 CRM_Core_Error::fatal($error);
6a488035
TO
732 }
733
bd00780f 734 }
6a488035 735
bd00780f 736 $upgrade->setSchemaStructureTables($rev);
6a488035 737
be2fb01f 738 if (is_callable([$versionObject, $phpFunctionName])) {
bd00780f
CW
739 $versionObject->$phpFunctionName($rev, $originalVer, $latestVer);
740 }
741 else {
742 $upgrade->processSQL($rev);
743 }
744
745 // set post-upgrade-message if any
be2fb01f 746 if (is_callable([$versionObject, 'setPostUpgradeMessage'])) {
bd00780f
CW
747 $postUpgradeMessage = file_get_contents($postUpgradeMessageFile);
748 $versionObject->setPostUpgradeMessage($postUpgradeMessage, $rev);
bd00780f 749 file_put_contents($postUpgradeMessageFile, $postUpgradeMessage);
6a488035
TO
750 }
751
752 return TRUE;
753 }
754
755 /**
fe482240 756 * Perform an incremental version update.
6a488035 757 *
77b97be7 758 * @param CRM_Queue_TaskContext $ctx
5a4f6742
CW
759 * @param string $rev
760 * the target (intermediate) revision e.g '3.2.alpha1'.
761 * @param string $currentVer
762 * the original revision.
763 * @param string $latestVer
764 * the target (final) revision.
765 * @param string $postUpgradeMessageFile
766 * path of a modifiable file which lists the post-upgrade messages.
77b97be7
EM
767 *
768 * @return bool
6a488035 769 */
00be9182 770 public static function doIncrementalUpgradeFinish(CRM_Queue_TaskContext $ctx, $rev, $currentVer, $latestVer, $postUpgradeMessageFile) {
6a488035
TO
771 $upgrade = new CRM_Upgrade_Form();
772 $upgrade->setVersion($rev);
773 CRM_Utils_System::flushCache();
ac05cde3 774
d8a4acc0
C
775 $config = CRM_Core_Config::singleton();
776 $config->userSystem->flush();
6a488035
TO
777 return TRUE;
778 }
779
00be9182 780 public static function doFinish() {
6a488035
TO
781 $upgrade = new CRM_Upgrade_Form();
782 list($ignore, $latestVer) = $upgrade->getUpgradeVersions();
783 // Seems extraneous in context, but we'll preserve old behavior
784 $upgrade->setVersion($latestVer);
785
f806379b
TO
786 // Clear cached metadata.
787 Civi::service('settings_manager')->flush();
b6386d8c 788
6a488035
TO
789 // cleanup caches CRM-8739
790 $config = CRM_Core_Config::singleton();
1fcf16cc 791 $config->cleanupCaches(1);
6a488035 792
6b4bec74
CW
793 $versionCheck = new CRM_Utils_VersionCheck();
794 $versionCheck->flushCache();
795
6a488035
TO
796 // Rebuild all triggers and re-enable logging if needed
797 $logging = new CRM_Logging_Schema();
798 $logging->fixSchemaDifferences();
912511a3
SL
799
800 CRM_Core_ManagedEntities::singleton(TRUE)->reconcile(TRUE);
6a488035
TO
801 }
802
803 /**
804 * Compute any messages which should be displayed before upgrade
805 * by calling the 'setPreUpgradeMessage' on each incremental upgrade
806 * object.
807 *
5a4f6742
CW
808 * @param string $preUpgradeMessage
809 * alterable.
77b97be7
EM
810 * @param $currentVer
811 * @param $latestVer
6a488035 812 */
00be9182 813 public function setPreUpgradeMessage(&$preUpgradeMessage, $currentVer, $latestVer) {
49368097
CW
814 // check for changed message templates
815 CRM_Upgrade_Incremental_General::checkMessageTemplate($preUpgradeMessage, $latestVer, $currentVer);
816 // set global messages
817 CRM_Upgrade_Incremental_General::setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer);
6a488035
TO
818
819 // Scan through all php files and see if any file is interested in setting pre-upgrade-message
820 // based on $currentVer, $latestVer.
821 // Please note, at this point upgrade hasn't started executing queries.
822 $revisions = $this->getRevisionSequence();
823 foreach ($revisions as $rev) {
49368097 824 if (version_compare($currentVer, $rev) < 0) {
6a488035 825 $versionObject = $this->incrementalPhpObject($rev);
fe83c251 826 CRM_Upgrade_Incremental_General::updateMessageTemplate($preUpgradeMessage, $rev);
be2fb01f 827 if (is_callable([$versionObject, 'setPreUpgradeMessage'])) {
e418776c
TO
828 $versionObject->setPreUpgradeMessage($preUpgradeMessage, $rev, $currentVer);
829 }
6a488035
TO
830 }
831 }
832 }
96025800 833
6a488035 834}