CrmUi - Fix crmSelect2 to work with ngOptions
[civicrm-core.git] / CRM / Extension / Upgrades.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
ce9781c5
TO
12use MJS\TopSort\CircularDependencyException;
13use MJS\TopSort\ElementNotFoundException;
14
6a488035
TO
15/**
16 * This class stores logic for managing schema upgrades in CiviCRM extensions.
17 *
18 * @package CRM
ca5cec67 19 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
20 */
21class CRM_Extension_Upgrades {
22
23 const QUEUE_NAME = 'ext-upgrade';
24
25 /**
fe482240 26 * Determine whether any extensions have pending upgrades.
6a488035
TO
27 *
28 * @return bool
29 */
00be9182 30 public static function hasPending() {
ce9781c5
TO
31 $hasTrue = function($checks) {
32 if (is_array($checks)) {
33 foreach ($checks as $check) {
34 if ($check) {
35 return TRUE;
36 }
6a488035
TO
37 }
38 }
ce9781c5
TO
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 }
6a488035
TO
47 }
48
ce9781c5
TO
49 $checks = CRM_Utils_Hook::upgrade('check');
50 return $hasTrue($checks);
6a488035
TO
51 }
52
53 /**
fe482240 54 * Fill a queue with upgrade tasks.
6a488035
TO
55 *
56 * @return CRM_Queue_Queue
57 */
00be9182 58 public static function createQueue() {
be2fb01f 59 $queue = CRM_Queue_Service::singleton()->create([
6a488035
TO
60 'type' => 'Sql',
61 'name' => self::QUEUE_NAME,
62 'reset' => TRUE,
be2fb01f 63 ]);
9416187d
TO
64 return static::fillQueue($queue);
65 }
6a488035 66
9416187d
TO
67 /**
68 * @param \CRM_Queue_Queue $queue
69 *
70 * @return \CRM_Queue_Queue
71 */
72 public static function fillQueue(CRM_Queue_Queue $queue): CRM_Queue_Queue {
ce9781c5
TO
73 foreach (self::getActiveUpgraders() as $upgrader) {
74 /** @var \CRM_Extension_Upgrader_Interface $upgrader */
75 $upgrader->notify('upgrade', ['enqueue', $queue]);
76 }
77
6a488035
TO
78 CRM_Utils_Hook::upgrade('enqueue', $queue);
79
2bbbd9b2
SL
80 // dev/core#1618 When Extension Upgrades are run reconcile log tables
81 $task = new CRM_Queue_Task(
82 [__CLASS__, 'upgradeLogTables'],
83 [],
84 ts('Update log tables')
85 );
86 // Set weight low so that it will be run last.
87 $queue->createItem($task, -2);
88
6a488035
TO
89 return $queue;
90 }
91
2bbbd9b2
SL
92 /**
93 * Update log tables following execution of extension upgrades
94 */
95 public static function upgradeLogTables() {
96 $logging = new CRM_Logging_Schema();
97 $logging->fixSchemaDifferences();
98 return TRUE;
99 }
100
ce9781c5
TO
101 /**
102 * @return array
103 * Array(string $extKey => CRM_Extension_Upgrader_Interface $upgrader)
104 */
105 protected static function getActiveUpgraders() {
106 $mapper = \CRM_Extension_System::singleton()->getMapper();
107 $keys = self::getActiveKeys();
108
109 $upgraders = [];
110 foreach ($keys as $key) {
111 $upgrader = $mapper->getUpgrader($key);
112 if ($upgrader !== NULL) {
113 $upgraders[$key] = $upgrader;
114 }
115 }
116 return $upgraders;
117 }
118
119 /**
120 * @return string[]
121 */
122 protected static function getActiveKeys() {
123 $mapper = \CRM_Extension_System::singleton()->getMapper();
124 try {
125 return self::sortKeys(array_column($mapper->getActiveModuleFiles(), 'fullName'));
126 }
127 catch (CircularDependencyException $e) {
128 CRM_Core_Error::debug_log_message("Failed to identify extensions. Circular dependency. " . $e->getMessage());
129 return [];
130 }
131 catch (ElementNotFoundException $e) {
132 CRM_Core_Error::debug_log_message("Failed to identify extensions. Unrecognized dependency. " . $e->getMessage());
133 return [];
134 }
135 }
136
137 /**
13182e01
CW
138 * Sorts active extensions according to their dependencies
139 *
ce9781c5 140 * @param string[] $keys
13182e01 141 * Names of all active modules
ce9781c5
TO
142 *
143 * @return string[]
144 * @throws \CRM_Extension_Exception
145 * @throws \MJS\TopSort\CircularDependencyException
146 * @throws \MJS\TopSort\ElementNotFoundException
147 */
148 protected static function sortKeys($keys) {
149 $infos = CRM_Extension_System::singleton()->getMapper()->getAllInfos();
150
13182e01 151 // Ensure a stable starting order.
ce9781c5
TO
152 $todoKeys = array_unique($keys);
153 sort($todoKeys);
154
ce9781c5
TO
155 $sorter = new \MJS\TopSort\Implementations\FixedArraySort();
156
13182e01 157 foreach ($todoKeys as $key) {
ce9781c5 158 /** @var CRM_Extension_Info $info */
13182e01 159 $info = $infos[$key] ?? NULL;
ce9781c5 160
13182e01
CW
161 // Add dependencies
162 if ($info) {
163 // Filter out missing dependencies; missing modules cannot be upgraded
164 $requires = array_intersect($info->requires ?? [], $keys);
165 $sorter->add($key, $requires);
ce9781c5 166 }
13182e01 167 // This shouldn't ever happen if this function is being passed a list of active extensions.
ce9781c5 168 else {
13182e01 169 throw new CRM_Extension_Exception('Invalid extension key: "' . $key . '"');
ce9781c5
TO
170 }
171 }
172 return $sorter->sort();
173 }
174
6a488035 175}