From 92a43aef6ab747b7f63217b09e701aa76e7b9047 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 18 May 2022 09:57:31 -0400 Subject: [PATCH] Afform - dispatch event when loading afforms Allows extensions to dynamically add afforms. --- .../core/Civi/Api4/Action/Afform/Get.php | 68 ++++++++++++------- ext/afform/core/afform.php | 13 ++-- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/ext/afform/core/Civi/Api4/Action/Afform/Get.php b/ext/afform/core/Civi/Api4/Action/Afform/Get.php index cfd32556cf..54afeb153d 100644 --- a/ext/afform/core/Civi/Api4/Action/Afform/Get.php +++ b/ext/afform/core/Civi/Api4/Action/Afform/Get.php @@ -4,6 +4,7 @@ namespace Civi\Api4\Action\Afform; use Civi\Api4\CustomField; use Civi\Api4\CustomGroup; +use Civi\Core\Event\GenericHookEvent; use CRM_Afform_ExtensionUtil as E; /** @@ -20,7 +21,7 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { $getComputed = $this->_isFieldSelected('has_local', 'has_base', 'base_module'); $getLayout = $this->_isFieldSelected('layout'); $getSearchDisplays = $this->_isFieldSelected('search_displays'); - $values = []; + $afforms = []; // This helps optimize lookups by file/module/directive name $getNames = array_filter([ @@ -32,9 +33,26 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { $names = $getNames['name'] ?? array_keys($scanner->findFilePaths()); - // Get autogenerated blocks if type block is not excluded - if (!$getTypes || in_array('block', $getTypes, TRUE)) { - $values = $this->getAutoGenerated($names, $getNames, $getLayout); + // Allow hooks to provide autoGenerated forms + $hookForms = []; + $hookParams = ['afforms' => &$hookForms, 'getNames' => $getNames, 'getTypes' => $getTypes, 'getLayout' => $getLayout]; + $event = GenericHookEvent::create($hookParams); + \Civi::dispatcher()->dispatch('civi.afform.get', $event); + // Set defaults for Afforms supplied by hook + foreach ($hookForms as $afform) { + $name = $afform['name']; + $afform += [ + 'has_base' => TRUE, + 'type' => 'form', + ]; + if ($afform['type'] === 'form') { + // afCore and af would normally get required by AngularDependencyMapper but that only works on file-based afforms + $afform['requires'] = array_unique(array_merge(['afCore', 'af'], $afform['requires'] ?? [])); + } + if (!in_array($name, $names)) { + $names[] = $name; + } + $afforms[$name] = $afform; } if ($this->checkPermissions) { @@ -56,31 +74,31 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { $record = $scanner->getMeta($name); // Skip if afform does not exist or is not of requested type(s) if ( - (!$record && !isset($values[$name])) || + (!$record && !isset($afforms[$name])) || ($getTypes && isset($record['type']) && !in_array($record['type'], $getTypes, TRUE)) ) { continue; } - $values[$name] = array_merge($values[$name] ?? [], $record ?? [], $info); + $afforms[$name] = array_merge($afforms[$name] ?? [], $record ?? [], $info); if ($getComputed) { - $scanner->addComputedFields($values[$name]); + $scanner->addComputedFields($afforms[$name]); } if ($getLayout || $getSearchDisplays) { // Autogenerated layouts will already be in values but can be overridden; scanner takes priority - $values[$name]['layout'] = $scanner->getLayout($name) ?? $values[$name]['layout'] ?? ''; + $afforms[$name]['layout'] = $scanner->getLayout($name) ?? $afforms[$name]['layout'] ?? ''; } if ($getSearchDisplays) { - $values[$name]['search_displays'] = $this->getSearchDisplays($values[$name]['layout']); + $afforms[$name]['search_displays'] = $this->getSearchDisplays($afforms[$name]['layout']); } } if ($getLayout && $this->layoutFormat !== 'html') { - foreach ($values as $name => $record) { - $values[$name]['layout'] = $this->convertHtmlToOutput($record['layout']); + foreach ($afforms as $name => $record) { + $afforms[$name]['layout'] = $this->convertHtmlToOutput($record['layout']); } } - return $values; + return $afforms; } /** @@ -95,14 +113,19 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { /** * Generates afform blocks from custom field sets. * - * @param array $names - * @param array $getNames - * @param bool $getLayout - * @return array + * @param \Civi\Core\Event\GenericHookEvent $event * @throws \API_Exception */ - protected function getAutoGenerated(&$names, $getNames, $getLayout) { - $values = $groupNames = []; + public static function getCustomGroupBlocks($event) { + // Early return if blocks are not requested + if ($event->getTypes && !in_array('block', $event->getTypes, TRUE)) { + return; + } + + $getNames = $event->getNames; + $getLayout = $event->getLayout; + $groupNames = []; + $afforms =& $event->afforms; foreach ($getNames['name'] ?? [] as $name) { if (strpos($name, 'afblockCustom_') === 0 && strlen($name) > 13) { $groupNames[] = substr($name, 14); @@ -113,7 +136,7 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { || (!empty($getNames['module_name']) && !strstr(implode(' ', $getNames['module_name']), 'afblockCustom')) || (!empty($getNames['directive_name']) && !strstr(implode(' ', $getNames['directive_name']), 'afblock-custom')) ) { - return $values; + return; } $customApi = CustomGroup::get(FALSE) ->addSelect('name', 'title', 'help_pre', 'help_post', 'extends', 'max_multiple') @@ -133,9 +156,6 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { } foreach ($customApi->execute() as $custom) { $name = 'afblockCustom_' . $custom['name']; - if (!in_array($name, $names)) { - $names[] = $name; - } $item = [ 'name' => $name, 'type' => 'block', @@ -148,7 +168,6 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { 'permission' => 'access CiviCRM', 'join_entity' => 'Custom_' . $custom['name'], 'entity_type' => $custom['extends'], - 'has_base' => TRUE, ]; if ($getLayout) { $item['layout'] = ($custom['help_pre'] ? '
' . $custom['help_pre'] . "
\n" : ''); @@ -157,9 +176,8 @@ class Get extends \Civi\Api4\Generic\BasicGetAction { } $item['layout'] .= ($custom['help_post'] ? '
' . $custom['help_post'] . "
\n" : ''); } - $values[$name] = $item; + $afforms[$name] = $item; } - return $values; } /** diff --git a/ext/afform/core/afform.php b/ext/afform/core/afform.php index 4cb7886524..167315bcf3 100644 --- a/ext/afform/core/afform.php +++ b/ext/afform/core/afform.php @@ -55,6 +55,7 @@ function afform_civicrm_config(&$config) { $dispatcher->addListener('hook_civicrm_angularModules', ['\Civi\Afform\AngularDependencyMapper', 'autoReq'], -1000); $dispatcher->addListener('hook_civicrm_alterAngular', ['\Civi\Afform\AfformMetadataInjector', 'preprocess']); $dispatcher->addListener('hook_civicrm_check', ['\Civi\Afform\StatusChecks', 'hook_civicrm_check']); + $dispatcher->addListener('civi.afform.get', ['\Civi\Api4\Action\Afform\Get', 'getCustomGroupBlocks']); // Register support for email tokens if (CRM_Extension_System::singleton()->getMapper()->isActiveModule('authx')) { @@ -358,14 +359,18 @@ function afform_civicrm_buildAsset($asset, $params, &$mimeType, &$content) { * Implements hook_civicrm_alterMenu(). */ function afform_civicrm_alterMenu(&$items) { - if (Civi::container()->has('afform_scanner')) { - $scanner = Civi::service('afform_scanner'); + try { + $afforms = \Civi\Api4\Afform::get(FALSE) + ->addWhere('server_route', 'IS NOT EMPTY') + ->addSelect('name', 'server_route', 'is_public') + ->execute()->indexBy('name'); } - else { + catch (Exception $e) { // During installation... $scanner = new CRM_Afform_AfformScanner(); + $afforms = $scanner->getMetas(); } - foreach ($scanner->getMetas() as $name => $meta) { + foreach ($afforms as $name => $meta) { if (!empty($meta['server_route'])) { $items[$meta['server_route']] = [ 'page_callback' => 'CRM_Afform_Page_AfformBase', -- 2.25.1