Merge pull request #21374 from ufundo/event-custom-tokens
[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 ]);
6a488035 64
ce9781c5
TO
65 foreach (self::getActiveUpgraders() as $upgrader) {
66 /** @var \CRM_Extension_Upgrader_Interface $upgrader */
67 $upgrader->notify('upgrade', ['enqueue', $queue]);
68 }
69
6a488035
TO
70 CRM_Utils_Hook::upgrade('enqueue', $queue);
71
2bbbd9b2
SL
72 // dev/core#1618 When Extension Upgrades are run reconcile log tables
73 $task = new CRM_Queue_Task(
74 [__CLASS__, 'upgradeLogTables'],
75 [],
76 ts('Update log tables')
77 );
78 // Set weight low so that it will be run last.
79 $queue->createItem($task, -2);
80
6a488035
TO
81 return $queue;
82 }
83
2bbbd9b2
SL
84 /**
85 * Update log tables following execution of extension upgrades
86 */
87 public static function upgradeLogTables() {
88 $logging = new CRM_Logging_Schema();
89 $logging->fixSchemaDifferences();
90 return TRUE;
91 }
92
ce9781c5
TO
93 /**
94 * @return array
95 * Array(string $extKey => CRM_Extension_Upgrader_Interface $upgrader)
96 */
97 protected static function getActiveUpgraders() {
98 $mapper = \CRM_Extension_System::singleton()->getMapper();
99 $keys = self::getActiveKeys();
100
101 $upgraders = [];
102 foreach ($keys as $key) {
103 $upgrader = $mapper->getUpgrader($key);
104 if ($upgrader !== NULL) {
105 $upgraders[$key] = $upgrader;
106 }
107 }
108 return $upgraders;
109 }
110
111 /**
112 * @return string[]
113 */
114 protected static function getActiveKeys() {
115 $mapper = \CRM_Extension_System::singleton()->getMapper();
116 try {
117 return self::sortKeys(array_column($mapper->getActiveModuleFiles(), 'fullName'));
118 }
119 catch (CircularDependencyException $e) {
120 CRM_Core_Error::debug_log_message("Failed to identify extensions. Circular dependency. " . $e->getMessage());
121 return [];
122 }
123 catch (ElementNotFoundException $e) {
124 CRM_Core_Error::debug_log_message("Failed to identify extensions. Unrecognized dependency. " . $e->getMessage());
125 return [];
126 }
127 }
128
129 /**
130 * @param string[] $keys
131 *
132 * @return string[]
133 * @throws \CRM_Extension_Exception
134 * @throws \MJS\TopSort\CircularDependencyException
135 * @throws \MJS\TopSort\ElementNotFoundException
136 */
137 protected static function sortKeys($keys) {
138 $infos = CRM_Extension_System::singleton()->getMapper()->getAllInfos();
139
140 // Start with our inputs in a normalized form.
141 $todoKeys = array_unique($keys);
142 sort($todoKeys);
143
144 // Goal: Add all active items to $sorter and flag $doneKeys['org.example.foobar']=1.
145 $doneKeys = [];
146 $sorter = new \MJS\TopSort\Implementations\FixedArraySort();
147
148 while (!empty($todoKeys)) {
149 $key = array_shift($todoKeys);
150 if (isset($doneKeys[$key])) {
151 continue;
152 }
153 $doneKeys[$key] = 1;
154
155 /** @var CRM_Extension_Info $info */
156 $info = @$infos[$key];
157
158 if ($info && $info->requires) {
159 $sorter->add($key, $info->requires);
160 $todoKeys = array_merge($todoKeys, $info->requires);
161 }
162 else {
163 $sorter->add($key, []);
164 }
165 }
166 return $sorter->sort();
167 }
168
6a488035 169}