From 47332dba8ff22aa6f08d473a0437dc02ca53458d Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Tue, 26 Apr 2022 15:16:55 +0200 Subject: [PATCH] Afform - Dispatch event to alter admin metadata; provide mixin Instead of scanning files for Afform entities, this dispatches an event to allow them to be altered. It provides a mixin which extensions can use to automatically do the file scanning. --- .../Civi/AfformAdmin/AfformAdminMeta.php | 240 ++++++++---------- .../Civi/Api4/Action/Afform/LoadAdminData.php | 2 +- ext/afform/admin/ang/afGuiEditor.ang.php | 2 +- ext/afform/admin/info.xml | 1 + mixin/afform-entity-php@1/mixin.php | 39 +++ 5 files changed, 151 insertions(+), 133 deletions(-) create mode 100644 mixin/afform-entity-php@1/mixin.php diff --git a/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php b/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php index 51cd1c151f..f195ccaff1 100644 --- a/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php +++ b/ext/afform/admin/Civi/AfformAdmin/AfformAdminMeta.php @@ -4,6 +4,7 @@ namespace Civi\AfformAdmin; use Civi\Api4\Entity; use Civi\Api4\Utils\CoreUtil; +use Civi\Core\Event\GenericHookEvent; use CRM_AfformAdmin_ExtensionUtil as E; class AfformAdminMeta { @@ -33,25 +34,6 @@ class AfformAdminMeta { ]; } - /** - * @param $entityName - * @return array|void - */ - public static function getAfformEntity($entityName) { - // Optimization: look here before scanning every other extension - global $civicrm_root; - $fileName = \CRM_Utils_File::addTrailingSlash($civicrm_root) . "ext/afform/admin/afformEntities/$entityName.php"; - if (is_file($fileName)) { - return include $fileName; - } - foreach (\CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles() as $ext) { - $fileName = \CRM_Utils_File::addTrailingSlash(dirname($ext['filePath'])) . "afformEntities/$entityName.php"; - if (is_file($fileName)) { - return include $fileName; - } - } - } - /** * Get info about an api entity, with special handling for contact types * @param string $entityName @@ -148,138 +130,134 @@ class AfformAdminMeta { * * @return array */ - public static function getGuiSettings() { - $data = [ - 'entities' => [ + public static function getMetadata() { + $data = \Civi::cache('metadata')->get('afform_admin.metadata'); + if (!$data) { + $entities = [ '*' => [ 'label' => E::ts('Content Block'), 'icon' => 'fa-pencil-square-o', 'fields' => [], ], - ], - ]; - - // Explicitly load Contact and Custom entities because they do not have afformEntity files - $entities = Entity::get(TRUE) - ->addClause('OR', ['name', '=', 'Contact'], ['type', 'CONTAINS', 'CustomValue']) - ->execute()->indexBy('name'); - foreach ($entities as $name => $entity) { - $data['entities'][$name] = self::entityToAfformMeta($entity); - } - - $contactTypes = \CRM_Contact_BAO_ContactType::basicTypeInfo(); + ]; - // Call getFields on getFields to get input type labels - $inputTypeLabels = \Civi\Api4\Contact::getFields() - ->setLoadOptions(TRUE) - ->setAction('getFields') - ->addWhere('name', '=', 'input_type') - ->execute() - ->column('options')[0]; + // Explicitly load Contact and Custom entities because they do not have afformEntity files + $contactAndCustom = Entity::get(TRUE) + ->addClause('OR', ['name', '=', 'Contact'], ['type', 'CONTAINS', 'CustomValue']) + ->execute()->indexBy('name'); + foreach ($contactAndCustom as $name => $entity) { + $entities[$name] = self::entityToAfformMeta($entity); + } - // Scan all extensions for entities & input types - foreach (\CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles() as $ext) { - $dir = \CRM_Utils_File::addTrailingSlash(dirname($ext['filePath'])); - if (is_dir($dir)) { - // Scan for entities - foreach (glob($dir . 'afformEntities/*.php') as $file) { - $entityInfo = include $file; - $entityName = basename($file, '.php'); - $apiInfo = self::getApiEntity($entityInfo['entity'] ?? $entityName); - // Skip disabled contact types & entities from disabled components/extensions - if (!$apiInfo) { - continue; - } - $entityInfo += $apiInfo; - $data['entities'][$entityName] = $entityInfo; - } - // Scan for input types, use label from getFields if available - foreach (glob($dir . 'ang/afGuiEditor/inputType/*.html') as $file) { - $name = basename($file, '.html'); - $data['inputType'][] = [ - 'name' => $name, - 'label' => $inputTypeLabels[$name] ?? E::ts($name), - ]; - } + // Call getFields on getFields to get input type labels + $inputTypeLabels = \Civi\Api4\Contact::getFields() + ->setLoadOptions(TRUE) + ->setAction('getFields') + ->addWhere('name', '=', 'input_type') + ->execute() + ->column('options')[0]; + // Scan for input types, use label from getFields if available + $inputTypes = []; + foreach (glob(__DIR__ . '/../../ang/afGuiEditor/inputType/*.html') as $file) { + $name = basename($file, '.html'); + $inputTypes[] = [ + 'name' => $name, + 'label' => $inputTypeLabels[$name] ?? E::ts($name), + ]; } - } - // Todo: add method for extensions to define other elements - $data['elements'] = [ - 'container' => [ - 'title' => E::ts('Container'), - 'element' => [ - '#tag' => 'div', - 'class' => 'af-container', - '#children' => [], + // Static elements + $elements = [ + 'container' => [ + 'title' => E::ts('Container'), + 'element' => [ + '#tag' => 'div', + 'class' => 'af-container', + '#children' => [], + ], ], - ], - 'text' => [ - 'title' => E::ts('Text box'), - 'element' => [ - '#tag' => 'p', - 'class' => 'af-text', - '#children' => [ - ['#text' => E::ts('Enter text')], + 'text' => [ + 'title' => E::ts('Text box'), + 'element' => [ + '#tag' => 'p', + 'class' => 'af-text', + '#children' => [ + ['#text' => E::ts('Enter text')], + ], ], ], - ], - 'markup' => [ - 'title' => E::ts('Rich content'), - 'element' => [ - '#tag' => 'div', - 'class' => 'af-markup', - '#markup' => FALSE, + 'markup' => [ + 'title' => E::ts('Rich content'), + 'element' => [ + '#tag' => 'div', + 'class' => 'af-markup', + '#markup' => FALSE, + ], ], - ], - 'submit' => [ - 'title' => E::ts('Submit Button'), - 'element' => [ - '#tag' => 'button', - 'class' => 'af-button btn btn-primary', - 'crm-icon' => 'fa-check', - 'ng-click' => 'afform.submit()', - '#children' => [ - ['#text' => E::ts('Submit')], + 'submit' => [ + 'title' => E::ts('Submit Button'), + 'element' => [ + '#tag' => 'button', + 'class' => 'af-button btn btn-primary', + 'crm-icon' => 'fa-check', + 'ng-click' => 'afform.submit()', + '#children' => [ + ['#text' => E::ts('Submit')], + ], ], ], - ], - 'fieldset' => [ - 'title' => E::ts('Fieldset'), - 'element' => [ - '#tag' => 'fieldset', - 'af-fieldset' => NULL, - 'class' => 'af-container', - 'af-title' => E::ts('Enter title'), - '#children' => [], + 'fieldset' => [ + 'title' => E::ts('Fieldset'), + 'element' => [ + '#tag' => 'fieldset', + 'af-fieldset' => NULL, + 'class' => 'af-container', + 'af-title' => E::ts('Enter title'), + '#children' => [], + ], ], - ], - ]; + ]; - $data['styles'] = [ - 'default' => E::ts('Default'), - 'primary' => E::ts('Primary'), - 'success' => E::ts('Success'), - 'info' => E::ts('Info'), - 'warning' => E::ts('Warning'), - 'danger' => E::ts('Danger'), - ]; + $styles = [ + 'default' => E::ts('Default'), + 'primary' => E::ts('Primary'), + 'success' => E::ts('Success'), + 'info' => E::ts('Info'), + 'warning' => E::ts('Warning'), + 'danger' => E::ts('Danger'), + ]; - $data['permissions'] = []; - $perms = \Civi\Api4\Permission::get() - ->addWhere('group', 'IN', ['afformGeneric', 'const', 'civicrm', 'cms']) - ->addWhere('is_active', '=', 1) - ->setOrderBy(['title' => 'ASC']) - ->execute(); - foreach ($perms as $perm) { - $data['permissions'][] = [ - 'id' => $perm['name'], - 'text' => $perm['title'], - 'description' => $perm['description'] ?? NULL, + $perms = \Civi\Api4\Permission::get() + ->addWhere('group', 'IN', ['afformGeneric', 'const', 'civicrm', 'cms']) + ->addWhere('is_active', '=', 1) + ->setOrderBy(['title' => 'ASC']) + ->execute(); + $permissions = []; + foreach ($perms as $perm) { + $permissions[] = [ + 'id' => $perm['name'], + 'text' => $perm['title'], + 'description' => $perm['description'] ?? NULL, + ]; + } + + $dateRanges = \CRM_Utils_Array::makeNonAssociative(\CRM_Core_OptionGroup::values('relative_date_filters'), 'id', 'label'); + $dateRanges = array_merge([['id' => '{}', 'label' => E::ts('Choose Date Range')]], $dateRanges); + + // Allow data to be modified by event listeners + $data = [ + // @see afform-entity-php/mixin.php + 'entities' => &$entities, + 'inputTypes' => &$inputTypes, + 'elements' => &$elements, + 'styles' => &$styles, + 'permissions' => &$permissions, + 'dateRanges' => &$dateRanges, ]; + $event = GenericHookEvent::create($data); + \Civi::dispatcher()->dispatch('civi.afform_admin.metadata', $event); + \Civi::cache('metadata')->set('afform_admin.metadata', $data); } - $dateRanges = \CRM_Utils_Array::makeNonAssociative(\CRM_Core_OptionGroup::values('relative_date_filters'), 'id', 'label'); - $data['dateRanges'] = array_merge([['id' => '{}', 'label' => E::ts('Choose Date Range')]], $dateRanges); return $data; } diff --git a/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php b/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php index e1dba58065..3c0e68ac55 100644 --- a/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php +++ b/ext/afform/admin/Civi/Api4/Action/Afform/LoadAdminData.php @@ -135,7 +135,7 @@ class LoadAdminData extends \Civi\Api4\Generic\AbstractAction { if ($info['definition']['type'] === 'form') { if ($newForm) { $entities[] = $this->entity; - $defaultEntity = AfformAdminMeta::getAfformEntity($this->entity); + $defaultEntity = AfformAdminMeta::getMetadata()['entities'][$this->entity] ?? []; if (!empty($defaultEntity['boilerplate'])) { $scanBlocks($defaultEntity['boilerplate']); } diff --git a/ext/afform/admin/ang/afGuiEditor.ang.php b/ext/afform/admin/ang/afGuiEditor.ang.php index c792ee0d64..f2953b222e 100644 --- a/ext/afform/admin/ang/afGuiEditor.ang.php +++ b/ext/afform/admin/ang/afGuiEditor.ang.php @@ -9,7 +9,7 @@ return [ 'css' => ['ang/afGuiEditor.css'], 'partials' => ['ang/afGuiEditor'], 'requires' => ['crmUi', 'crmUtil', 'dialogService', 'api4', 'crmMonaco', 'ui.sortable'], - 'settingsFactory' => ['Civi\AfformAdmin\AfformAdminMeta', 'getGuiSettings'], + 'settingsFactory' => ['Civi\AfformAdmin\AfformAdminMeta', 'getMetadata'], 'basePages' => [], 'exports' => [ 'af-gui-editor' => 'E', diff --git a/ext/afform/admin/info.xml b/ext/afform/admin/info.xml index 7a4c3efb8b..ad236b05d3 100644 --- a/ext/afform/admin/info.xml +++ b/ext/afform/admin/info.xml @@ -33,5 +33,6 @@ ang-php@1.0.0 menu-xml@1.0.0 mgd-php@1.0.0 + afform-entity-php@1.0.0 diff --git a/mixin/afform-entity-php@1/mixin.php b/mixin/afform-entity-php@1/mixin.php new file mode 100644 index 0000000000..04d637f32a --- /dev/null +++ b/mixin/afform-entity-php@1/mixin.php @@ -0,0 +1,39 @@ +addListener('civi.afform_admin.metadata', function ($e) use ($mixInfo) { + // When deactivating on a polyfill/pre-mixin system, listeners may not cleanup automatically. + if (!$mixInfo->isActive() || !is_dir($mixInfo->getPath('afformEntities'))) { + return; + } + + $files = (array) glob($mixInfo->getPath('afformEntities/*.php')); + foreach ($files as $file) { + $entityInfo = include $file; + $entityName = basename($file, '.php'); + $apiInfo = \Civi\AfformAdmin\AfformAdminMeta::getApiEntity($entityInfo['entity'] ?? $entityName); + // Skip disabled contact types & entities from disabled components/extensions + if (!$apiInfo) { + continue; + } + $entityInfo += $apiInfo; + $e->entities[$entityName] = $entityInfo; + } + }); + +}; -- 2.25.1