diff --git a/CRM/Core/BAO/Navigation.php b/CRM/Core/BAO/Navigation.php index 688d399d6f5a..f3e72a6b3fdd 100644 --- a/CRM/Core/BAO/Navigation.php +++ b/CRM/Core/BAO/Navigation.php @@ -19,6 +19,42 @@ class CRM_Core_BAO_Navigation extends CRM_Core_DAO_Navigation { // Number of characters in the menu js cache key const CACHE_KEY_STRLEN = 8; + /** + * Override parent method to flush caches after a write op. + * + * Note: this only applies to APIv4 because v3 uses the singular writeRecord. + * + * @param array[] $records + * @return CRM_Core_DAO_Navigation[] + * @throws CRM_Core_Exception + */ + public static function writeRecords($records): array { + $results = []; + foreach ($records as $record) { + $results[] = self::writeRecord($record); + } + self::resetNavigation(); + return $results; + } + + /** + * Override parent method to flush caches after delete. + * + * Note: this only applies to APIv4 because v3 uses the singular writeRecord. + * + * @param array[] $records + * @return CRM_Core_DAO_Navigation[] + * @throws CRM_Core_Exception + */ + public static function deleteRecords(array $records) { + $results = []; + foreach ($records as $record) { + $results[] = self::deleteRecord($record); + } + self::resetNavigation(); + return $results; + } + /** * Update the is_active flag in the db. * diff --git a/ext/afform/admin/ang/afGuiEditor.css b/ext/afform/admin/ang/afGuiEditor.css index 585a39784595..445fab469b9d 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 e99e02c58ce8..a1bdb7a7aae4 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js @@ -30,6 +30,7 @@ undoHistory = [], undoPosition = 0, undoAction = null, + lastSaved, sortableOptions = {}; // ngModelOptions to debounce input @@ -100,6 +101,11 @@ $scope.layoutHtml = ''; $scope.entities = {}; setEditorLayout(); + setLastSaved(); + + if (editor.afform.navigation) { + loadNavigationMenu(); + } if (editor.getFormType() === 'form') { editor.allowEntityConfig = true; @@ -334,6 +340,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': ''}); @@ -522,6 +579,13 @@ snapshot.saved = index === undoPosition; snapshot.afform.name = data[0].name; }); + if (!angular.equals(afform.navigation, lastSaved.navigation) || + (afform.server_route !== lastSaved.server_route && afform.navigation) + (afform.icon !== lastSaved.icon && afform.navigation) + ) { + refreshMenubar(); + } + setLastSaved(); }); }; @@ -535,6 +599,19 @@ } }); + // Sets last-saved form metadata (used to determine if the menubar needs refresh) + function setLastSaved() { + lastSaved = JSON.parse(angular.toJson(editor.afform)); + delete lastSaved.layout; + } + + // Force-refresh the menubar to instantly display the afform menu item + function refreshMenubar() { + CRM.menubar.destroy(); + CRM.menubar.cacheCode = Math.random(); + CRM.menubar.initialize(); + } + // Force editor panels to a fixed height, to avoid palette scrolling offscreen function fixEditorHeight() { var height = $(window).height() - $('#afGuiEditor').offset().top; diff --git a/ext/afform/admin/ang/afGuiEditor/config-form.html b/ext/afform/admin/ang/afGuiEditor/config-form.html index ca85a02cb134..c316e9ec4e47 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') }}
+{{:: 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.') }}
+