X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FExtension%2FUpgrades.php;h=adf3b1f49493501cda107cf59bf5654b2b772694;hb=56190307aed9afc288bb406b457c9f60c7b35cbf;hp=5a008e9fbcce87778211030a59f5c8ca68b0069f;hpb=2f36a39fa513d1bba497b5a540343d01308c99fb;p=civicrm-core.git diff --git a/CRM/Extension/Upgrades.php b/CRM/Extension/Upgrades.php index 5a008e9fbc..adf3b1f494 100644 --- a/CRM/Extension/Upgrades.php +++ b/CRM/Extension/Upgrades.php @@ -9,6 +9,9 @@ +--------------------------------------------------------------------+ */ +use MJS\TopSort\CircularDependencyException; +use MJS\TopSort\ElementNotFoundException; + /** * This class stores logic for managing schema upgrades in CiviCRM extensions. * @@ -25,16 +28,26 @@ class CRM_Extension_Upgrades { * @return bool */ public static function hasPending() { - $checks = CRM_Utils_Hook::upgrade('check'); - if (is_array($checks)) { - foreach ($checks as $check) { - if ($check) { - return TRUE; + $hasTrue = function($checks) { + if (is_array($checks)) { + foreach ($checks as $check) { + if ($check) { + return TRUE; + } } } + return FALSE; + }; + + foreach (self::getActiveUpgraders() as $upgrader) { + /** @var \CRM_Extension_Upgrader_Interface $upgrader */ + if ($hasTrue($upgrader->notify('upgrade', ['check']))) { + return TRUE; + } } - return FALSE; + $checks = CRM_Utils_Hook::upgrade('check'); + return $hasTrue($checks); } /** @@ -49,9 +62,108 @@ class CRM_Extension_Upgrades { 'reset' => TRUE, ]); + foreach (self::getActiveUpgraders() as $upgrader) { + /** @var \CRM_Extension_Upgrader_Interface $upgrader */ + $upgrader->notify('upgrade', ['enqueue', $queue]); + } + CRM_Utils_Hook::upgrade('enqueue', $queue); + // dev/core#1618 When Extension Upgrades are run reconcile log tables + $task = new CRM_Queue_Task( + [__CLASS__, 'upgradeLogTables'], + [], + ts('Update log tables') + ); + // Set weight low so that it will be run last. + $queue->createItem($task, -2); + return $queue; } + /** + * Update log tables following execution of extension upgrades + */ + public static function upgradeLogTables() { + $logging = new CRM_Logging_Schema(); + $logging->fixSchemaDifferences(); + return TRUE; + } + + /** + * @return array + * Array(string $extKey => CRM_Extension_Upgrader_Interface $upgrader) + */ + protected static function getActiveUpgraders() { + $mapper = \CRM_Extension_System::singleton()->getMapper(); + $keys = self::getActiveKeys(); + + $upgraders = []; + foreach ($keys as $key) { + $upgrader = $mapper->getUpgrader($key); + if ($upgrader !== NULL) { + $upgraders[$key] = $upgrader; + } + } + return $upgraders; + } + + /** + * @return string[] + */ + protected static function getActiveKeys() { + $mapper = \CRM_Extension_System::singleton()->getMapper(); + try { + return self::sortKeys(array_column($mapper->getActiveModuleFiles(), 'fullName')); + } + catch (CircularDependencyException $e) { + CRM_Core_Error::debug_log_message("Failed to identify extensions. Circular dependency. " . $e->getMessage()); + return []; + } + catch (ElementNotFoundException $e) { + CRM_Core_Error::debug_log_message("Failed to identify extensions. Unrecognized dependency. " . $e->getMessage()); + return []; + } + } + + /** + * @param string[] $keys + * + * @return string[] + * @throws \CRM_Extension_Exception + * @throws \MJS\TopSort\CircularDependencyException + * @throws \MJS\TopSort\ElementNotFoundException + */ + protected static function sortKeys($keys) { + $infos = CRM_Extension_System::singleton()->getMapper()->getAllInfos(); + + // Start with our inputs in a normalized form. + $todoKeys = array_unique($keys); + sort($todoKeys); + + // Goal: Add all active items to $sorter and flag $doneKeys['org.example.foobar']=1. + $doneKeys = []; + $sorter = new \MJS\TopSort\Implementations\FixedArraySort(); + + while (!empty($todoKeys)) { + $key = array_shift($todoKeys); + if (isset($doneKeys[$key])) { + continue; + } + $doneKeys[$key] = 1; + + /** @var CRM_Extension_Info $info */ + $info = @$infos[$key]; + + if ($info && $info->requires) { + $sorter->add($key, $info->requires); + $todoKeys = array_merge($todoKeys, $info->requires); + } + else { + $sorter->add($key, []); + } + } + return $sorter->sort(); + } + }