From cf8be4fb4a2b383096568a9299309f008efce4d1 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 15:37:54 -0800 Subject: [PATCH 01/15] CRM_Upgrade_Form::preProcess - Remove unused code. --- CRM/Upgrade/Form.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index e8519a0e3fcd..99e16e9cdf41 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -197,14 +197,9 @@ public function source($fileName, $isQueryString = FALSE) { } public function preProcess() { - CRM_Utils_System::setTitle($this->getTitle()); - if (!$this->verifyPreDBState($errorMessage)) { - if (!isset($errorMessage)) { - $errorMessage = 'pre-condition failed for current upgrade step'; - } - CRM_Core_Error::fatal($errorMessage); - } - $this->assign('recentlyViewed', FALSE); + // This function should be deleted, but I want to ensure it's not actually used, and + // deleting it would pass requests up to parent::preProcess(). + CRM_Core_Error::fatal(sprintf("The function %s::%s should not be called.", __CLASS__, __FUNCTION__)); } public function buildQuickForm() { From 6d98879a59b051f82ed5ceb353ffabac87a5819d Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 19:03:12 -0800 Subject: [PATCH 02/15] CRM-16860 - CRM_Upgrade_Incremental - Extract RevisionBase class Move helper functions that are specific to revision-based approach: * buildQueue * doIncrementalUpgradeStart * doIncrementalUpgradeStep * doIncrementalUpgradeFinish --- CRM/Upgrade/Form.php | 137 +------------- CRM/Upgrade/Incremental/Base.php | 25 +-- CRM/Upgrade/Incremental/RevisionBase.php | 221 ++++++++++++++++++++++ CRM/Upgrade/Incremental/php/FourFive.php | 31 ++- CRM/Upgrade/Incremental/php/FourFour.php | 20 +- CRM/Upgrade/Incremental/php/FourOne.php | 20 +- CRM/Upgrade/Incremental/php/FourSeven.php | 18 +- CRM/Upgrade/Incremental/php/FourSix.php | 34 +++- CRM/Upgrade/Incremental/php/FourThree.php | 28 ++- CRM/Upgrade/Incremental/php/FourTwo.php | 27 ++- 10 files changed, 400 insertions(+), 161 deletions(-) create mode 100644 CRM/Upgrade/Incremental/RevisionBase.php diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 99e16e9cdf41..8bae1ff311ac 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -556,143 +556,16 @@ public static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFi 'reset' => TRUE, )); - $revisions = $upgrade->getRevisionSequence(); - foreach ($revisions as $rev) { - // proceed only if $currentVer < $rev - if (version_compare($currentVer, $rev) < 0) { - $beginTask = new CRM_Queue_Task( - // callback - array('CRM_Upgrade_Form', 'doIncrementalUpgradeStart'), - // arguments - array($rev), - "Begin Upgrade to $rev" - ); - $queue->createItem($beginTask); - - $task = new CRM_Queue_Task( - // callback - array('CRM_Upgrade_Form', 'doIncrementalUpgradeStep'), - // arguments - array($rev, $currentVer, $latestVer, $postUpgradeMessageFile), - "Upgrade DB to $rev" - ); - $queue->createItem($task); - - $task = new CRM_Queue_Task( - // callback - array('CRM_Upgrade_Form', 'doIncrementalUpgradeFinish'), - // arguments - array($rev, $currentVer, $latestVer, $postUpgradeMessageFile), - "Finish Upgrade DB to $rev" - ); - $queue->createItem($task); - } + foreach (array('FourOne', 'FourTwo', 'FourThree', 'FourFour', 'FourFive', 'FourSix', 'FourSeven') as $major) { + $class = "CRM_Upgrade_Incremental_php_$major"; + /** @var CRM_Upgrade_Incremental_RevisionBase $obj */ + $obj = new $class(); + $obj->buildQueue($queue, $postUpgradeMessageFile, $currentVer, $latestVer); } return $queue; } - /** - * Perform an incremental version update. - * - * @param CRM_Queue_TaskContext $ctx - * @param string $rev - * the target (intermediate) revision e.g '3.2.alpha1'. - * - * @return bool - */ - public static function doIncrementalUpgradeStart(CRM_Queue_TaskContext $ctx, $rev) { - $upgrade = new CRM_Upgrade_Form(); - - // as soon as we start doing anything we append ".upgrade" to version. - // this also helps detect any partial upgrade issues - $upgrade->setVersion($rev . '.upgrade'); - - return TRUE; - } - - /** - * Perform an incremental version update. - * - * @param CRM_Queue_TaskContext $ctx - * @param string $rev - * the target (intermediate) revision e.g '3.2.alpha1'. - * @param string $originalVer - * the original revision. - * @param string $latestVer - * the target (final) revision. - * @param string $postUpgradeMessageFile - * path of a modifiable file which lists the post-upgrade messages. - * - * @return bool - */ - public static function doIncrementalUpgradeStep(CRM_Queue_TaskContext $ctx, $rev, $originalVer, $latestVer, $postUpgradeMessageFile) { - $upgrade = new CRM_Upgrade_Form(); - - $phpFunctionName = 'upgrade_' . str_replace('.', '_', $rev); - - $versionObject = $upgrade->incrementalPhpObject($rev); - - // pre-db check for major release. - if ($upgrade->checkVersionRelease($rev, 'alpha1')) { - if (!(is_callable(array($versionObject, 'verifyPreDBstate')))) { - CRM_Core_Error::fatal("verifyPreDBstate method was not found for $rev"); - } - - $error = NULL; - if (!($versionObject->verifyPreDBstate($error))) { - if (!isset($error)) { - $error = "post-condition failed for current upgrade for $rev"; - } - CRM_Core_Error::fatal($error); - } - - } - - $upgrade->setSchemaStructureTables($rev); - - if (is_callable(array($versionObject, $phpFunctionName))) { - $versionObject->$phpFunctionName($rev, $originalVer, $latestVer); - } - else { - $upgrade->processSQL($rev); - } - - // set post-upgrade-message if any - if (is_callable(array($versionObject, 'setPostUpgradeMessage'))) { - $postUpgradeMessage = file_get_contents($postUpgradeMessageFile); - $versionObject->setPostUpgradeMessage($postUpgradeMessage, $rev); - file_put_contents($postUpgradeMessageFile, $postUpgradeMessage); - } - - return TRUE; - } - - /** - * Perform an incremental version update. - * - * @param CRM_Queue_TaskContext $ctx - * @param string $rev - * the target (intermediate) revision e.g '3.2.alpha1'. - * @param string $currentVer - * the original revision. - * @param string $latestVer - * the target (final) revision. - * @param string $postUpgradeMessageFile - * path of a modifiable file which lists the post-upgrade messages. - * - * @return bool - */ - public static function doIncrementalUpgradeFinish(CRM_Queue_TaskContext $ctx, $rev, $currentVer, $latestVer, $postUpgradeMessageFile) { - $upgrade = new CRM_Upgrade_Form(); - $upgrade->setVersion($rev); - CRM_Utils_System::flushCache(); - - $config = CRM_Core_Config::singleton(); - $config->userSystem->flush(); - return TRUE; - } - public static function doFinish() { $upgrade = new CRM_Upgrade_Form(); list($ignore, $latestVer) = $upgrade->getUpgradeVersions(); diff --git a/CRM/Upgrade/Incremental/Base.php b/CRM/Upgrade/Incremental/Base.php index 858e1a5adcc6..15fdea5514a8 100644 --- a/CRM/Upgrade/Incremental/Base.php +++ b/CRM/Upgrade/Incremental/Base.php @@ -28,19 +28,11 @@ /** * Base class for incremental upgrades */ -class CRM_Upgrade_Incremental_Base { +abstract class CRM_Upgrade_Incremental_Base { const BATCH_SIZE = 5000; - /** - * Verify DB state. - * - * @param $errors - * - * @return bool - */ - public function verifyPreDBstate(&$errors) { - return TRUE; - } + public abstract function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $startVer, $endVer); + /** * Compute any messages which should be displayed before upgrade. @@ -56,17 +48,6 @@ public function verifyPreDBstate(&$errors) { public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) { } - /** - * Compute any messages which should be displayed after upgrade. - * - * @param string $postUpgradeMessage - * alterable. - * @param string $rev - * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs. - */ - public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { - } - /** * (Queue Task Callback) * diff --git a/CRM/Upgrade/Incremental/RevisionBase.php b/CRM/Upgrade/Incremental/RevisionBase.php new file mode 100644 index 000000000000..31e42f3a3303 --- /dev/null +++ b/CRM/Upgrade/Incremental/RevisionBase.php @@ -0,0 +1,221 @@ += v4.7, this was flattened out so that upgrades are handled by + * named scripts (with no particular meaning to major/minor versions). + * This design reduces maintenance arising from code-review/forks/merges. + * + * The `RevisionBase` class provides an adapter: old revision-based upgrade + * classes look like named migration scripts. + * + * @deprecated + */ +abstract class CRM_Upgrade_Incremental_RevisionBase extends CRM_Upgrade_Incremental_Base { + + /** + * Get a list of incremental revisions. + * + * @return array + */ + public abstract function getRevisions(); + + /** + * Verify DB state. + * + * @param $errors + * + * @return bool + */ + public function verifyPreDBstate(&$errors) { + return TRUE; + } + + /** + * Compute any messages which should be displayed after upgrade. + * + * @param string $postUpgradeMessage + * alterable. + * @param string $rev + * an intermediate version; note that setPostUpgradeMessage is called repeatedly with different $revs. + */ + public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { + } + + // -------------------------------------------- + + /** + * @param \CRM_Queue_Queue $queue + * @param $postUpgradeMessageFile + * @param $startVer + * @param $endVer + */ + public function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $startVer, $endVer) { + foreach ($this->getRevisions() as $rev) { + // proceed only if $currentVer < $rev + if (version_compare($startVer, $rev) < 0) { + $beginTask = new CRM_Queue_Task( + // callback + array('CRM_Upgrade_Incremental_RevisionBase', 'doIncrementalUpgradeStart'), + // arguments + array($rev), + "Begin Upgrade to $rev" + ); + $queue->createItem($beginTask); + + $task = new CRM_Queue_Task( + // callback + array('CRM_Upgrade_Incremental_RevisionBase', 'doIncrementalUpgradeStep'), + // arguments + array($rev, $startVer, $endVer, $postUpgradeMessageFile), + "Upgrade DB to $rev" + ); + $queue->createItem($task); + + $task = new CRM_Queue_Task( + // callback + array('CRM_Upgrade_Incremental_RevisionBase', 'doIncrementalUpgradeFinish'), + // arguments + array($rev, $startVer, $endVer, $postUpgradeMessageFile), + "Finish Upgrade DB to $rev" + ); + $queue->createItem($task); + } + } + } + + /** + * Perform an incremental version update. + * + * @param CRM_Queue_TaskContext $ctx + * @param string $rev + * the target (intermediate) revision e.g '3.2.alpha1'. + * + * @return bool + */ + public static function doIncrementalUpgradeStart(CRM_Queue_TaskContext $ctx, $rev) { + $upgrade = new CRM_Upgrade_Form(); + + // as soon as we start doing anything we append ".upgrade" to version. + // this also helps detect any partial upgrade issues + $upgrade->setVersion($rev . '.upgrade'); + + return TRUE; + } + + /** + * Perform an incremental version update. + * + * @param CRM_Queue_TaskContext $ctx + * @param string $rev + * the target (intermediate) revision e.g '3.2.alpha1'. + * @param string $originalVer + * the original revision. + * @param string $latestVer + * the target (final) revision. + * @param string $postUpgradeMessageFile + * path of a modifiable file which lists the post-upgrade messages. + * + * @return bool + */ + public static function doIncrementalUpgradeStep(CRM_Queue_TaskContext $ctx, $rev, $originalVer, $latestVer, $postUpgradeMessageFile) { + $upgrade = new CRM_Upgrade_Form(); + + $phpFunctionName = 'upgrade_' . str_replace('.', '_', $rev); + + $versionObject = $upgrade->incrementalPhpObject($rev); + + // pre-db check for major release. + if ($upgrade->checkVersionRelease($rev, 'alpha1')) { + if (!(is_callable(array($versionObject, 'verifyPreDBstate')))) { + CRM_Core_Error::fatal("verifyPreDBstate method was not found for $rev"); + } + + $error = NULL; + if (!($versionObject->verifyPreDBstate($error))) { + if (!isset($error)) { + $error = "post-condition failed for current upgrade for $rev"; + } + CRM_Core_Error::fatal($error); + } + + } + + $upgrade->setSchemaStructureTables($rev); + + if (is_callable(array($versionObject, $phpFunctionName))) { + $versionObject->$phpFunctionName($rev, $originalVer, $latestVer); + } + else { + $upgrade->processSQL($rev); + } + + // set post-upgrade-message if any + if (is_callable(array($versionObject, 'setPostUpgradeMessage'))) { + $postUpgradeMessage = file_get_contents($postUpgradeMessageFile); + $versionObject->setPostUpgradeMessage($postUpgradeMessage, $rev); + file_put_contents($postUpgradeMessageFile, $postUpgradeMessage); + } + + return TRUE; + } + + /** + * Perform an incremental version update. + * + * @param CRM_Queue_TaskContext $ctx + * @param string $rev + * the target (intermediate) revision e.g '3.2.alpha1'. + * @param string $currentVer + * the original revision. + * @param string $latestVer + * the target (final) revision. + * @param string $postUpgradeMessageFile + * path of a modifiable file which lists the post-upgrade messages. + * + * @return bool + */ + public static function doIncrementalUpgradeFinish(CRM_Queue_TaskContext $ctx, $rev, $currentVer, $latestVer, $postUpgradeMessageFile) { + $upgrade = new CRM_Upgrade_Form(); + $upgrade->setVersion($rev); + CRM_Utils_System::flushCache(); + + $config = CRM_Core_Config::singleton(); + $config->userSystem->flush(); + return TRUE; + } + +} diff --git a/CRM/Upgrade/Incremental/php/FourFive.php b/CRM/Upgrade/Incremental/php/FourFive.php index d96b88638538..8d6f2b8204ff 100755 --- a/CRM/Upgrade/Incremental/php/FourFive.php +++ b/CRM/Upgrade/Incremental/php/FourFive.php @@ -28,7 +28,36 @@ /** * Upgrade logic for 4.5 */ -class CRM_Upgrade_Incremental_php_FourFive extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourFive extends CRM_Upgrade_Incremental_RevisionBase { + + /** + * @return array + */ + public function getRevisions() { + return array( + "4.5.alpha1", + "4.5.alpha2", + "4.5.beta1", + "4.5.beta2", + "4.5.beta3", + "4.5.beta4", + "4.5.beta5", + "4.5.beta6", + "4.5.beta7", + "4.5.beta8", + "4.5.beta9", + "4.5.0", + "4.5.1", + "4.5.2", + "4.5.3", + "4.5.4", + "4.5.5", + "4.5.6", + "4.5.7", + "4.5.8", + "4.5.9", + ); + } /** * Compute any messages which should be displayed after upgrade. diff --git a/CRM/Upgrade/Incremental/php/FourFour.php b/CRM/Upgrade/Incremental/php/FourFour.php index 73399f6e67c6..345dd25e36e8 100644 --- a/CRM/Upgrade/Incremental/php/FourFour.php +++ b/CRM/Upgrade/Incremental/php/FourFour.php @@ -28,9 +28,27 @@ /** * Upgrade logic for 4.4 */ -class CRM_Upgrade_Incremental_php_FourFour extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourFour extends CRM_Upgrade_Incremental_RevisionBase { const MAX_WORD_REPLACEMENT_SIZE = 255; + /** + * @return array + */ + public function getRevisions() { + return array( + "4.4.alpha1", + "4.4.beta1", + "4.4.0", + "4.4.1", + "4.4.2", + "4.4.3", + "4.4.4", + "4.4.5", + "4.4.6", + "4.4.7", + ); + } + /** * Compute any messages which should be displayed beforeupgrade. * diff --git a/CRM/Upgrade/Incremental/php/FourOne.php b/CRM/Upgrade/Incremental/php/FourOne.php index ae926609004b..278324679d32 100644 --- a/CRM/Upgrade/Incremental/php/FourOne.php +++ b/CRM/Upgrade/Incremental/php/FourOne.php @@ -32,10 +32,28 @@ * $Id$ * */ -class CRM_Upgrade_Incremental_php_FourOne { +class CRM_Upgrade_Incremental_php_FourOne extends CRM_Upgrade_Incremental_RevisionBase { + // This was changed in 4.3 so we define it locally for compatibility with older dbs const NAVIGATION_NAME = "Navigation Menu"; + /** + * @return array + */ + public function getRevisions() { + return array( + "4.1.alpha1", + "4.1.alpha2", + "4.1.beta1", + "4.1.beta2", + "4.1.beta3", + "4.1.0", + "4.1.1", + "4.1.2", + "4.1.3", + ); + } + /** * @param $errors * diff --git a/CRM/Upgrade/Incremental/php/FourSeven.php b/CRM/Upgrade/Incremental/php/FourSeven.php index b5aba5cb832f..4da399f51afc 100644 --- a/CRM/Upgrade/Incremental/php/FourSeven.php +++ b/CRM/Upgrade/Incremental/php/FourSeven.php @@ -27,7 +27,23 @@ /** * Upgrade logic for 4.7 */ -class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_RevisionBase { + + /** + * @return array + */ + public function getRevisions() { + return array( + "4.7.alpha1", + "4.7.alpha2", + "4.7.alpha3", + "4.7.alpha4", + "4.7.alpha5", + "4.7.beta1", + "4.7.beta2", + "4.7.beta3", + ); + } /** * Compute any messages which should be displayed beforeupgrade. diff --git a/CRM/Upgrade/Incremental/php/FourSix.php b/CRM/Upgrade/Incremental/php/FourSix.php index de085ac4f080..29d7953fc745 100644 --- a/CRM/Upgrade/Incremental/php/FourSix.php +++ b/CRM/Upgrade/Incremental/php/FourSix.php @@ -28,7 +28,39 @@ /** * Upgrade logic for 4.6 */ -class CRM_Upgrade_Incremental_php_FourSix extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourSix extends CRM_Upgrade_Incremental_RevisionBase { + + /** + * @return array + */ + public function getRevisions() { + return array( + "4.6.alpha1", + "4.6.alpha2", + "4.6.alpha3", + "4.6.alpha4", + "4.6.alpha5", + "4.6.alpha6", + "4.6.alpha7", + "4.6.beta1", + "4.6.beta2", + "4.6.beta3", + "4.6.beta4", + "4.6.beta5", + "4.6.0", + "4.6.1", + "4.6.2", + "4.6.3", + "4.6.4", + "4.6.5", + "4.6.6", + "4.6.7", + "4.6.8", + "4.6.9", + "4.6.10", + "4.6.11", + ); + } /** * Compute any messages which should be displayed after upgrade. diff --git a/CRM/Upgrade/Incremental/php/FourThree.php b/CRM/Upgrade/Incremental/php/FourThree.php index 4901e78420a6..906afd25bac6 100644 --- a/CRM/Upgrade/Incremental/php/FourThree.php +++ b/CRM/Upgrade/Incremental/php/FourThree.php @@ -28,7 +28,33 @@ /** * Upgrade logic for 4.3 */ -class CRM_Upgrade_Incremental_php_FourThree extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourThree extends CRM_Upgrade_Incremental_RevisionBase { + + /** + * @return array + */ + public function getRevisions() { + return array( + "4.3.alpha1", + "4.3.alpha2", + "4.3.alpha3", + "4.3.beta1", + "4.3.beta2", + "4.3.beta3", + "4.3.beta4", + "4.3.beta5", + "4.3.0", + "4.3.1", + "4.3.2", + "4.3.3", + "4.3.4", + "4.3.5", + "4.3.6", + "4.3.7", + "4.3.8", + "4.3.9", + ); + } /** * Compute any messages which should be displayed beforeupgrade. diff --git a/CRM/Upgrade/Incremental/php/FourTwo.php b/CRM/Upgrade/Incremental/php/FourTwo.php index 021f53977368..e81e84a61438 100644 --- a/CRM/Upgrade/Incremental/php/FourTwo.php +++ b/CRM/Upgrade/Incremental/php/FourTwo.php @@ -28,10 +28,35 @@ /** * Upgrade logic for 4.2 */ -class CRM_Upgrade_Incremental_php_FourTwo extends CRM_Upgrade_Incremental_Base { +class CRM_Upgrade_Incremental_php_FourTwo extends CRM_Upgrade_Incremental_RevisionBase { const SETTINGS_SNIPPET_PATTERN = '/CRM_Core_ClassLoader::singleton\(\)-\>register/'; const SETTINGS_SNIPPET = "\nrequire_once 'CRM/Core/ClassLoader.php';\nCRM_Core_ClassLoader::singleton()->register();\n"; + /** + * @return array + */ + public function getRevisions() { + return array( + "4.2.alpha1", + "4.2.alpha2", + "4.2.alpha3", + "4.2.beta1", + "4.2.beta2", + "4.2.beta3", + "4.2.beta5", + "4.2.beta6", + "4.2.0", + "4.2.1", + "4.2.2", + "4.2.3", + "4.2.5", + "4.2.6", + "4.2.7", + "4.2.8", + "4.2.9", + ); + } + /** * Compute any messages which should be displayed beforeupgrade. * From 8d7826984e5bc6eff387dff4c13d13d7f95abb79 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 19:53:46 -0800 Subject: [PATCH 03/15] CRM-16860 - Change setPreUpgradeMessage to createPreUpgradeMessage This is a cleaner high-level contract (less side-effecty, more accurate name, not tied to "$rev" concept). --- CRM/Upgrade/Form.php | 47 +++++++++++++++++------- CRM/Upgrade/Headless.php | 3 +- CRM/Upgrade/Incremental/Base.php | 24 ++++++------ CRM/Upgrade/Incremental/RevisionBase.php | 34 +++++++++++++++++ CRM/Upgrade/Page/Upgrade.php | 3 +- 5 files changed, 80 insertions(+), 31 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 8bae1ff311ac..bc1220de6296 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -128,6 +128,27 @@ public function __construct( parent::__construct($state, $action, $method, $name); } + /** + * @return array + */ + protected static function getUpgradeObjects() { + $majors = array( + 'FourOne', + 'FourTwo', + 'FourThree', + 'FourFour', + 'FourFive', + 'FourSix', + 'FourSeven', + ); + $upgradeObjects = array(); + foreach ($majors as $major) { + $class = "CRM_Upgrade_Incremental_php_$major"; + $upgradeObjects[] = new $class(); + } + return $upgradeObjects; + } + /** * @param $version * @@ -556,10 +577,9 @@ public static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFi 'reset' => TRUE, )); - foreach (array('FourOne', 'FourTwo', 'FourThree', 'FourFour', 'FourFive', 'FourSix', 'FourSeven') as $major) { - $class = "CRM_Upgrade_Incremental_php_$major"; + $upgradeObjects = self::getUpgradeObjects(); + foreach ($upgradeObjects as $obj) { /** @var CRM_Upgrade_Incremental_RevisionBase $obj */ - $obj = new $class(); $obj->buildQueue($queue, $postUpgradeMessageFile, $currentVer, $latestVer); } @@ -592,12 +612,13 @@ public static function doFinish() { * by calling the 'setPreUpgradeMessage' on each incremental upgrade * object. * - * @param string $preUpgradeMessage - * alterable. * @param $currentVer * @param $latestVer + * @return string */ - public function setPreUpgradeMessage(&$preUpgradeMessage, $currentVer, $latestVer) { + public function createPreUpgradeMessage($currentVer, $latestVer) { + $preUpgradeMessage = NULL; + // check for changed message templates CRM_Upgrade_Incremental_General::checkMessageTemplate($preUpgradeMessage, $latestVer, $currentVer); // set global messages @@ -606,15 +627,13 @@ public function setPreUpgradeMessage(&$preUpgradeMessage, $currentVer, $latestVe // Scan through all php files and see if any file is interested in setting pre-upgrade-message // based on $currentVer, $latestVer. // Please note, at this point upgrade hasn't started executing queries. - $revisions = $this->getRevisionSequence(); - foreach ($revisions as $rev) { - if (version_compare($currentVer, $rev) < 0) { - $versionObject = $this->incrementalPhpObject($rev); - if (is_callable(array($versionObject, 'setPreUpgradeMessage'))) { - $versionObject->setPreUpgradeMessage($preUpgradeMessage, $rev, $currentVer); - } - } + $upgradeObjects = self::getUpgradeObjects(); + foreach ($upgradeObjects as $upgradeObject) { + /** @var CRM_Upgrade_Incremental_Base $upgradeObject */ + $preUpgradeMessage .= $upgradeObject->createPreUpgradeMessage($currentVer, $latestVer); } + + return $preUpgradeMessage; } } diff --git a/CRM/Upgrade/Headless.php b/CRM/Upgrade/Headless.php index 953aa9505979..1ba134af1ba8 100644 --- a/CRM/Upgrade/Headless.php +++ b/CRM/Upgrade/Headless.php @@ -56,8 +56,7 @@ public function run($enablePrint = TRUE) { CRM_Core_DAO::dropTriggers(); // CRM-11156 - $preUpgradeMessage = NULL; - $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer); + $preUpgradeMessage = $upgrade->createPreUpgradeMessage($currentVer, $latestVer); $postUpgradeMessageFile = CRM_Utils_File::tempnam('civicrm-post-upgrade'); $queueRunner = new CRM_Queue_Runner(array( diff --git a/CRM/Upgrade/Incremental/Base.php b/CRM/Upgrade/Incremental/Base.php index 15fdea5514a8..5cfabc3ffa60 100644 --- a/CRM/Upgrade/Incremental/Base.php +++ b/CRM/Upgrade/Incremental/Base.php @@ -31,22 +31,20 @@ abstract class CRM_Upgrade_Incremental_Base { const BATCH_SIZE = 5000; - public abstract function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $startVer, $endVer); - + /** + * @param string $startVer + * @param string $endVer + * @return string + */ + public abstract function createPreUpgradeMessage($startVer, $endVer); /** - * Compute any messages which should be displayed before upgrade. - * - * Note: This function is called iteratively for each upcoming - * revision to the database. - * - * @param $preUpgradeMessage - * @param string $rev - * a version number, e.g. '4.8.alpha1', '4.8.beta3', '4.8.0'. - * @param null $currentVer + * @param \CRM_Queue_Queue $queue + * @param string $postUpgradeMessageFile + * @param string $startVer + * @param string $endVer */ - public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) { - } + public abstract function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $startVer, $endVer); /** * (Queue Task Callback) diff --git a/CRM/Upgrade/Incremental/RevisionBase.php b/CRM/Upgrade/Incremental/RevisionBase.php index 31e42f3a3303..744fc8d79800 100644 --- a/CRM/Upgrade/Incremental/RevisionBase.php +++ b/CRM/Upgrade/Incremental/RevisionBase.php @@ -64,6 +64,20 @@ public function verifyPreDBstate(&$errors) { return TRUE; } + /** + * Compute any messages which should be displayed before upgrade. + * + * Note: This function is called iteratively for each upcoming + * revision to the database. + * + * @param $preUpgradeMessage + * @param string $rev + * a version number, e.g. '4.8.alpha1', '4.8.beta3', '4.8.0'. + * @param null $currentVer + */ + public function setPreUpgradeMessage(&$preUpgradeMessage, $rev, $currentVer = NULL) { + } + /** * Compute any messages which should be displayed after upgrade. * @@ -78,6 +92,26 @@ public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { // -------------------------------------------- /** + * Adapt from new-style `createPreUpgradeMessage()` to old-style + * `setPreUpgradeMessage(&$message)`. + * + * @param string $currentVer + * @param string $endVer + * @return string + */ + public function createPreUpgradeMessage($currentVer, $endVer) { + $preUpgradeMessage = ''; + foreach ($this->getRevisions() as $rev) { + if (version_compare($currentVer, $rev) < 0) { + $this->setPreUpgradeMessage($preUpgradeMessage, $rev, $currentVer); + } + } + return $preUpgradeMessage; + } + + /** + * Enqueue upgrade tasks for each revision. + * * @param \CRM_Queue_Queue $queue * @param $postUpgradeMessageFile * @param $startVer diff --git a/CRM/Upgrade/Page/Upgrade.php b/CRM/Upgrade/Page/Upgrade.php index 61e1ae1311c5..b52f0d7d453d 100644 --- a/CRM/Upgrade/Page/Upgrade.php +++ b/CRM/Upgrade/Page/Upgrade.php @@ -107,8 +107,7 @@ public function runIntro() { // cleanup only the templates_c directory $config->cleanup(1, FALSE); - $preUpgradeMessage = NULL; - $upgrade->setPreUpgradeMessage($preUpgradeMessage, $currentVer, $latestVer); + $preUpgradeMessage = $upgrade->createPreUpgradeMessage($currentVer, $latestVer); $template->assign('currentVersion', $currentVer); $template->assign('newVersion', $latestVer); From 5fcf4d4035c63d09d96e870876724b680aacbc2f Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 19:58:13 -0800 Subject: [PATCH 04/15] CRM-16860 - Remove incrementalPhpObject($version) The upgrade-handler class should no longer be determined by version. These classes are standalone entities (independent of version). --- CRM/Upgrade/Form.php | 36 ------------------------ CRM/Upgrade/Incremental/RevisionBase.php | 6 ++-- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index bc1220de6296..83b5745a8286 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -68,24 +68,6 @@ class CRM_Upgrade_Form extends CRM_Core_Form { */ public $locales; - /** - * Number to string mapper. - * - * @var array - */ - static $_numberMap = array( - 0 => 'Zero', - 1 => 'One', - 2 => 'Two', - 3 => 'Three', - 4 => 'Four', - 5 => 'Five', - 6 => 'Six', - 7 => 'Seven', - 8 => 'Eight', - 9 => 'Nine', - ); - /** * Constructor for the basic form page. * @@ -149,24 +131,6 @@ protected static function getUpgradeObjects() { return $upgradeObjects; } - /** - * @param $version - * - * @return mixed - */ - public static function &incrementalPhpObject($version) { - static $incrementalPhpObject = array(); - - $versionParts = explode('.', $version); - $versionName = self::$_numberMap[$versionParts[0]] . self::$_numberMap[$versionParts[1]]; - - if (!array_key_exists($versionName, $incrementalPhpObject)) { - $className = "CRM_Upgrade_Incremental_php_{$versionName}"; - $incrementalPhpObject[$versionName] = new $className(); - } - return $incrementalPhpObject[$versionName]; - } - /** * @param $version * @param $release diff --git a/CRM/Upgrade/Incremental/RevisionBase.php b/CRM/Upgrade/Incremental/RevisionBase.php index 744fc8d79800..ee4387706c84 100644 --- a/CRM/Upgrade/Incremental/RevisionBase.php +++ b/CRM/Upgrade/Incremental/RevisionBase.php @@ -134,7 +134,7 @@ public function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $sta // callback array('CRM_Upgrade_Incremental_RevisionBase', 'doIncrementalUpgradeStep'), // arguments - array($rev, $startVer, $endVer, $postUpgradeMessageFile), + array(get_class($this), $rev, $startVer, $endVer, $postUpgradeMessageFile), "Upgrade DB to $rev" ); $queue->createItem($task); @@ -185,12 +185,12 @@ public static function doIncrementalUpgradeStart(CRM_Queue_TaskContext $ctx, $re * * @return bool */ - public static function doIncrementalUpgradeStep(CRM_Queue_TaskContext $ctx, $rev, $originalVer, $latestVer, $postUpgradeMessageFile) { + public static function doIncrementalUpgradeStep(CRM_Queue_TaskContext $ctx, $thisClass, $rev, $originalVer, $latestVer, $postUpgradeMessageFile) { $upgrade = new CRM_Upgrade_Form(); $phpFunctionName = 'upgrade_' . str_replace('.', '_', $rev); - $versionObject = $upgrade->incrementalPhpObject($rev); + $versionObject = new $thisClass(); // pre-db check for major release. if ($upgrade->checkVersionRelease($rev, 'alpha1')) { From 5c7e58fa0edb6eb98857f44f57a5c2f0127ab7a3 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 19:59:23 -0800 Subject: [PATCH 05/15] CRM_Upgrade_Form - Remove `checkSQLConstraint` (unused, unimplemented) --- CRM/Upgrade/Form.php | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 83b5745a8286..c707349717b4 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -142,34 +142,6 @@ public function checkVersionRelease($version, $release) { return ($versionParts[2] == $release); } - /** - * @param $constraints - * - * @return array - */ - public function checkSQLConstraints(&$constraints) { - $pass = $fail = 0; - foreach ($constraints as $constraint) { - if ($this->checkSQLConstraint($constraint)) { - $pass++; - } - else { - $fail++; - } - return array($pass, $fail); - } - } - - /** - * @param $constraint - * - * @return bool - */ - public function checkSQLConstraint($constraint) { - // check constraint here - return TRUE; - } - /** * @param string $fileName * @param bool $isQueryString From 5305de9be8452cc4f724663c48a4b669c925bd79 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 20:01:35 -0800 Subject: [PATCH 06/15] CRM_Upgrade_Form - Remove `setQuery` (WTF) This function just calls `CRM_Core_DAO::executeQuery`, and it's only used once. --- CRM/Upgrade/Form.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index c707349717b4..c4f05d55db3e 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -222,17 +222,6 @@ public function postProcess() { } } - /** - * @param $query - * - * @return Object - */ - public function runQuery($query) { - return CRM_Core_DAO::executeQuery($query, - CRM_Core_DAO::$_nullArray - ); - } - /** * @param $version * @@ -245,7 +234,7 @@ public function setVersion($version) { UPDATE civicrm_domain SET version = '$version' "; - return $this->runQuery($query); + return CRM_Core_DAO::executeQuery($query); } /** From cbe3bc864e95c19636bd9212f8b24b1af72f0eab Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 20:03:19 -0800 Subject: [PATCH 07/15] CRM_Upgrade_Form - Remove `getRevisionSequence` (unused) We won't care about sequential list of revision numbers. Instead, we'll want a list of named upgrade steps. --- CRM/Upgrade/Form.php | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index c4f05d55db3e..b5cd122398a2 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -275,33 +275,6 @@ public function checkVersion($version) { return $domainID ? TRUE : FALSE; } - /** - * @return array - * @throws Exception - */ - public function getRevisionSequence() { - $revList = array(); - $sqlDir = implode(DIRECTORY_SEPARATOR, - array(dirname(__FILE__), 'Incremental', 'sql') - ); - $sqlFiles = scandir($sqlDir); - - $sqlFilePattern = '/^((\d{1,2}\.\d{1,2})\.(\d{1,2}\.)?(\d{1,2}|\w{4,7}))\.(my)?sql(\.tpl)?$/i'; - foreach ($sqlFiles as $file) { - if (preg_match($sqlFilePattern, $file, $matches)) { - if ($matches[2] == '4.0') { - CRM_Core_Error::fatal("4.0.x upgrade files shouldn't exist. Contact Lobo to discuss this. This is related to the issue CRM-7731."); - } - if (!in_array($matches[1], $revList)) { - $revList[] = $matches[1]; - } - } - } - - usort($revList, 'version_compare'); - return $revList; - } - /** * @param $rev * @param int $index From b73208454a4a66a021f33d5f2a496d61b7646604 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 20:36:43 -0800 Subject: [PATCH 08/15] CRM-16860 - Add CRM_Upgrade_Incremental_Interface --- CRM/Upgrade/Form.php | 3 +- CRM/Upgrade/Incremental/Base.php | 2 +- CRM/Upgrade/Incremental/Interface.php | 48 +++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 CRM/Upgrade/Incremental/Interface.php diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index b5cd122398a2..7127d3943cf9 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -112,6 +112,7 @@ public function __construct( /** * @return array + * Array(CRM_Upgrade_Incremental_Interface). */ protected static function getUpgradeObjects() { $majors = array( @@ -527,7 +528,7 @@ public function createPreUpgradeMessage($currentVer, $latestVer) { // Please note, at this point upgrade hasn't started executing queries. $upgradeObjects = self::getUpgradeObjects(); foreach ($upgradeObjects as $upgradeObject) { - /** @var CRM_Upgrade_Incremental_Base $upgradeObject */ + /** @var CRM_Upgrade_Incremental_Interface $upgradeObject */ $preUpgradeMessage .= $upgradeObject->createPreUpgradeMessage($currentVer, $latestVer); } diff --git a/CRM/Upgrade/Incremental/Base.php b/CRM/Upgrade/Incremental/Base.php index 5cfabc3ffa60..9463ab221588 100644 --- a/CRM/Upgrade/Incremental/Base.php +++ b/CRM/Upgrade/Incremental/Base.php @@ -28,7 +28,7 @@ /** * Base class for incremental upgrades */ -abstract class CRM_Upgrade_Incremental_Base { +abstract class CRM_Upgrade_Incremental_Base implements CRM_Upgrade_Incremental_Interface { const BATCH_SIZE = 5000; /** diff --git a/CRM/Upgrade/Incremental/Interface.php b/CRM/Upgrade/Incremental/Interface.php new file mode 100644 index 000000000000..bc2c021031df --- /dev/null +++ b/CRM/Upgrade/Incremental/Interface.php @@ -0,0 +1,48 @@ + Date: Fri, 11 Dec 2015 21:52:33 -0800 Subject: [PATCH 09/15] CRM-16860 - CRM_Upgrade - Scan `Steps/*.php` files --- CRM/Contact/BAO/Contact.php | 2 +- CRM/Core/BAO/WordReplacement.php | 4 +- CRM/Core/ClassLoader.php | 11 ++ CRM/Upgrade/Form.php | 28 ++-- CRM/Upgrade/Page/Cleanup.php | 2 +- CRM/Upgrade/Steps.php | 136 ++++++++++++++++++ .../php/FourOne.php => Steps/41_All.up.php} | 2 +- .../php/FourTwo.php => Steps/42_All.up.php} | 2 +- .../php/FourThree.php => Steps/43_All.up.php} | 2 +- .../php/FourFour.php => Steps/44_All.up.php} | 6 +- .../php/FourFive.php => Steps/45_All.up.php} | 2 +- .../php/FourSix.php => Steps/46_All.up.php} | 4 +- .../php/FourSeven.php => Steps/47_All.up.php} | 4 +- CRM/Upgrade/Steps/README.md | 33 +++++ Civi/Core/SettingsBag.php | 2 +- 15 files changed, 205 insertions(+), 35 deletions(-) create mode 100644 CRM/Upgrade/Steps.php rename CRM/Upgrade/{Incremental/php/FourOne.php => Steps/41_All.up.php} (99%) rename CRM/Upgrade/{Incremental/php/FourTwo.php => Steps/42_All.up.php} (99%) rename CRM/Upgrade/{Incremental/php/FourThree.php => Steps/43_All.up.php} (99%) rename CRM/Upgrade/{Incremental/php/FourFour.php => Steps/44_All.up.php} (99%) rename CRM/Upgrade/{Incremental/php/FourFive.php => Steps/45_All.up.php} (99%) mode change 100755 => 100644 rename CRM/Upgrade/{Incremental/php/FourSix.php => Steps/46_All.up.php} (98%) rename CRM/Upgrade/{Incremental/php/FourSeven.php => Steps/47_All.up.php} (98%) create mode 100644 CRM/Upgrade/Steps/README.md diff --git a/CRM/Contact/BAO/Contact.php b/CRM/Contact/BAO/Contact.php index 13c69e603d6e..7c4ee6986b79 100644 --- a/CRM/Contact/BAO/Contact.php +++ b/CRM/Contact/BAO/Contact.php @@ -37,7 +37,7 @@ class CRM_Contact_BAO_Contact extends CRM_Contact_DAO_Contact { * @see self::triggerInfo() * * Note that this is also used by the 4.3 upgrade script. - * @see CRM_Upgrade_Incremental_php_FourThree + * @see CRM_Upgrade_Steps_43_All */ const DROP_STRIP_FUNCTION_43 = "DROP FUNCTION IF EXISTS civicrm_strip_non_numeric"; const CREATE_STRIP_FUNCTION_43 = " diff --git a/CRM/Core/BAO/WordReplacement.php b/CRM/Core/BAO/WordReplacement.php index bbe3da3d8856..4ec8aed708b8 100644 --- a/CRM/Core/BAO/WordReplacement.php +++ b/CRM/Core/BAO/WordReplacement.php @@ -209,7 +209,7 @@ public static function rebuild($clearCaches = TRUE) { * and convert them to params for the WordReplacement.create API. * * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and - * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade + * CRM_Upgrade_Steps_44_All to ensure that the incremental upgrade * step behaves consistently even as the BAO evolves in future versions. * However, if there's a bug in here prior to 4.4.0, we should apply the * bug-fix in both places. @@ -265,7 +265,7 @@ public static function getConfigArraysAsAPIParams($rebuildEach) { * and write them out as records in civicrm_word_replacement. * * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and - * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade + * CRM_Upgrade_Steps_44_All to ensure that the incremental upgrade * step behaves consistently even as the BAO evolves in future versions. * However, if there's a bug in here prior to 4.4.0, we should apply the * bug-fix in both places. diff --git a/CRM/Core/ClassLoader.php b/CRM/Core/ClassLoader.php index 3002716fbebc..2f1c5bb518ac 100644 --- a/CRM/Core/ClassLoader.php +++ b/CRM/Core/ClassLoader.php @@ -168,6 +168,17 @@ public function loadClass($class) { // intelligible errors. if (FALSE != stream_resolve_include_path($file)) { require_once $file; + return; + } + + // CRM/Upgrade/Steps/{NUM}_{TextName}.up.php + // CRM/Upgrade/Steps/{NUM}/{NUM}_{TextName}.up.php + if (preg_match('/_[0-9]+_/', $class)) { + $file = preg_replace('/\/([0-9]+)\/([A-Z][a-zA-Z0-9]+)$/', '/\1_\2', strtr($class, '_', '/')) . '.up.php'; + if (FALSE != stream_resolve_include_path($file)) { + require_once $file; + return; + } } } } diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 7127d3943cf9..6ec568b81a78 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -111,25 +111,15 @@ public function __construct( } /** - * @return array - * Array(CRM_Upgrade_Incremental_Interface). + * @return CRM_Upgrade_Steps */ - protected static function getUpgradeObjects() { - $majors = array( - 'FourOne', - 'FourTwo', - 'FourThree', - 'FourFour', - 'FourFive', - 'FourSix', - 'FourSeven', - ); - $upgradeObjects = array(); - foreach ($majors as $major) { - $class = "CRM_Upgrade_Incremental_php_$major"; - $upgradeObjects[] = new $class(); + public static function getSteps() { + if (!isset(Civi::$statics[__CLASS__]['steps'])) { + Civi::$statics[__CLASS__]['steps'] = new CRM_Upgrade_Steps('civicrm', array( + __DIR__ . DIRECTORY_SEPARATOR . 'Steps' => 'CRM_Upgrade_Steps_', + )); } - return $upgradeObjects; + return Civi::$statics[__CLASS__]['steps']; } /** @@ -476,7 +466,7 @@ public static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFi 'reset' => TRUE, )); - $upgradeObjects = self::getUpgradeObjects(); + $upgradeObjects = self::getSteps()->getPendingObjects(); foreach ($upgradeObjects as $obj) { /** @var CRM_Upgrade_Incremental_RevisionBase $obj */ $obj->buildQueue($queue, $postUpgradeMessageFile, $currentVer, $latestVer); @@ -526,7 +516,7 @@ public function createPreUpgradeMessage($currentVer, $latestVer) { // Scan through all php files and see if any file is interested in setting pre-upgrade-message // based on $currentVer, $latestVer. // Please note, at this point upgrade hasn't started executing queries. - $upgradeObjects = self::getUpgradeObjects(); + $upgradeObjects = self::getSteps()->getPendingObjects(); foreach ($upgradeObjects as $upgradeObject) { /** @var CRM_Upgrade_Incremental_Interface $upgradeObject */ $preUpgradeMessage .= $upgradeObject->createPreUpgradeMessage($currentVer, $latestVer); diff --git a/CRM/Upgrade/Page/Cleanup.php b/CRM/Upgrade/Page/Cleanup.php index ecb1c2a69326..9dd3c3908932 100644 --- a/CRM/Upgrade/Page/Cleanup.php +++ b/CRM/Upgrade/Page/Cleanup.php @@ -30,7 +30,7 @@ */ class CRM_Upgrade_Page_Cleanup extends CRM_Core_Page { public function cleanup425() { - $rows = CRM_Upgrade_Incremental_php_FourTwo::deleteInvalidPairs(); + $rows = CRM_Upgrade_Steps_42_All::deleteInvalidPairs(); $template = CRM_Core_Smarty::singleton(); $columnHeaders = array( diff --git a/CRM/Upgrade/Steps.php b/CRM/Upgrade/Steps.php new file mode 100644 index 000000000000..206d47d84bce --- /dev/null +++ b/CRM/Upgrade/Steps.php @@ -0,0 +1,136 @@ + string $classPrefix). + */ + private $paths; + + /** + * CRM_Upgrade_Steps constructor. + * + * @param string $module + * @param array $paths + * Array(string $fullPath => string $classPrefix). + */ + public function __construct($module, $paths = array()) { + $this->module = $module; + $this->paths = $paths; + } + + /** + * Get a list of all upgrade steps (as class names). + * + * @return array + * Array(string $file => string $className). + */ + public function getAllClasses() { + $result = array(); + foreach ($this->paths as $path => $classPrefix) { + if (is_dir($path)) { + $files = CRM_Utils_File::findFiles($path, '*.up.php'); + foreach ($files as $file) { + $result[$file] = $this->toClassName($path, $classPrefix, $file); + } + } + } + ksort($result); + return $result; + } + + /** + * Get a list of all upgrade steps (as objects/instances). + * + * @return array + * Array(CRM_Upgrade_Incremental_Interface). + */ + public function getAllObjects() { + $result = array(); + foreach ($this->getAllClasses() as $file => $className) { + $result[$className] = new $className(); + } + return $result; + } + + /** + * @return array + * Array(CRM_Upgrade_Incremental_Interface). + */ + public function getPendingObjects() { + $result = array(); + foreach ($this->getAllClasses() as $file => $className) { + if ($this->isPending($className)) { + $result[] = new $className(); + } + } + return $result; + } + + /** + * @param string $className + * @return bool + */ + public function isPending($className) { + return TRUE; + } + + /** + * @param string $absPath + * Base search path. + * @param $classPrefix + * Class prefix of all files in the path. + * @param $absFile + * File path. + * @return string + */ + protected function toClassName($absPath, $classPrefix, $absFile) { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + $absPath = strtr($absPath, '\\', '/'); + $absFile = strtr($absFile, '\\', '/'); + } + + $relFile = CRM_Utils_File::relativize($absFile, CRM_Utils_File::addTrailingSlash($absPath, '/')); + $relFile = preg_replace('/\.up\.php$/', '', $relFile); + return $classPrefix . strtr($relFile, '/', '_'); + } + +} diff --git a/CRM/Upgrade/Incremental/php/FourOne.php b/CRM/Upgrade/Steps/41_All.up.php similarity index 99% rename from CRM/Upgrade/Incremental/php/FourOne.php rename to CRM/Upgrade/Steps/41_All.up.php index 278324679d32..dc3d6bbac288 100644 --- a/CRM/Upgrade/Incremental/php/FourOne.php +++ b/CRM/Upgrade/Steps/41_All.up.php @@ -32,7 +32,7 @@ * $Id$ * */ -class CRM_Upgrade_Incremental_php_FourOne extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_41_All extends CRM_Upgrade_Incremental_RevisionBase { // This was changed in 4.3 so we define it locally for compatibility with older dbs const NAVIGATION_NAME = "Navigation Menu"; diff --git a/CRM/Upgrade/Incremental/php/FourTwo.php b/CRM/Upgrade/Steps/42_All.up.php similarity index 99% rename from CRM/Upgrade/Incremental/php/FourTwo.php rename to CRM/Upgrade/Steps/42_All.up.php index e81e84a61438..8e1038e219da 100644 --- a/CRM/Upgrade/Incremental/php/FourTwo.php +++ b/CRM/Upgrade/Steps/42_All.up.php @@ -28,7 +28,7 @@ /** * Upgrade logic for 4.2 */ -class CRM_Upgrade_Incremental_php_FourTwo extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_42_All extends CRM_Upgrade_Incremental_RevisionBase { const SETTINGS_SNIPPET_PATTERN = '/CRM_Core_ClassLoader::singleton\(\)-\>register/'; const SETTINGS_SNIPPET = "\nrequire_once 'CRM/Core/ClassLoader.php';\nCRM_Core_ClassLoader::singleton()->register();\n"; diff --git a/CRM/Upgrade/Incremental/php/FourThree.php b/CRM/Upgrade/Steps/43_All.up.php similarity index 99% rename from CRM/Upgrade/Incremental/php/FourThree.php rename to CRM/Upgrade/Steps/43_All.up.php index 906afd25bac6..d09ee97b6bcf 100644 --- a/CRM/Upgrade/Incremental/php/FourThree.php +++ b/CRM/Upgrade/Steps/43_All.up.php @@ -28,7 +28,7 @@ /** * Upgrade logic for 4.3 */ -class CRM_Upgrade_Incremental_php_FourThree extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_43_All extends CRM_Upgrade_Incremental_RevisionBase { /** * @return array diff --git a/CRM/Upgrade/Incremental/php/FourFour.php b/CRM/Upgrade/Steps/44_All.up.php similarity index 99% rename from CRM/Upgrade/Incremental/php/FourFour.php rename to CRM/Upgrade/Steps/44_All.up.php index 345dd25e36e8..202ab1d275c0 100644 --- a/CRM/Upgrade/Incremental/php/FourFour.php +++ b/CRM/Upgrade/Steps/44_All.up.php @@ -28,7 +28,7 @@ /** * Upgrade logic for 4.4 */ -class CRM_Upgrade_Incremental_php_FourFour extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_44_All extends CRM_Upgrade_Incremental_RevisionBase { const MAX_WORD_REPLACEMENT_SIZE = 255; /** @@ -719,7 +719,7 @@ public static function wordReplacements_patch(CRM_Queue_TaskContext $ctx, $rev) * and convert them to params for the WordReplacement.create API. * * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and - * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade + * CRM_Upgrade_Steps_44_All to ensure that the incremental upgrade * step behaves consistently even as the BAO evolves in future versions. * However, if there's a bug in here prior to 4.4.0, we should apply the * bugfix in both places. @@ -775,7 +775,7 @@ public static function getConfigArraysAsAPIParams($rebuildEach) { * and write them out as records in civicrm_word_replacement. * * Note: This function is duplicated in CRM_Core_BAO_WordReplacement and - * CRM_Upgrade_Incremental_php_FourFour to ensure that the incremental upgrade + * CRM_Upgrade_Steps_44_All to ensure that the incremental upgrade * step behaves consistently even as the BAO evolves in future versions. * However, if there's a bug in here prior to 4.4.0, we should apply the * bugfix in both places. diff --git a/CRM/Upgrade/Incremental/php/FourFive.php b/CRM/Upgrade/Steps/45_All.up.php old mode 100755 new mode 100644 similarity index 99% rename from CRM/Upgrade/Incremental/php/FourFive.php rename to CRM/Upgrade/Steps/45_All.up.php index 8d6f2b8204ff..81fde97dd53b --- a/CRM/Upgrade/Incremental/php/FourFive.php +++ b/CRM/Upgrade/Steps/45_All.up.php @@ -28,7 +28,7 @@ /** * Upgrade logic for 4.5 */ -class CRM_Upgrade_Incremental_php_FourFive extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_45_All extends CRM_Upgrade_Incremental_RevisionBase { /** * @return array diff --git a/CRM/Upgrade/Incremental/php/FourSix.php b/CRM/Upgrade/Steps/46_All.up.php similarity index 98% rename from CRM/Upgrade/Incremental/php/FourSix.php rename to CRM/Upgrade/Steps/46_All.up.php index 29d7953fc745..6f6fbcae1492 100644 --- a/CRM/Upgrade/Incremental/php/FourSix.php +++ b/CRM/Upgrade/Steps/46_All.up.php @@ -28,7 +28,7 @@ /** * Upgrade logic for 4.6 */ -class CRM_Upgrade_Incremental_php_FourSix extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_46_All extends CRM_Upgrade_Incremental_RevisionBase { /** * @return array @@ -223,7 +223,7 @@ public static function task_4_6_x_runOnlySql(CRM_Queue_TaskContext $ctx, $rev) { $smarty = CRM_Core_Smarty::singleton(); $smarty->assign('domainID', CRM_Core_Config::domainID()); - $fileName = dirname(__DIR__) . "/sql/$rev.mysql.tpl"; + $fileName = dirname(__DIR__) . "/Incremental/sql/$rev.mysql.tpl"; $upgrade->source($smarty->fetch($fileName), TRUE); diff --git a/CRM/Upgrade/Incremental/php/FourSeven.php b/CRM/Upgrade/Steps/47_All.up.php similarity index 98% rename from CRM/Upgrade/Incremental/php/FourSeven.php rename to CRM/Upgrade/Steps/47_All.up.php index 4da399f51afc..77a4ebcf0e16 100644 --- a/CRM/Upgrade/Incremental/php/FourSeven.php +++ b/CRM/Upgrade/Steps/47_All.up.php @@ -27,7 +27,7 @@ /** * Upgrade logic for 4.7 */ -class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_47_All extends CRM_Upgrade_Incremental_RevisionBase { /** * @return array @@ -179,7 +179,7 @@ public function migrateSettings(CRM_Queue_TaskContext $ctx) { $domainDao = CRM_Core_DAO::executeQuery('SELECT id, config_backend FROM civicrm_domain'); while ($domainDao->fetch()) { - $settings = CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($domainDao->id, $domainDao->config_backend); + $settings = CRM_Upgrade_Steps_47_All::convertBackendToSettings($domainDao->id, $domainDao->config_backend); CRM_Core_Error::debug_var('convertBackendToSettings', array( 'domainId' => $domainDao->id, 'backend' => $domainDao->config_backend, diff --git a/CRM/Upgrade/Steps/README.md b/CRM/Upgrade/Steps/README.md new file mode 100644 index 000000000000..4cfd1bbbde91 --- /dev/null +++ b/CRM/Upgrade/Steps/README.md @@ -0,0 +1,33 @@ +## Upgrade Steps: Naming Conventions + +``` +## Formula (New Code; v4.7+) +CRM/Upgrade/Steps/{MAJOR}/{COUNTER}_{DescriptiveName}.up.php + +## Formula (Legacy Code) +CRM/Upgrade/Steps/{MAJOR}_All.up.php + +## Examples +CRM/Upgrade/Steps/47/100_FixFoo.up.php (CRM_Upgrade_Steps_47_100_FixFoo) +CRM/Upgrade/Steps/47/100_UpdateBar.up.php (CRM_Upgrade_Steps_47_100_UpdateBar) +CRM/Upgrade/Steps/47/205_TwiddleBaz.up.php (CRM_Upgrade_Steps_47_205_TwiddleBaz) +CRM/Upgrade/Steps/46_All.up.php (CRM_Upgrade_Steps_46_All) +CRM/Upgrade/Steps/45_All.up.php (CRM_Upgrade_Steps_45_All) +``` + +## Important Notes + + * Recognized file extensions: `*.up.php` + * The file name must be a number followed by a camelcase word (eg `100_FixFoo.up.php`). + * For *old* upgrades, all incremental changes were originally written as one big file (eg `FourFive.php` aka `45_All.up.php`). These files extend `RevisionBase`. + * For *new* upgrades, all incremental changes must be standalone files in a subfolder (eg `47/100_FixFoo.up.php`). These files extend `SimpleBase`. + +## Execution + +The files in this folder are executed by the following process: + + 1. Find all files named `CRM/Upgrade/Steps/**.up.php`. + 2. Convert file names to class names. + 3. Sort class names (dictionary order). + 4. Identify classes which have been executed already. Skip. + 5. Execute all other classes. diff --git a/Civi/Core/SettingsBag.php b/Civi/Core/SettingsBag.php index 73b820fb568b..0794fccead2d 100644 --- a/Civi/Core/SettingsBag.php +++ b/Civi/Core/SettingsBag.php @@ -142,7 +142,7 @@ public function loadValues() { if ($isUpgradeMode && empty($this->contactId) && \CRM_Core_DAO::checkFieldExists('civicrm_domain', 'config_backend', FALSE)) { $config_backend = \CRM_Core_DAO::singleValueQuery('SELECT config_backend FROM civicrm_domain WHERE id = %1', array(1 => array($this->domainId, 'Positive'))); - $oldSettings = \CRM_Upgrade_Incremental_php_FourSeven::convertBackendToSettings($this->domainId, $config_backend); + $oldSettings = \CRM_Upgrade_Steps_47_All::convertBackendToSettings($this->domainId, $config_backend); \CRM_Utils_Array::extend($this->values, $oldSettings); } From e523f218f0c5fc6c0d9f84f36d5f0824962416ef Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 11 Dec 2015 23:54:54 -0800 Subject: [PATCH 10/15] CRM-16860 - Rename CRM/Upgrade/Steps/47_All to CRM/Upgrade/Steps/47/000_Legacy. Since 4.7 will be the new stable, any maintenance upgrades will go to 4.7. We want those to use the new merge-friendly upgrade scheme, so we should put all 4.7-related changes in a subdirectory to set the proper atmosphere. --- CRM/Upgrade/Steps/{47_All.up.php => 47/000_Legacy.up.php} | 4 ++-- Civi/Core/SettingsBag.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename CRM/Upgrade/Steps/{47_All.up.php => 47/000_Legacy.up.php} (98%) diff --git a/CRM/Upgrade/Steps/47_All.up.php b/CRM/Upgrade/Steps/47/000_Legacy.up.php similarity index 98% rename from CRM/Upgrade/Steps/47_All.up.php rename to CRM/Upgrade/Steps/47/000_Legacy.up.php index 77a4ebcf0e16..f133ebed51b4 100644 --- a/CRM/Upgrade/Steps/47_All.up.php +++ b/CRM/Upgrade/Steps/47/000_Legacy.up.php @@ -27,7 +27,7 @@ /** * Upgrade logic for 4.7 */ -class CRM_Upgrade_Steps_47_All extends CRM_Upgrade_Incremental_RevisionBase { +class CRM_Upgrade_Steps_47_000_Legacy extends CRM_Upgrade_Incremental_RevisionBase { /** * @return array @@ -179,7 +179,7 @@ public function migrateSettings(CRM_Queue_TaskContext $ctx) { $domainDao = CRM_Core_DAO::executeQuery('SELECT id, config_backend FROM civicrm_domain'); while ($domainDao->fetch()) { - $settings = CRM_Upgrade_Steps_47_All::convertBackendToSettings($domainDao->id, $domainDao->config_backend); + $settings = CRM_Upgrade_Steps_47_000_Legacy::convertBackendToSettings($domainDao->id, $domainDao->config_backend); CRM_Core_Error::debug_var('convertBackendToSettings', array( 'domainId' => $domainDao->id, 'backend' => $domainDao->config_backend, diff --git a/Civi/Core/SettingsBag.php b/Civi/Core/SettingsBag.php index 0794fccead2d..44d92c5c4369 100644 --- a/Civi/Core/SettingsBag.php +++ b/Civi/Core/SettingsBag.php @@ -142,7 +142,7 @@ public function loadValues() { if ($isUpgradeMode && empty($this->contactId) && \CRM_Core_DAO::checkFieldExists('civicrm_domain', 'config_backend', FALSE)) { $config_backend = \CRM_Core_DAO::singleValueQuery('SELECT config_backend FROM civicrm_domain WHERE id = %1', array(1 => array($this->domainId, 'Positive'))); - $oldSettings = \CRM_Upgrade_Steps_47_All::convertBackendToSettings($this->domainId, $config_backend); + $oldSettings = \CRM_Upgrade_Steps_47_000_Legacy::convertBackendToSettings($this->domainId, $config_backend); \CRM_Utils_Array::extend($this->values, $oldSettings); } From 779cff49adfcfe7ce5c9dd7fdcaa4b3748b7cc17 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sat, 12 Dec 2015 01:20:44 -0800 Subject: [PATCH 11/15] CRM-16860 - Add `SimpleBase` for and an example upgrade task --- CRM/Queue/MultiTasker.php | 147 +++++++++++++++++++++++ CRM/Upgrade/Incremental/Base.php | 114 ------------------ CRM/Upgrade/Incremental/RevisionBase.php | 68 ++++++++++- CRM/Upgrade/Incremental/SimpleBase.php | 108 +++++++++++++++++ CRM/Upgrade/Steps/47/000_Example.up.php | 53 ++++++++ 5 files changed, 375 insertions(+), 115 deletions(-) create mode 100644 CRM/Queue/MultiTasker.php delete mode 100644 CRM/Upgrade/Incremental/Base.php create mode 100644 CRM/Upgrade/Incremental/SimpleBase.php create mode 100644 CRM/Upgrade/Steps/47/000_Example.up.php diff --git a/CRM/Queue/MultiTasker.php b/CRM/Queue/MultiTasker.php new file mode 100644 index 000000000000..e7265d8d40b4 --- /dev/null +++ b/CRM/Queue/MultiTasker.php @@ -0,0 +1,147 @@ +addTask('Do the bar thing', 'bar', 123); + * $this->addTask('Do the bar thing', 'bar', 456); + * } + * public function bar($x) { + * $this->ctx->log->info("Got value $x"); + * } + * } + * @endCode + */ +class CRM_Queue_MultiTasker { + + /** + * @var CRM_Queue_TaskContext + */ + protected $ctx; + + /** + * @var array + * Quasi-persistent data that's serialized and restored whenever we + * run a task for this class. + */ + protected $stickyData; + + /** + * CRM_Upgrade_Incremental_SimpleBase constructor. + * + * @param CRM_Queue_TaskContext|NULL $ctx + * @param array|NULL $stickyData + */ + public function __construct($ctx = NULL, $stickyData = array()) { + $this->ctx = $ctx; + $this->stickyData = $stickyData; + if (!isset($this->stickyData['class'])) { + $this->stickyData['class'] = get_class($this); + } + if ($ctx && !isset($this->stickyData['queue'])) { + $this->stickyData['queue'] = $ctx->queue->getName(); + } + } + + /** + * Syntactic sugar for adding a task. + * + * Task is (a) in this class and (b) has a very high priority. + * + * After passing the $funcName, you can also pass parameters that will go to + * the function. Note that all params must be serializable. + * + * Given the very high priority, this is intended for last-minute additions to the start + * of the queue. It should *not* be used for initializing the queue. + * + * @param string $title + * @param string $funcName + */ + public function addTask($title, $funcName) { + $task = call_user_func_array(array($this, 'createTask'), func_get_args()); + $this->getQueue()->createItem($task, array('weight' => -1)); + } + + public function addInitialTask($title, $funcName) { + $task = call_user_func_array(array($this, 'createTask'), func_get_args()); + $this->getQueue()->createItem($task, array('weight' => 0)); + } + + /** + * Create a task which points to local function (in this class). + * + * After passing the $funcName, you can also pass parameters that will go to + * the function. Note that all params must be serializable. + * + * Note: The task is *not* enqueued. + * + * @param $title + * @param $funcName + * @return \CRM_Queue_Task + */ + protected function createTask($title, $funcName) { + $funcArgs = func_get_args(); + $title = array_shift($funcArgs); + $funcName = array_shift($funcArgs); + $task = new CRM_Queue_Task( + array(get_class($this), 'doTask'), + array($this->stickyData, $funcName, $funcArgs), + $title + ); + return $task; + } + + /** + * @return \CRM_Queue_Queue + */ + protected function getQueue() { + $queue = CRM_Queue_Service::singleton()->load(array( + 'type' => 'Sql', + 'name' => $this->stickyData['queue'], + )); + return $queue; + } + + public static function doTask(CRM_Queue_TaskContext $ctx, $stickyData, $funcName, $funcArgs) { + // FIXME + // $upgrade = new CRM_Upgrade_Form(); + // $upgrade->setSchemaStructureTables($rev); + + /** @var self $obj */ + $className = $stickyData['class']; + $obj = new $className($ctx, $stickyData); + call_user_func_array(array($obj, $funcName), $funcArgs); + + return TRUE; + } + +} diff --git a/CRM/Upgrade/Incremental/Base.php b/CRM/Upgrade/Incremental/Base.php deleted file mode 100644 index 9463ab221588..000000000000 --- a/CRM/Upgrade/Incremental/Base.php +++ /dev/null @@ -1,114 +0,0 @@ -processSQL($rev); - - return TRUE; - } - - /** - * Syntactic sugar for adding a task. - * - * Task is (a) in this class and (b) has a high priority. - * - * After passing the $funcName, you can also pass parameters that will go to - * the function. Note that all params must be serializable. - * - * @param string $title - * @param string $funcName - */ - protected function addTask($title, $funcName) { - $queue = CRM_Queue_Service::singleton()->load(array( - 'type' => 'Sql', - 'name' => CRM_Upgrade_Form::QUEUE_NAME, - )); - - $args = func_get_args(); - $title = array_shift($args); - $funcName = array_shift($args); - $task = new CRM_Queue_Task( - array(get_class($this), $funcName), - $args, - $title - ); - $queue->createItem($task, array('weight' => -1)); - } - - /** - * Remove a payment processor if not in use - * - * @param CRM_Queue_TaskContext $ctx - * @param string $name - * @return bool - * @throws \CiviCRM_API3_Exception - */ - public static function removePaymentProcessorType(CRM_Queue_TaskContext $ctx, $name) { - $processors = civicrm_api3('PaymentProcessor', 'getcount', array('payment_processor_type_id' => $name)); - if (empty($processors['result'])) { - $result = civicrm_api3('PaymentProcessorType', 'get', array( - 'name' => $name, - 'return' => 'id', - )); - if (!empty($result['id'])) { - civicrm_api3('PaymentProcessorType', 'delete', array('id' => $result['id'])); - } - } - return TRUE; - } - -} diff --git a/CRM/Upgrade/Incremental/RevisionBase.php b/CRM/Upgrade/Incremental/RevisionBase.php index ee4387706c84..7c4c7b2364b1 100644 --- a/CRM/Upgrade/Incremental/RevisionBase.php +++ b/CRM/Upgrade/Incremental/RevisionBase.php @@ -44,7 +44,8 @@ * * @deprecated */ -abstract class CRM_Upgrade_Incremental_RevisionBase extends CRM_Upgrade_Incremental_Base { +abstract class CRM_Upgrade_Incremental_RevisionBase implements CRM_Upgrade_Incremental_Interface { + const BATCH_SIZE = 5000; /** * Get a list of incremental revisions. @@ -252,4 +253,69 @@ public static function doIncrementalUpgradeFinish(CRM_Queue_TaskContext $ctx, $r return TRUE; } + /** + * Syntactic sugar for adding a task. + * + * Task is (a) in this class and (b) has a high priority. + * + * After passing the $funcName, you can also pass parameters that will go to + * the function. Note that all params must be serializable. + * + * @param string $title + * @param string $funcName + */ + protected function addTask($title, $funcName) { + $queue = CRM_Queue_Service::singleton()->load(array( + 'type' => 'Sql', + 'name' => CRM_Upgrade_Form::QUEUE_NAME, + )); + + $args = func_get_args(); + $title = array_shift($args); + $funcName = array_shift($args); + $task = new CRM_Queue_Task( + array(get_class($this), $funcName), + $args, + $title + ); + $queue->createItem($task, array('weight' => -1)); + } + + /** + * (Queue Task Callback) + * + * @param \CRM_Queue_TaskContext $ctx + * @param string $rev + * + * @return bool + */ + public static function runSql(CRM_Queue_TaskContext $ctx, $rev) { + $upgrade = new CRM_Upgrade_Form(); + $upgrade->processSQL($rev); + + return TRUE; + } + + /** + * Remove a payment processor if not in use + * + * @param CRM_Queue_TaskContext $ctx + * @param string $name + * @return bool + * @throws \CiviCRM_API3_Exception + */ + public static function removePaymentProcessorType(CRM_Queue_TaskContext $ctx, $name) { + $processors = civicrm_api3('PaymentProcessor', 'getcount', array('payment_processor_type_id' => $name)); + if (empty($processors['result'])) { + $result = civicrm_api3('PaymentProcessorType', 'get', array( + 'name' => $name, + 'return' => 'id', + )); + if (!empty($result['id'])) { + civicrm_api3('PaymentProcessorType', 'delete', array('id' => $result['id'])); + } + } + return TRUE; + } + } diff --git a/CRM/Upgrade/Incremental/SimpleBase.php b/CRM/Upgrade/Incremental/SimpleBase.php new file mode 100644 index 000000000000..c04589f7a8ba --- /dev/null +++ b/CRM/Upgrade/Incremental/SimpleBase.php @@ -0,0 +1,108 @@ +stickyData['startVer'] = $startVer; + $this->stickyData['endVer'] = $endVer; + $this->stickyData['postUpgradeMessageFile'] = $postUpgradeMessageFile; + $this->stickyData['queue'] = $queue->getName(); + $this->buildTasks(); + } + + /** + * Schedule the initial set of tasks. + * + * If you want to schedule several tasks from the outset, you may override this. + */ + protected function buildTasks() { + $this->addInitialTask($this->getTitle(), 'upgrade'); + } + + /** + * Perform some upgrade work. + * + * In implementing this function, you may find it helpful to: + * - Break down the work into small chunks by calling addTask(...). + * (Note: If any information is required for the task, pass it through + * as an argument. You cannot store persistently at the class level.) + * - Display a notification by calling addPostUpgradeMessage(...). + */ + public function upgrade() { + // Override me! + } + + /** + * @return string + */ + public function getTitle() { + return "Upgrade DB: " . get_class($this); + } + + /** + * Append another message. + * + * @param $message + * @return $this + */ + public function addPostUpgradeMessage($message) { + file_put_contents( + $this->stickyData['postUpgradeMessageFile'], + '

' . $message, + FILE_APPEND + ); + return $this; + } + +} diff --git a/CRM/Upgrade/Steps/47/000_Example.up.php b/CRM/Upgrade/Steps/47/000_Example.up.php new file mode 100644 index 000000000000..c66fde57f003 --- /dev/null +++ b/CRM/Upgrade/Steps/47/000_Example.up.php @@ -0,0 +1,53 @@ +ctx->log->info("Foooo baaar !!"); + // CRM_Core_DAO::executeQuery('UPDATE something'); + // $this->addTask(ts('Do some more work'), '_task_do_more', 'extradata'); + // $this->addPostUpgradeMessage(ts('Ran the example upgrade step!')); + } + + // public function _task_do_more($extradata) { + // } + +} From 86c92de2023011a93df9a027f1ea0d61d9bafb49 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sun, 13 Dec 2015 15:08:56 -0800 Subject: [PATCH 12/15] CRM_Upgrade_Incremental_Interface - Add getName() --- CRM/Upgrade/Incremental/Interface.php | 11 +++++++++++ CRM/Upgrade/Incremental/RevisionBase.php | 7 +++++++ CRM/Upgrade/Incremental/SimpleBase.php | 7 +++++++ 3 files changed, 25 insertions(+) diff --git a/CRM/Upgrade/Incremental/Interface.php b/CRM/Upgrade/Incremental/Interface.php index bc2c021031df..272925c5bda9 100644 --- a/CRM/Upgrade/Incremental/Interface.php +++ b/CRM/Upgrade/Incremental/Interface.php @@ -30,6 +30,13 @@ */ interface CRM_Upgrade_Incremental_Interface { + /** + * Get a symbolic name for this incremental upgrade step. + * + * @return string + */ + public function getName(); + /** * @param string $startVer * @param string $endVer @@ -38,6 +45,10 @@ interface CRM_Upgrade_Incremental_Interface { public function createPreUpgradeMessage($startVer, $endVer); /** + * Enqueue a set of tasks for this upgrade. + * + * In addition to performing the actual upgrade, + * * @param \CRM_Queue_Queue $queue * @param string $postUpgradeMessageFile * @param string $startVer diff --git a/CRM/Upgrade/Incremental/RevisionBase.php b/CRM/Upgrade/Incremental/RevisionBase.php index 7c4c7b2364b1..4b11dfb48222 100644 --- a/CRM/Upgrade/Incremental/RevisionBase.php +++ b/CRM/Upgrade/Incremental/RevisionBase.php @@ -92,6 +92,13 @@ public function setPostUpgradeMessage(&$postUpgradeMessage, $rev) { // -------------------------------------------- + /** + * @return string + */ + public function getName() { + return get_class($this); + } + /** * Adapt from new-style `createPreUpgradeMessage()` to old-style * `setPreUpgradeMessage(&$message)`. diff --git a/CRM/Upgrade/Incremental/SimpleBase.php b/CRM/Upgrade/Incremental/SimpleBase.php index c04589f7a8ba..129e1a75fe3a 100644 --- a/CRM/Upgrade/Incremental/SimpleBase.php +++ b/CRM/Upgrade/Incremental/SimpleBase.php @@ -38,6 +38,13 @@ */ class CRM_Upgrade_Incremental_SimpleBase extends CRM_Queue_MultiTasker implements CRM_Upgrade_Incremental_Interface { + /** + * @return string + */ + public function getName() { + return get_class($this); + } + /** * @param string $startVer * @param string $endVer From f1b25308aef22ba926eb7d54a6065eb0926cba6c Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sun, 13 Dec 2015 15:28:16 -0800 Subject: [PATCH 13/15] CRM-16860 - Declare `civicrm_upgrade` table --- .gitignore | 1 + sql/civicrm_upgrade.mysql | 27 +++++++++++++++++++ xml/schema/Core/Upgrade.xml | 53 +++++++++++++++++++++++++++++++++++++ xml/schema/Core/files.xml | 1 + 4 files changed, 82 insertions(+) create mode 100644 sql/civicrm_upgrade.mysql create mode 100644 xml/schema/Core/Upgrade.xml diff --git a/.gitignore b/.gitignore index b6b605e2ce11..021befe3b9e9 100644 --- a/.gitignore +++ b/.gitignore @@ -90,6 +90,7 @@ CRM/Core/DAO/UFField.php CRM/Core/DAO/UFGroup.php CRM/Core/DAO/UFJoin.php CRM/Core/DAO/UFMatch.php +CRM/Core/DAO/Upgrade.php CRM/Core/DAO/Website.php CRM/Core/DAO/WordReplacement.php CRM/Core/DAO/Worldregion.php diff --git a/sql/civicrm_upgrade.mysql b/sql/civicrm_upgrade.mysql new file mode 100644 index 000000000000..259b08b138db --- /dev/null +++ b/sql/civicrm_upgrade.mysql @@ -0,0 +1,27 @@ +-- This code has been copied from the auto-generated civicrm.mysql +-- It is required to facilitate upgrades. + +-- /******************************************************* +-- * +-- * civicrm_upgrade +-- * +-- * Track individual upgrade steps +-- * +-- *******************************************************/ +CREATE TABLE `civicrm_upgrade` ( + + + `id` int unsigned NOT NULL AUTO_INCREMENT , + `module` varchar(127) NOT NULL COMMENT 'The module or extension for which upgrades were handled.', + `name` varchar(255) COMMENT 'Unique name for upgrade', + `created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'When was the upgrade executed' +, + PRIMARY KEY ( `id` ) + + , UNIQUE INDEX `index_module_name`( + module + , name + ) + + +) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ; diff --git a/xml/schema/Core/Upgrade.xml b/xml/schema/Core/Upgrade.xml new file mode 100644 index 000000000000..cb2355d5bdbf --- /dev/null +++ b/xml/schema/Core/Upgrade.xml @@ -0,0 +1,53 @@ + + + + CRM/Core + Upgrade + civicrm_upgrade + Track individual upgrade steps + 4.7 + + id + Step ID + int unsigned + true + 4.7 + + + id + true + + + module + Upgrade Module + varchar + 127 + true + The module or extension for which upgrades were handled. + 4.7 + + + name + Upgrade Name + varchar + 255 + Unique name for upgrade + 4.7 + + + created_date + Upgrade Created Date + timestamp + When was the upgrade executed + false + CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + 4.7 + + + index_module_name + module + name + true + 4.7 + +
diff --git a/xml/schema/Core/files.xml b/xml/schema/Core/files.xml index 51a0ba7ebec0..9270ec681e4d 100644 --- a/xml/schema/Core/files.xml +++ b/xml/schema/Core/files.xml @@ -41,6 +41,7 @@ + From 615821035aaff95914e939af014033c02b223169 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sun, 13 Dec 2015 15:44:41 -0800 Subject: [PATCH 14/15] CRM-16860 - Keep track of previously-executed upgrade steps. --- CRM/Upgrade/Form.php | 17 ++++++++++- CRM/Upgrade/Steps.php | 66 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/CRM/Upgrade/Form.php b/CRM/Upgrade/Form.php index 6ec568b81a78..94566c56f9f6 100644 --- a/CRM/Upgrade/Form.php +++ b/CRM/Upgrade/Form.php @@ -468,13 +468,28 @@ public static function buildQueue($currentVer, $latestVer, $postUpgradeMessageFi $upgradeObjects = self::getSteps()->getPendingObjects(); foreach ($upgradeObjects as $obj) { - /** @var CRM_Upgrade_Incremental_RevisionBase $obj */ + /** @var CRM_Upgrade_Incremental_Interface $obj */ $obj->buildQueue($queue, $postUpgradeMessageFile, $currentVer, $latestVer); + + // It would be nice if the doFinishUpgradeObject/setExecuted were done within + // the enqueued steps, but it's a little messy to pass around the necessary + // details. + $task = new CRM_Queue_Task( + array('CRM_Upgrade_Form', 'doFinishUpgradeObject'), + array($obj->getName()), + "Mark finished: " . $obj->getName() + ); + $queue->createItem($task); } return $queue; } + public static function doFinishUpgradeObject(CRM_Queue_TaskContext $ctx, $name) { + CRM_Upgrade_Form::getSteps()->setExecuted($name, TRUE); + return TRUE; + } + public static function doFinish() { $upgrade = new CRM_Upgrade_Form(); list($ignore, $latestVer) = $upgrade->getUpgradeVersions(); diff --git a/CRM/Upgrade/Steps.php b/CRM/Upgrade/Steps.php index 206d47d84bce..d31617c31a96 100644 --- a/CRM/Upgrade/Steps.php +++ b/CRM/Upgrade/Steps.php @@ -26,11 +26,12 @@ */ /** - * * @package CRM * @copyright CiviCRM LLC (c) 2004-2015 * $Id$ * + * The `CRM_Upgrade_Steps` class tracks upgrade files (eg `CRM/Upgrade/Steps/*.up.php`). + * It can report a list of pending upgrade steps and toggle the "pending" flag. */ class CRM_Upgrade_Steps { @@ -53,6 +54,9 @@ class CRM_Upgrade_Steps { * Array(string $fullPath => string $classPrefix). */ public function __construct($module, $paths = array()) { + if (!self::findCreateTable()) { + CRM_Core_Error::fatal(ts('Failed to find or create upgrade table')); + } $this->module = $module; $this->paths = $paths; } @@ -98,7 +102,7 @@ public function getAllObjects() { public function getPendingObjects() { $result = array(); foreach ($this->getAllClasses() as $file => $className) { - if ($this->isPending($className)) { + if (!$this->isExecuted($className)) { $result[] = new $className(); } } @@ -106,11 +110,40 @@ public function getPendingObjects() { } /** - * @param string $className + * Determine whether the step has been executed before. + * + * @param string $name * @return bool */ - public function isPending($className) { - return TRUE; + public function isExecuted($name) { + return (bool) CRM_Core_DAO::singleValueQuery( + 'SELECT count(*) FROM civicrm_upgrade WHERE module = %1 AND name = %2', + array( + 1 => array($this->module, 'String'), + 2 => array($name, 'String'), + ) + ); + } + + /** + * Specify whether the step has been executed before. + * + * @param string $name + * @param bool $value + * @return $this + */ + public function setExecuted($name, $value) { + $sqlParams = array( + 1 => array($this->module, 'String'), + 2 => array($name, 'String'), + ); + if ($value) { + CRM_Core_DAO::executeQuery('INSERT IGNORE INTO civicrm_upgrade (module, name) VALUES (%1,%2)', $sqlParams); + } + else { + CRM_Core_DAO::executeQuery('DELETE FROM civicrm_upgrade WHERE module = %1 AND name = %2', $sqlParams); + } + return $this; } /** @@ -133,4 +166,27 @@ protected function toClassName($absPath, $classPrefix, $absFile) { return $classPrefix . strtr($relFile, '/', '_'); } + /** + * Ensure that the required SQL table exists. + * + * @return bool + * TRUE if table now exists + */ + private static function findCreateTable() { + $checkTableSql = "show tables like 'civicrm_upgrade'"; + $foundName = CRM_Core_DAO::singleValueQuery($checkTableSql); + if ($foundName == 'civicrm_upgrade') { + return TRUE; + } + + $fileName = dirname(__FILE__) . '/../../sql/civicrm_upgrade.mysql'; + + $config = CRM_Core_Config::singleton(); + CRM_Utils_File::sourceSQLFile($config->dsn, $fileName); + + // Make sure it succeeded + $foundName = CRM_Core_DAO::singleValueQuery($checkTableSql); + return ($foundName == 'civicrm_upgrade'); + } + } From bda7dd33226d29c3179776105c47033d4a8750b4 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Sun, 13 Dec 2015 20:45:04 -0800 Subject: [PATCH 15/15] CRM-16860 - CRM_Upgrade_Steps - Identify *.mysql.tpl files --- CRM/Upgrade/Incremental/SqlStep.php | 77 +++++++++++++++++++++++++++++ CRM/Upgrade/Steps.php | 45 ++++++++--------- 2 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 CRM/Upgrade/Incremental/SqlStep.php diff --git a/CRM/Upgrade/Incremental/SqlStep.php b/CRM/Upgrade/Incremental/SqlStep.php new file mode 100644 index 000000000000..96b99be5fe8f --- /dev/null +++ b/CRM/Upgrade/Incremental/SqlStep.php @@ -0,0 +1,77 @@ +file = $file; + $this->name = $name; + } + + public function createPreUpgradeMessage($startVer, $endVer) { + return NULL; + } + + public function getName() { + return $this->name; + } + + public function buildQueue(CRM_Queue_Queue $queue, $postUpgradeMessageFile, $startVer, $endVer) { + $task = new CRM_Queue_Task( + array('CRM_Upgrade_Incremental_SqlStep', 'doSqlFile'), + array($this->file), + ts('Execute SQL: %1', array( + 1 => $this->file, + )) + ); + $queue->createItem($task); + } + + public static function doSqlFile(CRM_Queue_TaskContext $ctx, $sqlFile) { + $upgrade = new CRM_Upgrade_Form(); + // FIXME: Multilingual and $rev + // $upgrade->setSchemaStructureTables($rev); + // $upgrade->processLocales($sqlFile, $rev); + // return TRUE; + throw new RuntimeException(sprintf("Not implemented: %s::%s for %s", __CLASS__, __FUNCTION__, $sqlFile)); + } + +} diff --git a/CRM/Upgrade/Steps.php b/CRM/Upgrade/Steps.php index d31617c31a96..6eede0608123 100644 --- a/CRM/Upgrade/Steps.php +++ b/CRM/Upgrade/Steps.php @@ -30,8 +30,9 @@ * @copyright CiviCRM LLC (c) 2004-2015 * $Id$ * - * The `CRM_Upgrade_Steps` class tracks upgrade files (eg `CRM/Upgrade/Steps/*.up.php`). - * It can report a list of pending upgrade steps and toggle the "pending" flag. + * The `CRM_Upgrade_Steps` class tracks upgrade files (eg `CRM/Upgrade/Steps/*.up.php` + * or `CRM/Upgrade/Steps/*.mysql.tpl`). It can report a list of pending upgrade steps + * and toggle the "executed" flag. */ class CRM_Upgrade_Steps { @@ -62,18 +63,25 @@ public function __construct($module, $paths = array()) { } /** - * Get a list of all upgrade steps (as class names). + * Get a list of all upgrade steps (as objects/instances). * * @return array - * Array(string $file => string $className). + * Array(CRM_Upgrade_Incremental_Interface). */ - public function getAllClasses() { + public function getAllObjects() { $result = array(); foreach ($this->paths as $path => $classPrefix) { if (is_dir($path)) { - $files = CRM_Utils_File::findFiles($path, '*.up.php'); - foreach ($files as $file) { - $result[$file] = $this->toClassName($path, $classPrefix, $file); + $phpFiles = CRM_Utils_File::findFiles($path, '*.up.php'); + foreach ($phpFiles as $phpFile) { + $className = $this->toClassName($path, $classPrefix, $phpFile); + $result[$phpFile] = new $className(); + } + $sqlFiles = CRM_Utils_File::findFiles($path, '*.mysql.tpl'); + foreach ($sqlFiles as $sqlFile) { + $result[$sqlFile] = new CRM_Upgrade_Incremental_SqlStep($sqlFile, + 'SqlStep:' . CRM_Utils_File::relativize($sqlFile, CRM_Utils_File::addTrailingSlash($path)) + ); } } } @@ -81,29 +89,16 @@ public function getAllClasses() { return $result; } - /** - * Get a list of all upgrade steps (as objects/instances). - * - * @return array - * Array(CRM_Upgrade_Incremental_Interface). - */ - public function getAllObjects() { - $result = array(); - foreach ($this->getAllClasses() as $file => $className) { - $result[$className] = new $className(); - } - return $result; - } - /** * @return array * Array(CRM_Upgrade_Incremental_Interface). */ public function getPendingObjects() { $result = array(); - foreach ($this->getAllClasses() as $file => $className) { - if (!$this->isExecuted($className)) { - $result[] = new $className(); + foreach ($this->getAllObjects() as $upgrade) { + /** @var CRM_Upgrade_Incremental_Interface $upgrade */ + if (!$this->isExecuted($upgrade->getName())) { + $result[] = $upgrade; } } return $result;