From 7b9f8bdccb1fc5629760537560893ff1a5c01fe7 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sun, 17 Jul 2022 11:56:31 -0400 Subject: [PATCH] Afform - provide easy way to add navigation menu from the form --- ext/afform/admin/ang/afGuiEditor.css | 4 - .../ang/afGuiEditor/afGuiEditor.component.js | 55 +++++++++++++ .../admin/ang/afGuiEditor/config-form.html | 49 +++++++++--- ext/afform/core/Civi/Api4/Afform.php | 5 ++ .../core/Civi/Api4/Utils/AfformSaveTrait.php | 8 +- ext/afform/core/afform.php | 78 +++++++++++++------ 6 files changed, 161 insertions(+), 38 deletions(-) diff --git a/ext/afform/admin/ang/afGuiEditor.css b/ext/afform/admin/ang/afGuiEditor.css index 585a397845..445fab469b 100644 --- a/ext/afform/admin/ang/afGuiEditor.css +++ b/ext/afform/admin/ang/afGuiEditor.css @@ -112,10 +112,6 @@ margin-bottom: 10px; } -#afGuiEditor-palette-config .form-inline label { - min-width: 110px; -} - #afGuiEditor-palette-config .af-gui-entity-palette [type=search] { width: 120px; padding: 3px 3px 3px 5px; diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js index e99e02c58c..12f4d927de 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js @@ -101,6 +101,10 @@ $scope.entities = {}; setEditorLayout(); + if (editor.afform.navigation) { + loadNavigationMenu(); + } + if (editor.getFormType() === 'form') { editor.allowEntityConfig = true; $scope.entities = _.mapValues(afGui.findRecursive(editor.layout['#children'], {'#tag': 'af-entity'}, 'name'), backfillEntityDefaults); @@ -334,6 +338,57 @@ } }; + this.toggleNavigation = function() { + if (editor.afform.navigation) { + editor.afform.navigation = null; + } else { + loadNavigationMenu(); + editor.afform.navigation = { + parent: null, + label: editor.afform.title, + weight: 0 + }; + } + }; + + function loadNavigationMenu() { + if ('navigationMenu' in editor) { + return; + } + editor.navigationMenu = null; + var conditions = [ + ['domain_id', '=', 'current_domain'], + ['name', '!=', 'Home'] + ]; + if (editor.afform.name) { + conditions.push(['name', '!=', editor.afform.name]); + } + crmApi4('Navigation', 'get', { + select: ['name', 'label', 'parent_id', 'icon'], + where: conditions, + orderBy: {weight: 'ASC'} + }).then(function(items) { + editor.navigationMenu = buildTree(items, null); + }); + } + + function buildTree(items, parentId) { + return _.transform(items, function(navigationMenu, item) { + if (parentId === item.parent_id) { + var children = buildTree(items, item.id), + menuItem = { + id: item.name, + text: item.label, + icon: item.icon + }; + if (children.length) { + menuItem.children = children; + } + navigationMenu.push(menuItem); + } + }, []); + } + // Collects all search displays currently on the form function getSearchDisplaysOnForm() { var searchFieldsets = afGui.findRecursive(editor.afform.layout, {'af-fieldset': ''}); diff --git a/ext/afform/admin/ang/afGuiEditor/config-form.html b/ext/afform/admin/ang/afGuiEditor/config-form.html index ca85a02cb1..c316e9ec4e 100644 --- a/ext/afform/admin/ang/afGuiEditor/config-form.html +++ b/ext/afform/admin/ang/afGuiEditor/config-form.html @@ -32,7 +32,7 @@

{{:: ts('Expose the form as a standalone webpage. (Example: "civicrm/my-form")') }}

@@ -54,11 +54,32 @@
- -

{{:: ts('Allow CiviCRM users to add the form to their home dashboard.') }}

+
+ +
+ + + + + + + + + +
+
+

{{:: ts('Requires a page route') }}

+
+ +
+
+ + +
@@ -71,12 +92,20 @@ -
- -
-

{{:: ts('Placement can be configured using the Contact Layout Editor.') }}

+

+ {{:: ts('Placement can be configured using the Contact Layout Editor.') }} +

+ +
+ +

{{:: ts('Allow CiviCRM users to add the form to their home dashboard.') }}

+
+ diff --git a/ext/afform/core/Civi/Api4/Afform.php b/ext/afform/core/Civi/Api4/Afform.php index 15d2aea8d8..2a21cf756c 100644 --- a/ext/afform/core/Civi/Api4/Afform.php +++ b/ext/afform/core/Civi/Api4/Afform.php @@ -182,6 +182,11 @@ class Afform extends Generic\AbstractEntity { 'name' => 'create_submission', 'data_type' => 'Boolean', ], + [ + 'name' => 'navigation', + 'data_type' => 'Array', + 'description' => 'Insert into navigation menu {parent: string, label: string, weight: int}', + ], [ 'name' => 'layout', 'data_type' => 'Array', diff --git a/ext/afform/core/Civi/Api4/Utils/AfformSaveTrait.php b/ext/afform/core/Civi/Api4/Utils/AfformSaveTrait.php index 0d109351af..466705a00d 100644 --- a/ext/afform/core/Civi/Api4/Utils/AfformSaveTrait.php +++ b/ext/afform/core/Civi/Api4/Utils/AfformSaveTrait.php @@ -67,10 +67,14 @@ trait AfformSaveTrait { return ($item[$field] ?? NULL) !== ($orig[$field] ?? NULL); }; - // If the dashlet setting changed, managed entities must be reconciled + // If the dashlet or navigation setting changed, managed entities must be reconciled + // TODO: If this list of conditions gets any longer, then + // maybe we should unconditionally reconcile and accept the small performance drag. if ( $isChanged('is_dashlet') || - (!empty($meta['is_dashlet']) && $isChanged('title')) + $isChanged('navigation') || + (!empty($meta['is_dashlet']) && $isChanged('title')) || + (!empty($meta['navigation']) && ($isChanged('title') || $isChanged('permission') || $isChanged('icon') || $isChanged('server_route'))) ) { \CRM_Core_ManagedEntities::singleton()->reconcile(E::LONG_NAME); } diff --git a/ext/afform/core/afform.php b/ext/afform/core/afform.php index be15c8f322..b1d294b44f 100644 --- a/ext/afform/core/afform.php +++ b/ext/afform/core/afform.php @@ -13,7 +13,7 @@ function _afform_fields_filter($params) { $result = []; $fields = \Civi\Api4\Afform::getfields(FALSE)->setAction('create')->execute()->indexBy('name'); foreach ($fields as $fieldName => $field) { - if (isset($params[$fieldName])) { + if (array_key_exists($fieldName, $params)) { $result[$fieldName] = $params[$fieldName]; if ($field['data_type'] === 'Boolean' && !is_bool($params[$fieldName])) { @@ -140,32 +140,66 @@ function afform_civicrm_managed(&$entities, $modules) { // This AfformScanner instance only lives during this method call, and it feeds off the regular cache. $scanner = new CRM_Afform_AfformScanner(); } + $domains = NULL; foreach ($scanner->getMetas() as $afform) { - if (empty($afform['is_dashlet']) || empty($afform['name'])) { + if (empty($afform['name'])) { continue; } - $entities[] = [ - 'module' => E::LONG_NAME, - 'name' => 'afform_dashlet_' . $afform['name'], - 'entity' => 'Dashboard', - 'update' => 'always', - // ideal cleanup policy might be to (a) deactivate if used and (b) remove if unused - 'cleanup' => 'always', - 'params' => [ - 'version' => 4, - 'values' => [ - // Q: Should we loop through all domains? - 'domain_id' => 'current_domain', - 'is_active' => TRUE, - 'name' => $afform['name'], - 'label' => $afform['title'] ?? E::ts('(Untitled)'), - 'directive' => _afform_angular_module_name($afform['name'], 'dash'), - 'permission' => "@afform:" . $afform['name'], - 'url' => NULL, + if (!empty($afform['is_dashlet'])) { + $entities[] = [ + 'module' => E::LONG_NAME, + 'name' => 'afform_dashlet_' . $afform['name'], + 'entity' => 'Dashboard', + 'update' => 'always', + // ideal cleanup policy might be to (a) deactivate if used and (b) remove if unused + 'cleanup' => 'always', + 'params' => [ + 'version' => 4, + 'values' => [ + // Q: Should we loop through all domains? + 'domain_id' => 'current_domain', + 'is_active' => TRUE, + 'name' => $afform['name'], + 'label' => $afform['title'] ?? E::ts('(Untitled)'), + 'directive' => _afform_angular_module_name($afform['name'], 'dash'), + 'permission' => "@afform:" . $afform['name'], + 'url' => NULL, + ], ], - ], - ]; + ]; + } + if (!empty($afform['navigation']) && !empty($afform['server_route'])) { + $domains = $domains ?: \Civi\Api4\Domain::get(FALSE)->addSelect('id')->execute(); + foreach ($domains as $domain) { + $params = [ + 'version' => 4, + 'values' => [ + 'name' => $afform['name'], + 'label' => $afform['navigation']['label'] ?: $afform['title'], + 'permission' => (array) $afform['permission'], + 'permission_operator' => 'OR', + 'weight' => $afform['navigation']['weight'] ?? 0, + 'url' => $afform['server_route'], + 'is_active' => 1, + 'icon' => 'crm-i ' . $afform['icon'], + 'domain_id' => $domain['id'], + ], + 'match' => ['domain_id', 'name'], + ]; + if (!empty($afform['navigation']['parent'])) { + $params['values']['parent_id.name'] = $afform['navigation']['parent']; + } + $entities[] = [ + 'module' => E::LONG_NAME, + 'name' => 'navigation_' . $afform['name'] . '_' . $domain['id'], + 'cleanup' => 'always', + 'update' => 'unmodified', + 'entity' => 'Navigation', + 'params' => $params, + ]; + } + } } } -- 2.25.1