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'); + } + }