Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | ||
a092536e EM |
3 | use Civi\Api4\Managed; |
4 | ||
6a488035 TO |
5 | /** |
6 | * The ManagedEntities system allows modules to add records to the database | |
7 | * declaratively. Those records will be automatically inserted, updated, | |
8 | * deactivated, and deleted in tandem with their modules. | |
9 | */ | |
10 | class CRM_Core_ManagedEntities { | |
1f103dc4 | 11 | |
4b62bc4f EM |
12 | /** |
13 | * Get clean up options. | |
14 | * | |
15 | * @return array | |
16 | */ | |
1f103dc4 | 17 | public static function getCleanupOptions() { |
be2fb01f | 18 | return [ |
1f103dc4 TO |
19 | 'always' => ts('Always'), |
20 | 'never' => ts('Never'), | |
21 | 'unused' => ts('If Unused'), | |
be2fb01f | 22 | ]; |
1f103dc4 TO |
23 | } |
24 | ||
6a488035 | 25 | /** |
88718db2 TO |
26 | * @var array |
27 | * Array($status => array($name => CRM_Core_Module)). | |
6a488035 | 28 | */ |
e9b95545 | 29 | protected $moduleIndex; |
6a488035 | 30 | |
a092536e EM |
31 | /** |
32 | * Actions arising from the managed entities. | |
33 | * | |
34 | * @var array | |
35 | */ | |
36 | protected $managedActions = []; | |
37 | ||
6a488035 | 38 | /** |
88718db2 TO |
39 | * @var array |
40 | * List of all entity declarations. | |
41 | * @see CRM_Utils_Hook::managed() | |
6a488035 | 42 | */ |
e9b95545 | 43 | protected $declarations; |
6a488035 TO |
44 | |
45 | /** | |
d09edf64 | 46 | * Get an instance. |
ba3228d1 EM |
47 | * @param bool $fresh |
48 | * @return \CRM_Core_ManagedEntities | |
6a488035 TO |
49 | */ |
50 | public static function singleton($fresh = FALSE) { | |
51 | static $singleton; | |
52 | if ($fresh || !$singleton) { | |
8cb5ca10 | 53 | $singleton = new CRM_Core_ManagedEntities(CRM_Core_Module::getAll()); |
6a488035 TO |
54 | } |
55 | return $singleton; | |
56 | } | |
57 | ||
9c19292b TO |
58 | /** |
59 | * Perform an asynchronous reconciliation when the transaction ends. | |
60 | */ | |
ba3228d1 | 61 | public static function scheduleReconciliation() { |
9c19292b TO |
62 | CRM_Core_Transaction::addCallback( |
63 | CRM_Core_Transaction::PHASE_POST_COMMIT, | |
64 | function () { | |
e70a7fc0 | 65 | CRM_Core_ManagedEntities::singleton(TRUE)->reconcile(); |
9c19292b | 66 | }, |
be2fb01f | 67 | [], |
9c19292b TO |
68 | 'ManagedEntities::reconcile' |
69 | ); | |
70 | } | |
71 | ||
6a488035 | 72 | /** |
6a0b768e TO |
73 | * @param array $modules |
74 | * CRM_Core_Module. | |
6a488035 | 75 | */ |
8cb5ca10 | 76 | public function __construct(array $modules) { |
85917c3b | 77 | $this->moduleIndex = $this->createModuleIndex($modules); |
6a488035 TO |
78 | } |
79 | ||
80 | /** | |
88718db2 | 81 | * Read a managed entity using APIv3. |
378e2654 | 82 | * |
a092536e EM |
83 | * @deprecated |
84 | * | |
88718db2 TO |
85 | * @param string $moduleName |
86 | * The name of the module which declared entity. | |
87 | * @param string $name | |
88 | * The symbolic name of the entity. | |
72b3a70c CW |
89 | * @return array|NULL |
90 | * API representation, or NULL if the entity does not exist | |
6a488035 TO |
91 | */ |
92 | public function get($moduleName, $name) { | |
93 | $dao = new CRM_Core_DAO_Managed(); | |
94 | $dao->module = $moduleName; | |
95 | $dao->name = $name; | |
96 | if ($dao->find(TRUE)) { | |
be2fb01f | 97 | $params = [ |
6a488035 | 98 | 'id' => $dao->entity_id, |
be2fb01f | 99 | ]; |
bbf66e9c | 100 | $result = NULL; |
637ea2cf E |
101 | try { |
102 | $result = civicrm_api3($dao->entity_type, 'getsingle', $params); | |
103 | } | |
104 | catch (Exception $e) { | |
e9b95545 | 105 | $this->onApiError($dao->entity_type, 'getsingle', $params, $result); |
6a488035 | 106 | } |
637ea2cf | 107 | return $result; |
0db6c3e1 TO |
108 | } |
109 | else { | |
6a488035 TO |
110 | return NULL; |
111 | } | |
112 | } | |
113 | ||
88718db2 TO |
114 | /** |
115 | * Identify any enabled/disabled modules. Add new entities, update | |
116 | * existing entities, and remove orphaned (stale) entities. | |
8cb5ca10 | 117 | * |
912511a3 | 118 | * @param bool $ignoreUpgradeMode |
88718db2 | 119 | * |
8cb5ca10 | 120 | * @throws \CRM_Core_Exception |
88718db2 | 121 | */ |
912511a3 SL |
122 | public function reconcile($ignoreUpgradeMode = FALSE) { |
123 | // Do not reconcile whilst we are in upgrade mode | |
124 | if (CRM_Core_Config::singleton()->isUpgradeMode() && !$ignoreUpgradeMode) { | |
125 | return; | |
126 | } | |
f9ee37c9 | 127 | $this->loadDeclarations(); |
e9b95545 | 128 | if ($error = $this->validate($this->getDeclarations())) { |
8cb5ca10 | 129 | throw new CRM_Core_Exception($error); |
6a488035 | 130 | } |
a092536e | 131 | $this->loadManagedEntityActions(); |
6a488035 TO |
132 | $this->reconcileEnabledModules(); |
133 | $this->reconcileDisabledModules(); | |
134 | $this->reconcileUnknownModules(); | |
135 | } | |
136 | ||
88718db2 TO |
137 | /** |
138 | * For all enabled modules, add new entities, update | |
139 | * existing entities, and remove orphaned (stale) entities. | |
88718db2 | 140 | */ |
5c095937 | 141 | protected function reconcileEnabledModules(): void { |
6a488035 TO |
142 | // Note: any thing currently declared is necessarily from |
143 | // an active module -- because we got it from a hook! | |
144 | ||
145 | // index by moduleName,name | |
85917c3b | 146 | $decls = $this->createDeclarationIndex($this->moduleIndex, $this->getDeclarations()); |
6a488035 | 147 | foreach ($decls as $moduleName => $todos) { |
9cf68fcc | 148 | if ($this->isModuleEnabled($moduleName)) { |
a092536e | 149 | $this->reconcileEnabledModule($moduleName); |
0db6c3e1 | 150 | } |
6a488035 TO |
151 | } |
152 | } | |
153 | ||
154 | /** | |
88718db2 TO |
155 | * For one enabled module, add new entities, update existing entities, |
156 | * and remove orphaned (stale) entities. | |
6a488035 | 157 | * |
a092536e | 158 | * @param string $module |
6a488035 | 159 | */ |
5c095937 | 160 | protected function reconcileEnabledModule(string $module): void { |
a092536e EM |
161 | foreach ($this->getManagedEntitiesToUpdate(['module' => $module]) as $todo) { |
162 | $dao = new CRM_Core_DAO_Managed(); | |
163 | $dao->module = $todo['module']; | |
164 | $dao->name = $todo['name']; | |
165 | $dao->entity_type = $todo['entity_type']; | |
166 | $dao->entity_id = $todo['entity_id']; | |
167 | $dao->id = $todo['id']; | |
168 | $this->updateExistingEntity($dao, $todo); | |
6a488035 TO |
169 | } |
170 | ||
a092536e EM |
171 | foreach ($this->getManagedEntitiesToDelete(['module' => $module]) as $todo) { |
172 | $dao = new CRM_Core_DAO_Managed(); | |
173 | $dao->module = $todo['module']; | |
174 | $dao->name = $todo['name']; | |
175 | $dao->entity_type = $todo['entity_type']; | |
176 | $dao->id = $todo['id']; | |
177 | $dao->cleanup = $todo['cleanup']; | |
178 | $dao->entity_id = $todo['entity_id']; | |
179 | $this->removeStaleEntity($dao); | |
180 | } | |
181 | foreach ($this->getManagedEntitiesToCreate(['module' => $module]) as $todo) { | |
7cddb4ae | 182 | $this->insertNewEntity($todo); |
6a488035 TO |
183 | } |
184 | } | |
185 | ||
a092536e EM |
186 | /** |
187 | * Get the managed entities to be created. | |
188 | * | |
189 | * @param array $filters | |
190 | * | |
191 | * @return array | |
192 | */ | |
193 | protected function getManagedEntitiesToCreate(array $filters = []): array { | |
194 | return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'create'])); | |
195 | } | |
196 | ||
197 | /** | |
198 | * Get the managed entities to be created. | |
199 | * | |
200 | * @param array $filters | |
201 | * | |
202 | * @return array | |
203 | */ | |
204 | protected function getManagedEntitiesToUpdate(array $filters = []): array { | |
205 | return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'update'])); | |
206 | } | |
207 | ||
208 | /** | |
209 | * Get the managed entities to be deleted. | |
210 | * | |
211 | * @param array $filters | |
212 | * | |
213 | * @return array | |
214 | */ | |
215 | protected function getManagedEntitiesToDelete(array $filters = []): array { | |
216 | return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'delete'])); | |
217 | } | |
218 | ||
219 | /** | |
220 | * Get the managed entities that fit the criteria. | |
221 | * | |
222 | * @param array $filters | |
223 | * | |
224 | * @return array | |
225 | */ | |
226 | protected function getManagedEntities(array $filters = []): array { | |
227 | $return = []; | |
228 | foreach ($this->managedActions as $actionKey => $action) { | |
229 | foreach ($filters as $filterKey => $filterValue) { | |
230 | if ($action[$filterKey] !== $filterValue) { | |
231 | continue 2; | |
232 | } | |
233 | } | |
234 | $return[$actionKey] = $action; | |
235 | } | |
236 | return $return; | |
237 | } | |
238 | ||
88718db2 TO |
239 | /** |
240 | * For all disabled modules, disable any managed entities. | |
241 | */ | |
5c095937 | 242 | protected function reconcileDisabledModules() { |
6a488035 TO |
243 | if (empty($this->moduleIndex[FALSE])) { |
244 | return; | |
245 | } | |
246 | ||
247 | $in = CRM_Core_DAO::escapeStrings(array_keys($this->moduleIndex[FALSE])); | |
248 | $dao = new CRM_Core_DAO_Managed(); | |
249 | $dao->whereAdd("module in ($in)"); | |
04b08baa | 250 | $dao->orderBy('id DESC'); |
6a488035 TO |
251 | $dao->find(); |
252 | while ($dao->fetch()) { | |
7cddb4ae TO |
253 | $this->disableEntity($dao); |
254 | ||
6a488035 TO |
255 | } |
256 | } | |
257 | ||
88718db2 TO |
258 | /** |
259 | * Remove any orphaned (stale) entities that are linked to | |
260 | * unknown modules. | |
261 | */ | |
5c095937 | 262 | protected function reconcileUnknownModules() { |
be2fb01f | 263 | $knownModules = []; |
6a488035 TO |
264 | if (array_key_exists(0, $this->moduleIndex) && is_array($this->moduleIndex[0])) { |
265 | $knownModules = array_merge($knownModules, array_keys($this->moduleIndex[0])); | |
266 | } | |
267 | if (array_key_exists(1, $this->moduleIndex) && is_array($this->moduleIndex[1])) { | |
268 | $knownModules = array_merge($knownModules, array_keys($this->moduleIndex[1])); | |
6a488035 TO |
269 | } |
270 | ||
271 | $dao = new CRM_Core_DAO_Managed(); | |
272 | if (!empty($knownModules)) { | |
273 | $in = CRM_Core_DAO::escapeStrings($knownModules); | |
274 | $dao->whereAdd("module NOT IN ($in)"); | |
04b08baa | 275 | $dao->orderBy('id DESC'); |
6a488035 TO |
276 | } |
277 | $dao->find(); | |
278 | while ($dao->fetch()) { | |
bbf66e9c TO |
279 | $this->removeStaleEntity($dao); |
280 | } | |
281 | } | |
6a488035 | 282 | |
7cddb4ae | 283 | /** |
88718db2 | 284 | * Create a new entity. |
7cddb4ae | 285 | * |
6a0b768e TO |
286 | * @param array $todo |
287 | * Entity specification (per hook_civicrm_managedEntities). | |
7cddb4ae | 288 | */ |
d80c6631 | 289 | protected function insertNewEntity($todo) { |
a092536e | 290 | $result = civicrm_api($todo['entity_type'], 'create', $todo['params']); |
ef902ada | 291 | if (!empty($result['is_error'])) { |
a092536e | 292 | $this->onApiError($todo['entity_type'], 'create', $todo['params'], $result); |
7cddb4ae TO |
293 | } |
294 | ||
295 | $dao = new CRM_Core_DAO_Managed(); | |
296 | $dao->module = $todo['module']; | |
297 | $dao->name = $todo['name']; | |
a092536e | 298 | $dao->entity_type = $todo['entity_type']; |
be76e704 | 299 | // A fatal error will result if there is no valid id but if |
300 | // this is v4 api we might need to access it via ->first(). | |
301 | $dao->entity_id = $result['id'] ?? $result->first()['id']; | |
9c1bc317 | 302 | $dao->cleanup = $todo['cleanup'] ?? NULL; |
7cddb4ae TO |
303 | $dao->save(); |
304 | } | |
305 | ||
306 | /** | |
20429eb9 | 307 | * Update an entity which is believed to exist. |
7cddb4ae TO |
308 | * |
309 | * @param CRM_Core_DAO_Managed $dao | |
6a0b768e TO |
310 | * @param array $todo |
311 | * Entity specification (per hook_civicrm_managedEntities). | |
7cddb4ae | 312 | */ |
5c095937 | 313 | protected function updateExistingEntity($dao, $todo) { |
0dd54586 | 314 | $policy = CRM_Utils_Array::value('update', $todo, 'always'); |
35c1c211 | 315 | $doUpdate = ($policy === 'always'); |
0dd54586 TO |
316 | |
317 | if ($doUpdate) { | |
3cf02556 TO |
318 | $defaults = ['id' => $dao->entity_id]; |
319 | if ($this->isActivationSupported($dao->entity_type)) { | |
320 | $defaults['is_active'] = 1; | |
321 | } | |
0dd54586 | 322 | $params = array_merge($defaults, $todo['params']); |
20429eb9 RL |
323 | |
324 | $manager = CRM_Extension_System::singleton()->getManager(); | |
325 | if ($dao->entity_type === 'Job' && !$manager->extensionIsBeingInstalledOrEnabled($dao->module)) { | |
326 | // Special treatment for scheduled jobs: | |
327 | // | |
328 | // If we're being called as part of enabling/installing a module then | |
329 | // we want the default behaviour of setting is_active = 1. | |
330 | // | |
331 | // However, if we're just being called by a normal cache flush then we | |
332 | // should not re-enable a job that an administrator has decided to disable. | |
333 | // | |
334 | // Without this logic there was a problem: site admin might disable | |
335 | // a job, but then when there was a flush op, the job was re-enabled | |
336 | // which can cause significant embarrassment, depending on the job | |
337 | // ("Don't worry, sending mailings is disabled right now..."). | |
338 | unset($params['is_active']); | |
339 | } | |
340 | ||
0dd54586 TO |
341 | $result = civicrm_api($dao->entity_type, 'create', $params); |
342 | if ($result['is_error']) { | |
353ffa53 | 343 | $this->onApiError($dao->entity_type, 'create', $params, $result); |
0dd54586 | 344 | } |
7cddb4ae | 345 | } |
1f103dc4 TO |
346 | |
347 | if (isset($todo['cleanup'])) { | |
348 | $dao->cleanup = $todo['cleanup']; | |
349 | $dao->update(); | |
350 | } | |
7cddb4ae TO |
351 | } |
352 | ||
353 | /** | |
354 | * Update an entity which (a) is believed to exist and which (b) ought to be | |
355 | * inactive. | |
356 | * | |
357 | * @param CRM_Core_DAO_Managed $dao | |
8b91d849 | 358 | * |
359 | * @throws \CiviCRM_API3_Exception | |
7cddb4ae | 360 | */ |
d80c6631 | 361 | protected function disableEntity($dao): void { |
fc625166 TO |
362 | $entity_type = $dao->entity_type; |
363 | if ($this->isActivationSupported($entity_type)) { | |
7cddb4ae | 364 | // FIXME cascading for payproc types? |
be2fb01f | 365 | $params = [ |
7cddb4ae TO |
366 | 'version' => 3, |
367 | 'id' => $dao->entity_id, | |
368 | 'is_active' => 0, | |
be2fb01f | 369 | ]; |
7cddb4ae TO |
370 | $result = civicrm_api($dao->entity_type, 'create', $params); |
371 | if ($result['is_error']) { | |
353ffa53 | 372 | $this->onApiError($dao->entity_type, 'create', $params, $result); |
7cddb4ae TO |
373 | } |
374 | } | |
375 | } | |
376 | ||
bbf66e9c | 377 | /** |
88718db2 | 378 | * Remove a stale entity (if policy allows). |
bbf66e9c TO |
379 | * |
380 | * @param CRM_Core_DAO_Managed $dao | |
a092536e | 381 | * @throws CRM_Core_Exception |
bbf66e9c | 382 | */ |
d80c6631 | 383 | protected function removeStaleEntity($dao) { |
1f103dc4 | 384 | $policy = empty($dao->cleanup) ? 'always' : $dao->cleanup; |
378e2654 TO |
385 | switch ($policy) { |
386 | case 'always': | |
387 | $doDelete = TRUE; | |
388 | break; | |
ea100cb5 | 389 | |
378e2654 TO |
390 | case 'never': |
391 | $doDelete = FALSE; | |
392 | break; | |
ea100cb5 | 393 | |
378e2654 | 394 | case 'unused': |
be2fb01f | 395 | $getRefCount = civicrm_api3($dao->entity_type, 'getrefcount', [ |
378e2654 | 396 | 'debug' => 1, |
21dfd5f5 | 397 | 'id' => $dao->entity_id, |
be2fb01f | 398 | ]); |
378e2654 TO |
399 | |
400 | $total = 0; | |
401 | foreach ($getRefCount['values'] as $refCount) { | |
402 | $total += $refCount['count']; | |
403 | } | |
404 | ||
405 | $doDelete = ($total == 0); | |
406 | break; | |
ea100cb5 | 407 | |
378e2654 | 408 | default: |
a092536e | 409 | throw new CRM_Core_Exception('Unrecognized cleanup policy: ' . $policy); |
378e2654 | 410 | } |
bbf66e9c | 411 | |
1f103dc4 | 412 | if ($doDelete) { |
be2fb01f | 413 | $params = [ |
1f103dc4 TO |
414 | 'version' => 3, |
415 | 'id' => $dao->entity_id, | |
be2fb01f | 416 | ]; |
a60c0bc8 SL |
417 | $check = civicrm_api3($dao->entity_type, 'get', $params); |
418 | if ((bool) $check['count']) { | |
419 | $result = civicrm_api($dao->entity_type, 'delete', $params); | |
420 | if ($result['is_error']) { | |
9f4f065a MW |
421 | if (isset($dao->name)) { |
422 | $params['name'] = $dao->name; | |
423 | } | |
a60c0bc8 SL |
424 | $this->onApiError($dao->entity_type, 'delete', $params, $result); |
425 | } | |
a60c0bc8 | 426 | } |
be2fb01f CW |
427 | CRM_Core_DAO::executeQuery('DELETE FROM civicrm_managed WHERE id = %1', [ |
428 | 1 => [$dao->id, 'Integer'], | |
429 | ]); | |
1f103dc4 | 430 | } |
6a488035 TO |
431 | } |
432 | ||
2e2605fe EM |
433 | /** |
434 | * Get declarations. | |
435 | * | |
436 | * @return array|null | |
437 | */ | |
d80c6631 | 438 | protected function getDeclarations() { |
e9b95545 TO |
439 | return $this->declarations; |
440 | } | |
441 | ||
6a488035 | 442 | /** |
88718db2 TO |
443 | * @param array $modules |
444 | * Array<CRM_Core_Module>. | |
77b97be7 | 445 | * |
a6c01b45 CW |
446 | * @return array |
447 | * indexed by is_active,name | |
6a488035 | 448 | */ |
85917c3b | 449 | protected function createModuleIndex($modules) { |
be2fb01f | 450 | $result = []; |
6a488035 TO |
451 | foreach ($modules as $module) { |
452 | $result[$module->is_active][$module->name] = $module; | |
453 | } | |
454 | return $result; | |
455 | } | |
456 | ||
457 | /** | |
88718db2 TO |
458 | * @param array $moduleIndex |
459 | * @param array $declarations | |
77b97be7 | 460 | * |
a6c01b45 CW |
461 | * @return array |
462 | * indexed by module,name | |
6a488035 | 463 | */ |
85917c3b | 464 | protected function createDeclarationIndex($moduleIndex, $declarations) { |
be2fb01f | 465 | $result = []; |
6a488035 TO |
466 | if (!isset($moduleIndex[TRUE])) { |
467 | return $result; | |
468 | } | |
469 | foreach ($moduleIndex[TRUE] as $moduleName => $module) { | |
470 | if ($module->is_active) { | |
471 | // need an empty array() for all active modules, even if there are no current $declarations | |
be2fb01f | 472 | $result[$moduleName] = []; |
6a488035 TO |
473 | } |
474 | } | |
475 | foreach ($declarations as $declaration) { | |
476 | $result[$declaration['module']][$declaration['name']] = $declaration; | |
477 | } | |
478 | return $result; | |
479 | } | |
480 | ||
481 | /** | |
fd31fa4c EM |
482 | * @param $declarations |
483 | * | |
72b3a70c CW |
484 | * @return string|bool |
485 | * string on error, or FALSE | |
6a488035 | 486 | */ |
85917c3b | 487 | protected function validate($declarations) { |
9cf68fcc | 488 | foreach ($declarations as $module => $declare) { |
be2fb01f | 489 | foreach (['name', 'module', 'entity', 'params'] as $key) { |
6a488035 TO |
490 | if (empty($declare[$key])) { |
491 | $str = print_r($declare, TRUE); | |
9cf68fcc | 492 | return ts('Managed Entity (%1) is missing field "%2": %3', [$module, $key, $str]); |
6a488035 TO |
493 | } |
494 | } | |
9cf68fcc EM |
495 | if (!$this->isModuleRecognised($declare['module'])) { |
496 | return ts('Entity declaration references invalid or inactive module name [%1]', [$declare['module']]); | |
497 | } | |
6a488035 TO |
498 | } |
499 | return FALSE; | |
500 | } | |
501 | ||
9cf68fcc EM |
502 | /** |
503 | * Is the module recognised (as an enabled or disabled extension in the system). | |
504 | * | |
505 | * @param string $module | |
506 | * | |
507 | * @return bool | |
508 | */ | |
509 | protected function isModuleRecognised(string $module): bool { | |
510 | return $this->isModuleDisabled($module) || $this->isModuleEnabled($module); | |
511 | } | |
512 | ||
513 | /** | |
514 | * Is the module enabled. | |
515 | * | |
516 | * @param string $module | |
517 | * | |
518 | * @return bool | |
519 | */ | |
520 | protected function isModuleEnabled(string $module): bool { | |
521 | return isset($this->moduleIndex[TRUE][$module]); | |
522 | } | |
523 | ||
524 | /** | |
525 | * Is the module disabled. | |
526 | * | |
527 | * @param string $module | |
528 | * | |
529 | * @return bool | |
530 | */ | |
531 | protected function isModuleDisabled(string $module): bool { | |
532 | return isset($this->moduleIndex[FALSE][$module]); | |
533 | } | |
534 | ||
a0ee3941 | 535 | /** |
72b3a70c | 536 | * @param array $declarations |
a0ee3941 | 537 | * |
72b3a70c | 538 | * @return array |
a0ee3941 | 539 | */ |
85917c3b | 540 | protected function cleanDeclarations(array $declarations): array { |
6a488035 TO |
541 | foreach ($declarations as $name => &$declare) { |
542 | if (!array_key_exists('name', $declare)) { | |
543 | $declare['name'] = $name; | |
544 | } | |
545 | } | |
546 | return $declarations; | |
547 | } | |
548 | ||
a0ee3941 | 549 | /** |
e9b95545 TO |
550 | * @param string $entity |
551 | * @param string $action | |
552 | * @param array $params | |
553 | * @param array $result | |
a0ee3941 EM |
554 | * |
555 | * @throws Exception | |
556 | */ | |
e9b95545 | 557 | protected function onApiError($entity, $action, $params, $result) { |
be2fb01f | 558 | CRM_Core_Error::debug_var('ManagedEntities_failed', [ |
e9b95545 TO |
559 | 'entity' => $entity, |
560 | 'action' => $action, | |
6a488035 TO |
561 | 'params' => $params, |
562 | 'result' => $result, | |
be2fb01f | 563 | ]); |
35c1c211 | 564 | throw new Exception('API error: ' . $result['error_message'] . ' on ' . $entity . '.' . $action |
9f4f065a | 565 | . (!empty($params['name']) ? '( entity name ' . $params['name'] . ')' : '') |
35c1c211 | 566 | ); |
cbb7c7e0 | 567 | } |
96025800 | 568 | |
fc625166 TO |
569 | /** |
570 | * Determine if an entity supports APIv3-based activation/de-activation. | |
571 | * @param string $entity_type | |
572 | * | |
573 | * @return bool | |
574 | * @throws \CiviCRM_API3_Exception | |
575 | */ | |
576 | private function isActivationSupported(string $entity_type): bool { | |
577 | if (!isset(Civi::$statics[__CLASS__][__FUNCTION__][$entity_type])) { | |
578 | $actions = civicrm_api3($entity_type, 'getactions', [])['values']; | |
579 | Civi::$statics[__CLASS__][__FUNCTION__][$entity_type] = FALSE; | |
580 | if (in_array('create', $actions, TRUE) && in_array('getfields', $actions)) { | |
581 | $fields = civicrm_api3($entity_type, 'getfields', ['action' => 'create'])['values']; | |
582 | Civi::$statics[__CLASS__][__FUNCTION__][$entity_type] = array_key_exists('is_active', $fields); | |
583 | } | |
584 | } | |
585 | return Civi::$statics[__CLASS__][__FUNCTION__][$entity_type]; | |
586 | } | |
587 | ||
78ce6ebb EM |
588 | /** |
589 | * Load declarations into the class property. | |
590 | * | |
591 | * This picks it up from hooks and enabled components. | |
592 | */ | |
593 | protected function loadDeclarations(): void { | |
594 | $this->declarations = []; | |
595 | foreach (CRM_Core_Component::getEnabledComponents() as $component) { | |
596 | $this->declarations = array_merge($this->declarations, $component->getManagedEntities()); | |
597 | } | |
598 | CRM_Utils_Hook::managed($this->declarations); | |
599 | $this->declarations = $this->cleanDeclarations($this->declarations); | |
600 | } | |
601 | ||
a092536e EM |
602 | protected function loadManagedEntityActions(): void { |
603 | $managedEntities = Managed::get(FALSE)->addSelect('*')->execute(); | |
604 | foreach ($managedEntities as $managedEntity) { | |
605 | $key = "{$managedEntity['module']}_{$managedEntity['name']}_{$managedEntity['entity_type']}"; | |
606 | // Set to 'delete' - it will be overwritten below if it is to be updated. | |
607 | $action = 'delete'; | |
608 | $this->managedActions[$key] = array_merge($managedEntity, ['managed_action' => $action]); | |
609 | } | |
610 | foreach ($this->declarations as $declaration) { | |
611 | $key = "{$declaration['module']}_{$declaration['name']}_{$declaration['entity']}"; | |
612 | if (isset($this->managedActions[$key])) { | |
613 | $this->managedActions[$key]['params'] = $declaration['params']; | |
614 | $this->managedActions[$key]['managed_action'] = 'update'; | |
615 | $this->managedActions[$key]['cleanup'] = $declaration['cleanup'] ?? NULL; | |
616 | $this->managedActions[$key]['update'] = $declaration['update'] ?? 'always'; | |
617 | } | |
618 | else { | |
619 | $this->managedActions[$key] = [ | |
620 | 'module' => $declaration['module'], | |
621 | 'name' => $declaration['name'], | |
622 | 'entity_type' => $declaration['entity'], | |
623 | 'managed_action' => 'create', | |
624 | 'params' => $declaration['params'], | |
625 | 'cleanup' => $declaration['cleanup'] ?? NULL, | |
626 | 'update' => $declaration['update'] ?? 'always', | |
627 | ]; | |
628 | } | |
629 | } | |
630 | } | |
631 | ||
6a488035 | 632 | } |