From a092536e5335dbed5fbfa03f797c81bc88680a90 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Wed, 8 Sep 2021 17:08:34 +1200 Subject: [PATCH] dev/core#2823 Restructure determination of required actions. The expected actions are now calculated at the start of reconcile. and retrievable by function. The code that calls the action-actions is still pretty cludgey - but the intent is that we would stop passing the dao object into those action-action functions in the near future. This PR punts any decisions about how outputs might look (eg. making the array retrievable from outside of the function. Note that there is pretty comprehensive test cover in CRM_Core_ManagedEntitiesTest --- CRM/Core/ManagedEntities.php | 149 +++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 26 deletions(-) diff --git a/CRM/Core/ManagedEntities.php b/CRM/Core/ManagedEntities.php index e4415db15f..2f862483ec 100644 --- a/CRM/Core/ManagedEntities.php +++ b/CRM/Core/ManagedEntities.php @@ -1,5 +1,7 @@ isUpgradeMode() && !$ignoreUpgradeMode) { return; } + if ($error = $this->validate($this->getDeclarations())) { throw new CRM_Core_Exception($error); } + $this->loadManagedEntityActions(); $this->reconcileEnabledModules(); $this->reconcileDisabledModules(); $this->reconcileUnknownModules(); @@ -136,7 +149,7 @@ class CRM_Core_ManagedEntities { $decls = $this->createDeclarationIndex($this->moduleIndex, $this->getDeclarations()); foreach ($decls as $moduleName => $todos) { if ($this->isModuleEnabled($moduleName)) { - $this->reconcileEnabledModule($this->moduleIndex[TRUE][$moduleName], $todos); + $this->reconcileEnabledModule($moduleName); } } } @@ -145,33 +158,87 @@ class CRM_Core_ManagedEntities { * For one enabled module, add new entities, update existing entities, * and remove orphaned (stale) entities. * - * @param \CRM_Core_Module $module - * @param array $todos - * List of entities currently declared by this module. - * array(string $name => array $entityDef). + * @param string $module */ - public function reconcileEnabledModule(CRM_Core_Module $module, $todos) { - $dao = new CRM_Core_DAO_Managed(); - $dao->module = $module->name; - $dao->find(); - while ($dao->fetch()) { - if (isset($todos[$dao->name]) && $todos[$dao->name]) { - // update existing entity; remove from $todos - $this->updateExistingEntity($dao, $todos[$dao->name]); - unset($todos[$dao->name]); - } - else { - // remove stale entity; not in $todos - $this->removeStaleEntity($dao); - } + public function reconcileEnabledModule(string $module): void { + foreach ($this->getManagedEntitiesToUpdate(['module' => $module]) as $todo) { + $dao = new CRM_Core_DAO_Managed(); + $dao->module = $todo['module']; + $dao->name = $todo['name']; + $dao->entity_type = $todo['entity_type']; + $dao->entity_id = $todo['entity_id']; + $dao->id = $todo['id']; + $this->updateExistingEntity($dao, $todo); } - // create new entities from leftover $todos - foreach ($todos as $name => $todo) { + foreach ($this->getManagedEntitiesToDelete(['module' => $module]) as $todo) { + $dao = new CRM_Core_DAO_Managed(); + $dao->module = $todo['module']; + $dao->name = $todo['name']; + $dao->entity_type = $todo['entity_type']; + $dao->id = $todo['id']; + $dao->cleanup = $todo['cleanup']; + $dao->entity_id = $todo['entity_id']; + $this->removeStaleEntity($dao); + } + foreach ($this->getManagedEntitiesToCreate(['module' => $module]) as $todo) { $this->insertNewEntity($todo); } } + /** + * Get the managed entities to be created. + * + * @param array $filters + * + * @return array + */ + protected function getManagedEntitiesToCreate(array $filters = []): array { + return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'create'])); + } + + /** + * Get the managed entities to be created. + * + * @param array $filters + * + * @return array + */ + protected function getManagedEntitiesToUpdate(array $filters = []): array { + return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'update'])); + } + + /** + * Get the managed entities to be deleted. + * + * @param array $filters + * + * @return array + */ + protected function getManagedEntitiesToDelete(array $filters = []): array { + return $this->getManagedEntities(array_merge($filters, ['managed_action' => 'delete'])); + } + + /** + * Get the managed entities that fit the criteria. + * + * @param array $filters + * + * @return array + */ + protected function getManagedEntities(array $filters = []): array { + $return = []; + foreach ($this->managedActions as $actionKey => $action) { + foreach ($filters as $filterKey => $filterValue) { + if ($action[$filterKey] !== $filterValue) { + continue 2; + } + } + $return[$actionKey] = $action; + } + return $return; + } + /** * For all disabled modules, disable any managed entities. */ @@ -223,15 +290,15 @@ class CRM_Core_ManagedEntities { * Entity specification (per hook_civicrm_managedEntities). */ protected function insertNewEntity($todo) { - $result = civicrm_api($todo['entity'], 'create', $todo['params']); + $result = civicrm_api($todo['entity_type'], 'create', $todo['params']); if (!empty($result['is_error'])) { - $this->onApiError($todo['entity'], 'create', $todo['params'], $result); + $this->onApiError($todo['entity_type'], 'create', $todo['params'], $result); } $dao = new CRM_Core_DAO_Managed(); $dao->module = $todo['module']; $dao->name = $todo['name']; - $dao->entity_type = $todo['entity']; + $dao->entity_type = $todo['entity_type']; // A fatal error will result if there is no valid id but if // this is v4 api we might need to access it via ->first(). $dao->entity_id = $result['id'] ?? $result->first()['id']; @@ -314,7 +381,7 @@ class CRM_Core_ManagedEntities { * Remove a stale entity (if policy allows). * * @param CRM_Core_DAO_Managed $dao - * @throws Exception + * @throws CRM_Core_Exception */ protected function removeStaleEntity($dao) { $policy = empty($dao->cleanup) ? 'always' : $dao->cleanup; @@ -342,7 +409,7 @@ class CRM_Core_ManagedEntities { break; default: - throw new \Exception('Unrecognized cleanup policy: ' . $policy); + throw new CRM_Core_Exception('Unrecognized cleanup policy: ' . $policy); } if ($doDelete) { @@ -535,4 +602,34 @@ class CRM_Core_ManagedEntities { $this->declarations = $this->cleanDeclarations($this->declarations); } + protected function loadManagedEntityActions(): void { + $managedEntities = Managed::get(FALSE)->addSelect('*')->execute(); + foreach ($managedEntities as $managedEntity) { + $key = "{$managedEntity['module']}_{$managedEntity['name']}_{$managedEntity['entity_type']}"; + // Set to 'delete' - it will be overwritten below if it is to be updated. + $action = 'delete'; + $this->managedActions[$key] = array_merge($managedEntity, ['managed_action' => $action]); + } + foreach ($this->declarations as $declaration) { + $key = "{$declaration['module']}_{$declaration['name']}_{$declaration['entity']}"; + if (isset($this->managedActions[$key])) { + $this->managedActions[$key]['params'] = $declaration['params']; + $this->managedActions[$key]['managed_action'] = 'update'; + $this->managedActions[$key]['cleanup'] = $declaration['cleanup'] ?? NULL; + $this->managedActions[$key]['update'] = $declaration['update'] ?? 'always'; + } + else { + $this->managedActions[$key] = [ + 'module' => $declaration['module'], + 'name' => $declaration['name'], + 'entity_type' => $declaration['entity'], + 'managed_action' => 'create', + 'params' => $declaration['params'], + 'cleanup' => $declaration['cleanup'] ?? NULL, + 'update' => $declaration['update'] ?? 'always', + ]; + } + } + } + } -- 2.25.1