Resolve docblock inaccuracy in CRM_Contribute_Form_Task_TaskTrait
[civicrm-core.git] / CRM / Extension / Upgrades.php
index 5a008e9fbcce87778211030a59f5c8ca68b0069f..adf3b1f49493501cda107cf59bf5654b2b772694 100644 (file)
@@ -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();
+  }
+
 }