3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.4 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2013
35 class CRM_Upgrade_Form
extends CRM_Core_Form
{
36 CONST QUEUE_NAME
= 'CRM_Upgrade';
39 * Minimum size of MySQL's thread_stack option
41 * @see install/index.php MINIMUM_THREAD_STACK
43 const MINIMUM_THREAD_STACK
= 192;
47 // note latestVersion is legacy code, and
48 // only used for 2.0 -> 2.1 upgrade
49 public $latestVersion;
52 * Upgrade for multilingual
57 public $multilingual = FALSE;
60 * locales available for multilingual upgrade
68 * number to string mapper
73 static $_numberMap = array(
86 function __construct($state = NULL,
87 $action = CRM_Core_Action
::NONE
,
91 $this->_config
= CRM_Core_Config
::singleton();
93 // this->latestVersion is legacy code, only used for 2.0 -> 2.1 upgrade
94 // latest ver in 2.1 series
95 $this->latestVersion
= '2.1.6';
97 $domain = new CRM_Core_DAO_Domain();
100 $this->multilingual
= (bool) $domain->locales
;
101 $this->locales
= explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $domain->locales
);
103 $smarty = CRM_Core_Smarty
::singleton();
104 $smarty->compile_dir
= $this->_config
->templateCompileDir
;
105 $smarty->assign('multilingual', $this->multilingual
);
106 $smarty->assign('locales', $this->locales
);
108 // we didn't call CRM_Core_BAO_ConfigSetting::retrieve(), so we need to set $dbLocale by hand
109 if ($this->multilingual
) {
111 $dbLocale = "_{$this->_config->lcMessages}";
114 parent
::__construct($state, $action, $method, $name);
117 static function &incrementalPhpObject($version) {
118 static $incrementalPhpObject = array();
120 $versionParts = explode('.', $version);
121 $versionName = self
::$_numberMap[$versionParts[0]] . self
::$_numberMap[$versionParts[1]];
123 if (!array_key_exists($versionName, $incrementalPhpObject)) {
124 $className = "CRM_Upgrade_Incremental_php_{$versionName}";
125 $incrementalPhpObject[$versionName] = new $className();
127 return $incrementalPhpObject[$versionName];
130 function checkVersionRelease($version, $release) {
131 $versionParts = explode('.', $version);
132 if ($versionParts[2] == $release) {
138 function checkSQLConstraints(&$constraints) {
140 foreach ($constraints as $constraint) {
141 if ($this->checkSQLConstraint($constraint)) {
147 return array($pass, $fail);
151 function checkSQLConstraint($constraint) {
152 // check constraint here
156 function source($fileName, $isQueryString = FALSE) {
158 CRM_Utils_File
::sourceSQLFile($this->_config
->dsn
,
159 $fileName, NULL, $isQueryString
163 function preProcess() {
164 CRM_Utils_System
::setTitle($this->getTitle());
165 if (!$this->verifyPreDBState($errorMessage)) {
166 if (!isset($errorMessage)) {
167 $errorMessage = 'pre-condition failed for current upgrade step';
169 CRM_Core_Error
::fatal($errorMessage);
171 $this->assign('recentlyViewed', FALSE);
174 function buildQuickForm() {
175 $this->addDefaultButtons($this->getButtonTitle(),
182 function getTitle() {
183 return ts('Title not Set');
186 function getFieldsetTitle() {
190 function getButtonTitle() {
191 return ts('Continue');
194 function getTemplateFileName() {
195 $this->assign('title',
196 $this->getFieldsetTitle()
198 $this->assign('message',
199 $this->getTemplateMessage()
201 return 'CRM/Upgrade/Base.tpl';
204 function postProcess() {
207 if (!$this->verifyPostDBState($errorMessage)) {
208 if (!isset($errorMessage)) {
209 $errorMessage = 'post-condition failed for current upgrade step';
211 CRM_Core_Error
::fatal($errorMessage);
215 function runQuery($query) {
216 return CRM_Core_DAO
::executeQuery($query,
217 CRM_Core_DAO
::$_nullArray
221 function setVersion($version) {
222 $this->logVersion($version);
225 UPDATE civicrm_domain
226 SET version = '$version'
228 return $this->runQuery($query);
231 function logVersion($newVersion) {
233 $oldVersion = CRM_Core_BAO_Domain
::version();
235 $session = CRM_Core_Session
::singleton();
237 'entity_table' => 'civicrm_domain',
239 'data' => "upgrade:{$oldVersion}->{$newVersion}",
240 // lets skip 'modified_id' for now, as it causes FK issues And
241 // is not very important for now.
242 'modified_date' => date('YmdHis'),
244 CRM_Core_BAO_Log
::add($logParams);
251 function checkVersion($version) {
252 $domainID = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_Domain',
256 return $domainID ?
TRUE : FALSE;
259 function getRevisionSequence() {
261 $sqlDir = implode(DIRECTORY_SEPARATOR
,
262 array(dirname(__FILE__
), 'Incremental', 'sql')
264 $sqlFiles = scandir($sqlDir);
266 $sqlFilePattern = '/^((\d{1,2}\.\d{1,2})\.(\d{1,2}\.)?(\d{1,2}|\w{4,7}))\.(my)?sql(\.tpl)?$/i';
267 foreach ($sqlFiles as $file) {
268 if (preg_match($sqlFilePattern, $file, $matches)) {
269 if ($matches[2] == '4.0') {
270 CRM_Core_Error
::fatal(ts("4.0.x upgrade files shouldn't exist. Contact Lobo to discuss this. This is related to the issue CRM-7731."));
272 if (!in_array($matches[1], $revList)) {
273 $revList[] = $matches[1];
280 '2.1.0', '2.2.beta2', '2.2.beta1', '2.2.alpha1', */
282 /* '2.2.alpha3', '2.2.0', '2.2.2', '2.1.alpha1', '2.1.3'); */
285 usort($revList, 'version_compare');
289 static function getRevisionPart($rev, $index = 1) {
290 $revPattern = '/^((\d{1,2})\.\d{1,2})\.(\d{1,2}|\w{4,7})?$/i';
291 preg_match($revPattern, $rev, $matches);
293 return array_key_exists($index, $matches) ?
$matches[$index] : NULL;
296 function processLocales($tplFile, $rev) {
297 $smarty = CRM_Core_Smarty
::singleton();
298 $smarty->assign('domainID', CRM_Core_Config
::domainID());
300 $this->source($smarty->fetch($tplFile), TRUE);
302 if ($this->multilingual
) {
303 CRM_Core_I18n_Schema
::rebuildMultilingualSchema($this->locales
, $rev);
305 return $this->multilingual
;
308 function setSchemaStructureTables($rev) {
309 if ($this->multilingual
) {
310 CRM_Core_I18n_Schema
::schemaStructureTables($rev, TRUE);
314 function processSQL($rev) {
315 $sqlFile = implode(DIRECTORY_SEPARATOR
,
317 dirname(__FILE__
), 'Incremental',
318 'sql', $rev . '.mysql',
321 $tplFile = "$sqlFile.tpl";
323 if (file_exists($tplFile)) {
324 $this->processLocales($tplFile, $rev);
327 if (!file_exists($sqlFile)) {
328 CRM_Core_Error
::fatal("sqlfile - $rev.mysql not found.");
330 $this->source($sqlFile);
335 * Determine the start and end version of the upgrade process
337 * @return array(0=>$currentVer, 1=>$latestVer)
339 function getUpgradeVersions() {
340 $latestVer = CRM_Utils_System
::version();
341 $currentVer = CRM_Core_BAO_Domain
::version(true);
343 CRM_Core_Error
::fatal(ts('Version information missing in civicrm database.'));
345 elseif (stripos($currentVer, 'upgrade')) {
346 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.'));
349 CRM_Core_Error
::fatal(ts('Version information missing in civicrm codebase.'));
352 // hack to make past ver compatible /w new incremental upgrade process
355 '2.2' => '2.2.alpha1',
356 '2.2.alph' => '2.2.alpha3',
357 // since 3.1.1 had domain.version set as 3.1.0
360 if (isset($convertVer[$currentVer])) {
361 $currentVer = $convertVer[$currentVer];
364 return array($currentVer, $latestVer);
368 * Determine if $currentVer can be upgraded to $latestVer
370 * @return mixed, a string error message or boolean 'false' if OK
372 function checkUpgradeableVersion($currentVer, $latestVer) {
374 // since version is suppose to be in valid format at this point, especially after conversion ($convertVer),
375 // lets do a pattern check -
376 if (!CRM_Utils_System
::isVersionFormatValid($currentVer)) {
377 $error = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
379 elseif (version_compare($currentVer, $latestVer) > 0) {
380 // DB version number is higher than codebase being upgraded to. This is unexpected condition-fatal error.
381 $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.',
382 array(1 => $currentVer, 2 => $latestVer, 3 => $dbToolsLink)
385 elseif (version_compare($currentVer, $latestVer) == 0) {
386 $error = ts('Your database has already been upgraded to CiviCRM %1',
387 array(1 => $latestVer)
391 $phpVersion = phpversion();
392 $minPhpVersion = '5.3.3';
393 if (version_compare($phpVersion, $minPhpVersion) < 0) {
394 $error = ts('CiviCRM %3 requires PHP version %1 (or newer), but the current system uses %2 ',
402 // check for mysql trigger privileges
403 if (!CRM_Core_DAO
::checkTriggerViewPermission(FALSE, TRUE)) {
404 $error = ts('CiviCRM %1 requires MySQL trigger privileges.',
405 array(1 => $latestVer));
408 if (CRM_Core_DAO
::getGlobalSetting('thread_stack', 0) < (1024*self
::MINIMUM_THREAD_STACK
)) {
409 $error = ts('CiviCRM %1 requires MySQL thread stack >= %2k', array(
411 2 => self
::MINIMUM_THREAD_STACK
419 * Determine if $currentver already matches $latestVer
421 * @return mixed, a string error message or boolean 'false' if OK
423 function checkCurrentVersion($currentVer, $latestVer) {
426 // since version is suppose to be in valid format at this point, especially after conversion ($convertVer),
427 // lets do a pattern check -
428 if (!CRM_Utils_System
::isVersionFormatValid($currentVer)) {
429 $error = ts('Database is marked with invalid version format. You may want to investigate this before you proceed further.');
431 elseif (version_compare($currentVer, $latestVer) != 0) {
432 $error = ts('Your database is not configured for version %1',
433 array(1 => $latestVer)
440 * Fill the queue with upgrade tasks
442 * @param $currentVer string, the original revision
443 * @param $latestVer string, the target (final) revision
444 * @param $postUpgradeMessageFile string, path of a modifiable file which lists the post-upgrade messages
448 static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFile) {
449 $upgrade = new CRM_Upgrade_Form();
451 // hack to make 4.0.x (D7,J1.6) codebase go through 3.4.x (d6, J1.5) upgrade files,
452 // since schema wise they are same
453 if (CRM_Upgrade_Form
::getRevisionPart($currentVer) == '4.0') {
454 $currentVer = str_replace('4.0.', '3.4.', $currentVer);
457 // Ensure that queue can be created
458 if (!CRM_Queue_BAO_QueueItem
::findCreateTable()) {
459 CRM_Core_Error
::fatal(ts('Failed to find or create queueing table'));
461 $queue = CRM_Queue_Service
::singleton()->create(array(
462 'name' => self
::QUEUE_NAME
,
467 $revisions = $upgrade->getRevisionSequence();
468 foreach ($revisions as $rev) {
469 // proceed only if $currentVer < $rev
470 if (version_compare($currentVer, $rev) < 0) {
471 $beginTask = new CRM_Queue_Task(
473 array('CRM_Upgrade_Form', 'doIncrementalUpgradeStart'),
476 "Begin Upgrade to $rev"
478 $queue->createItem($beginTask);
480 $task = new CRM_Queue_Task(
482 array('CRM_Upgrade_Form', 'doIncrementalUpgradeStep'),
484 array($rev, $currentVer, $latestVer, $postUpgradeMessageFile),
487 $queue->createItem($task);
489 $task = new CRM_Queue_Task(
491 array('CRM_Upgrade_Form', 'doIncrementalUpgradeFinish'),
493 array($rev, $currentVer, $latestVer, $postUpgradeMessageFile),
494 "Finish Upgrade DB to $rev"
496 $queue->createItem($task);
504 * Perform an incremental version update
506 * @param $rev string, the target (intermediate) revision e.g '3.2.alpha1'
507 * @param $currentVer string, the original revision
508 * @param $latestVer string, the target (final) revision
509 * @param $postUpgradeMessageFile string, path of a modifiable file which lists the post-upgrade messages
511 static function doIncrementalUpgradeStart(CRM_Queue_TaskContext
$ctx, $rev) {
512 $upgrade = new CRM_Upgrade_Form();
514 // as soon as we start doing anything we append ".upgrade" to version.
515 // this also helps detect any partial upgrade issues
516 $upgrade->setVersion($rev . '.upgrade');
522 * Perform an incremental version update
524 * @param $rev string, the target (intermediate) revision e.g '3.2.alpha1'
525 * @param $currentVer string, the original revision
526 * @param $latestVer string, the target (final) revision
527 * @param $postUpgradeMessageFile string, path of a modifiable file which lists the post-upgrade messages
529 static function doIncrementalUpgradeStep(CRM_Queue_TaskContext
$ctx, $rev, $currentVer, $latestVer, $postUpgradeMessageFile) {
530 $upgrade = new CRM_Upgrade_Form();
532 $phpFunctionName = 'upgrade_' . str_replace('.', '_', $rev);
534 // follow old upgrade process for all version
536 if (version_compare($rev, '3.2.alpha1') < 0) {
537 if (is_callable(array(
538 'CRM_Upgrade_Incremental_Legacy', $phpFunctionName))) {
539 call_user_func(array('CRM_Upgrade_Incremental_Legacy', $phpFunctionName), $rev);
542 $upgrade->processSQL($rev);
546 // new upgrade process from version
548 $versionObject = $upgrade->incrementalPhpObject($rev);
550 // pre-db check for major release.
551 if ($upgrade->checkVersionRelease($rev, 'alpha1')) {
552 if (!(is_callable(array(
553 $versionObject, 'verifyPreDBstate')))) {
554 CRM_Core_Error
::fatal("verifyPreDBstate method was not found for $rev");
558 if (!($versionObject->verifyPreDBstate($error))) {
559 if (!isset($error)) {
560 $error = "post-condition failed for current upgrade for $rev";
562 CRM_Core_Error
::fatal($error);
567 $upgrade->setSchemaStructureTables($rev);
569 if (is_callable(array(
570 $versionObject, $phpFunctionName))) {
571 $versionObject->$phpFunctionName($rev);
574 $upgrade->processSQL($rev);
577 // set post-upgrade-message if any
578 if (is_callable(array(
579 $versionObject, 'setPostUpgradeMessage'))) {
580 $postUpgradeMessage = file_get_contents($postUpgradeMessageFile);
581 $versionObject->setPostUpgradeMessage($postUpgradeMessage, $rev);
582 file_put_contents($postUpgradeMessageFile, $postUpgradeMessage);
584 $postUpgradeMessage = file_get_contents($postUpgradeMessageFile);
585 CRM_Upgrade_Incremental_Legacy
::setPostUpgradeMessage($postUpgradeMessage, $rev);
586 file_put_contents($postUpgradeMessageFile, $postUpgradeMessage);
594 * Perform an incremental version update
596 * @param $rev string, the target (intermediate) revision e.g '3.2.alpha1'
597 * @param $currentVer string, the original revision
598 * @param $latestVer string, the target (final) revision
599 * @param $postUpgradeMessageFile string, path of a modifiable file which lists the post-upgrade messages
601 static function doIncrementalUpgradeFinish(CRM_Queue_TaskContext
$ctx, $rev, $currentVer, $latestVer, $postUpgradeMessageFile) {
602 $upgrade = new CRM_Upgrade_Form();
603 $upgrade->setVersion($rev);
604 CRM_Utils_System
::flushCache();
606 $config = CRM_Core_Config
::singleton();
607 $config->userSystem
->flush();
611 static function doFinish() {
612 $upgrade = new CRM_Upgrade_Form();
613 list($ignore, $latestVer) = $upgrade->getUpgradeVersions();
614 // Seems extraneous in context, but we'll preserve old behavior
615 $upgrade->setVersion($latestVer);
617 // lets rebuild the config array in case we've made a few changes in the
619 // this also helps us always store the latest version of civi in the DB
621 CRM_Core_BAO_ConfigSetting
::add($params);
623 // CRM-12804 comment-51411 : add any missing settings
624 // at the end of upgrade
625 CRM_Core_BAO_Setting
::updateSettingsFromMetaData();
627 // cleanup caches CRM-8739
628 $config = CRM_Core_Config
::singleton();
629 $config->cleanupCaches(1);
631 // Rebuild all triggers and re-enable logging if needed
632 $logging = new CRM_Logging_Schema();
633 $logging->fixSchemaDifferences();
637 * Compute any messages which should be displayed before upgrade
638 * by calling the 'setPreUpgradeMessage' on each incremental upgrade
641 * @param $preUpgradeMessage string, alterable
643 function setPreUpgradeMessage(&$preUpgradeMessage, $currentVer, $latestVer) {
644 CRM_Upgrade_Incremental_Legacy
::setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer);
646 // Scan through all php files and see if any file is interested in setting pre-upgrade-message
647 // based on $currentVer, $latestVer.
648 // Please note, at this point upgrade hasn't started executing queries.
649 $revisions = $this->getRevisionSequence();
650 foreach ($revisions as $rev) {
651 if (version_compare($currentVer, $rev) < 0 &&
652 version_compare($rev, '3.2.alpha1') > 0
654 $versionObject = $this->incrementalPhpObject($rev);
655 if (is_callable(array(
656 $versionObject, 'setPreUpgradeMessage'))) {
657 $versionObject->setPreUpgradeMessage($preUpgradeMessage, $rev, $currentVer);