Merge pull request #20092 from larssandergreen/fix-is-recur-label
[civicrm-core.git] / CRM / Extension / Upgrades.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 use MJS\TopSort\CircularDependencyException;
13 use MJS\TopSort\ElementNotFoundException;
14
15 /**
16 * This class stores logic for managing schema upgrades in CiviCRM extensions.
17 *
18 * @package CRM
19 * @copyright CiviCRM LLC https://civicrm.org/licensing
20 */
21 class CRM_Extension_Upgrades {
22
23 const QUEUE_NAME = 'ext-upgrade';
24
25 /**
26 * Determine whether any extensions have pending upgrades.
27 *
28 * @return bool
29 */
30 public static function hasPending() {
31 $hasTrue = function($checks) {
32 if (is_array($checks)) {
33 foreach ($checks as $check) {
34 if ($check) {
35 return TRUE;
36 }
37 }
38 }
39 return FALSE;
40 };
41
42 foreach (self::getActiveUpgraders() as $upgrader) {
43 /** @var \CRM_Extension_Upgrader_Interface $upgrader */
44 if ($hasTrue($upgrader->notify('upgrade', ['check']))) {
45 return TRUE;
46 }
47 }
48
49 $checks = CRM_Utils_Hook::upgrade('check');
50 return $hasTrue($checks);
51 }
52
53 /**
54 * Fill a queue with upgrade tasks.
55 *
56 * @return CRM_Queue_Queue
57 */
58 public static function createQueue() {
59 $queue = CRM_Queue_Service::singleton()->create([
60 'type' => 'Sql',
61 'name' => self::QUEUE_NAME,
62 'reset' => TRUE,
63 ]);
64
65 foreach (self::getActiveUpgraders() as $upgrader) {
66 /** @var \CRM_Extension_Upgrader_Interface $upgrader */
67 $upgrader->notify('upgrade', ['enqueue', $queue]);
68 }
69
70 CRM_Utils_Hook::upgrade('enqueue', $queue);
71
72 return $queue;
73 }
74
75 /**
76 * @return array
77 * Array(string $extKey => CRM_Extension_Upgrader_Interface $upgrader)
78 */
79 protected static function getActiveUpgraders() {
80 $mapper = \CRM_Extension_System::singleton()->getMapper();
81 $keys = self::getActiveKeys();
82
83 $upgraders = [];
84 foreach ($keys as $key) {
85 $upgrader = $mapper->getUpgrader($key);
86 if ($upgrader !== NULL) {
87 $upgraders[$key] = $upgrader;
88 }
89 }
90 return $upgraders;
91 }
92
93 /**
94 * @return string[]
95 */
96 protected static function getActiveKeys() {
97 $mapper = \CRM_Extension_System::singleton()->getMapper();
98 try {
99 return self::sortKeys(array_column($mapper->getActiveModuleFiles(), 'fullName'));
100 }
101 catch (CircularDependencyException $e) {
102 CRM_Core_Error::debug_log_message("Failed to identify extensions. Circular dependency. " . $e->getMessage());
103 return [];
104 }
105 catch (ElementNotFoundException $e) {
106 CRM_Core_Error::debug_log_message("Failed to identify extensions. Unrecognized dependency. " . $e->getMessage());
107 return [];
108 }
109 }
110
111 /**
112 * @param string[] $keys
113 *
114 * @return string[]
115 * @throws \CRM_Extension_Exception
116 * @throws \MJS\TopSort\CircularDependencyException
117 * @throws \MJS\TopSort\ElementNotFoundException
118 */
119 protected static function sortKeys($keys) {
120 $infos = CRM_Extension_System::singleton()->getMapper()->getAllInfos();
121
122 // Start with our inputs in a normalized form.
123 $todoKeys = array_unique($keys);
124 sort($todoKeys);
125
126 // Goal: Add all active items to $sorter and flag $doneKeys['org.example.foobar']=1.
127 $doneKeys = [];
128 $sorter = new \MJS\TopSort\Implementations\FixedArraySort();
129
130 while (!empty($todoKeys)) {
131 $key = array_shift($todoKeys);
132 if (isset($doneKeys[$key])) {
133 continue;
134 }
135 $doneKeys[$key] = 1;
136
137 /** @var CRM_Extension_Info $info */
138 $info = @$infos[$key];
139
140 if ($info && $info->requires) {
141 $sorter->add($key, $info->requires);
142 $todoKeys = array_merge($todoKeys, $info->requires);
143 }
144 else {
145 $sorter->add($key, []);
146 }
147 }
148 return $sorter->sort();
149 }
150
151 }