From 9e58ceedbeba33102e5a02cee135966833337158 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Wed, 13 Jan 2021 10:21:30 -0500 Subject: [PATCH] Afform - refactor admin page to use routing, add tabbed UI --- .../admin/CRM/AfformAdmin/Page/Base.php | 32 ++++++++++ ext/afform/admin/CRM/AfformAdmin/Utils.php | 17 +++++- ext/afform/admin/ang/afAdmin.aff.html | 7 --- ext/afform/admin/ang/afAdmin.aff.json | 8 --- ext/afform/admin/ang/afAdmin.ang.php | 15 +++++ ext/afform/admin/ang/afAdmin.js | 30 ++++++++++ .../ang/afAdmin/afAdminGui.controller.js | 15 +++++ .../ang/afAdmin/afAdminList.controller.js | 51 ++++++++++++++++ ext/afform/admin/ang/afAdmin/afAdminList.html | 59 +++++++++++++++++++ ext/afform/admin/ang/afAdminList.aff.html | 55 ----------------- ext/afform/admin/ang/afGuiEditor.ang.php | 2 +- .../ang/afGuiEditor/afGuiEditor.component.js | 11 ++-- .../admin/ang/afGuiEditor/afGuiEditor.html | 8 ++- .../templates/CRM/AfformAdmin/Page/Base.tpl | 0 ext/afform/admin/xml/Menu/afform_admin.xml | 8 +++ ext/afform/core/Civi/Api4/Afform.php | 3 + .../core/ang/afblockNameHousehold.aff.json | 1 + .../core/ang/afblockNameIndividual.aff.json | 1 + .../core/ang/afblockNameOrganization.aff.json | 1 + .../core/ang/afjoinAddressDefault.aff.json | 1 + .../core/ang/afjoinEmailDefault.aff.json | 1 + ext/afform/core/ang/afjoinIMDefault.aff.json | 1 + .../core/ang/afjoinPhoneDefault.aff.json | 1 + .../core/ang/afjoinWebsiteDefault.aff.json | 1 + 24 files changed, 249 insertions(+), 80 deletions(-) create mode 100644 ext/afform/admin/CRM/AfformAdmin/Page/Base.php delete mode 100644 ext/afform/admin/ang/afAdmin.aff.html delete mode 100644 ext/afform/admin/ang/afAdmin.aff.json create mode 100644 ext/afform/admin/ang/afAdmin.ang.php create mode 100644 ext/afform/admin/ang/afAdmin.js create mode 100644 ext/afform/admin/ang/afAdmin/afAdminGui.controller.js create mode 100644 ext/afform/admin/ang/afAdmin/afAdminList.controller.js create mode 100644 ext/afform/admin/ang/afAdmin/afAdminList.html delete mode 100644 ext/afform/admin/ang/afAdminList.aff.html create mode 100644 ext/afform/admin/templates/CRM/AfformAdmin/Page/Base.tpl create mode 100644 ext/afform/admin/xml/Menu/afform_admin.xml diff --git a/ext/afform/admin/CRM/AfformAdmin/Page/Base.php b/ext/afform/admin/CRM/AfformAdmin/Page/Base.php new file mode 100644 index 0000000000..31338e5eaf --- /dev/null +++ b/ext/afform/admin/CRM/AfformAdmin/Page/Base.php @@ -0,0 +1,32 @@ + ts('Forms'), + 'url' => CRM_Utils_System::url('civicrm/admin/afform', NULL, FALSE, '/'), + ]; + CRM_Utils_System::appendBreadCrumb([$breadCrumb]); + + // Load angular module + $loader = new Civi\Angular\AngularLoader(); + $loader->setPageName('civicrm/admin/afform'); + $loader->useApp(); + $loader->load(); + parent::run(); + } + +} diff --git a/ext/afform/admin/CRM/AfformAdmin/Utils.php b/ext/afform/admin/CRM/AfformAdmin/Utils.php index 00cf47ba82..5233a295d1 100644 --- a/ext/afform/admin/CRM/AfformAdmin/Utils.php +++ b/ext/afform/admin/CRM/AfformAdmin/Utils.php @@ -3,12 +3,27 @@ use CRM_AfformAdmin_ExtensionUtil as E; class CRM_AfformAdmin_Utils { + /** + * @return array + */ + public static function getAdminSettings() { + return [ + 'afform_type' => \Civi\Api4\OptionValue::get(FALSE) + ->addSelect('name', 'label', 'icon') + ->addWhere('is_active', '=', TRUE) + ->addWhere('option_group_id:name', '=', 'afform_type') + ->addOrderBy('weight', 'ASC') + ->execute(), + ]; + } + /** * Loads metadata for the gui editor. * * FIXME: This is a prototype and should get broken out into separate callbacks with hooks, events, etc. + * @return array */ - public static function getAngularSettings() { + public static function getGuiSettings() { $getFieldParams = [ 'checkPermissions' => FALSE, 'includeCustom' => TRUE, diff --git a/ext/afform/admin/ang/afAdmin.aff.html b/ext/afform/admin/ang/afAdmin.aff.html deleted file mode 100644 index ad443fe3e6..0000000000 --- a/ext/afform/admin/ang/afAdmin.aff.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -
- -
- -
diff --git a/ext/afform/admin/ang/afAdmin.aff.json b/ext/afform/admin/ang/afAdmin.aff.json deleted file mode 100644 index 22078f8bed..0000000000 --- a/ext/afform/admin/ang/afAdmin.aff.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "title": "Afform Administration", - "server_route": "civicrm/admin/afform", - "permission": "administer CiviCRM", - "requires": [ - "afGuiEditor" - ] -} diff --git a/ext/afform/admin/ang/afAdmin.ang.php b/ext/afform/admin/ang/afAdmin.ang.php new file mode 100644 index 0000000000..818a6f4ca0 --- /dev/null +++ b/ext/afform/admin/ang/afAdmin.ang.php @@ -0,0 +1,15 @@ + [ + 'ang/afAdmin.js', + 'ang/afAdmin/*.js', + 'ang/afAdmin/*/*.js', + ], + 'css' => [], + 'partials' => ['ang/afAdmin'], + 'requires' => ['api4', 'afGuiEditor', 'crmRouteBinder'], + 'settingsFactory' => ['CRM_AfformAdmin_Utils', 'getAdminSettings'], + 'basePages' => ['civicrm/admin/afform'], + 'bundles' => ['bootstrap3'], +]; diff --git a/ext/afform/admin/ang/afAdmin.js b/ext/afform/admin/ang/afAdmin.js new file mode 100644 index 0000000000..1284b00bb7 --- /dev/null +++ b/ext/afform/admin/ang/afAdmin.js @@ -0,0 +1,30 @@ +(function(angular, $, _) { + "use strict"; + angular.module('afAdmin', CRM.angRequires('afAdmin')) + + .config(function($routeProvider) { + $routeProvider.when('/', { + controller: 'afAdminList', + reloadOnSearch: false, + templateUrl: '~/afAdmin/afAdminList.html', + resolve: { + // Load data for lists + afforms: function(crmApi4) { + return crmApi4('Afform', 'get', { + select: ['name', 'title', 'type', 'is_public', 'server_route', 'has_local', 'has_base'], + orderBy: {title: 'ASC'} + }); + } + } + }); + $routeProvider.when('/create/:type', { + controller: 'afAdminGui', + template: '', + }); + $routeProvider.when('/edit/:name', { + controller: 'afAdminGui', + template: '', + }); + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/afform/admin/ang/afAdmin/afAdminGui.controller.js b/ext/afform/admin/ang/afAdmin/afAdminGui.controller.js new file mode 100644 index 0000000000..a7713e31b8 --- /dev/null +++ b/ext/afform/admin/ang/afAdmin/afAdminGui.controller.js @@ -0,0 +1,15 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('afAdmin').controller('afAdminGui', function($scope, $routeParams) { + var ts = $scope.ts = CRM.ts(), + ctrl = $scope.$ctrl = this; + + // Edit mode + this.name = $routeParams.name; + // Create mode + this.type = $routeParams.type; + + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/afform/admin/ang/afAdmin/afAdminList.controller.js b/ext/afform/admin/ang/afAdmin/afAdminList.controller.js new file mode 100644 index 0000000000..682b17aa33 --- /dev/null +++ b/ext/afform/admin/ang/afAdmin/afAdminList.controller.js @@ -0,0 +1,51 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('afAdmin').controller('afAdminList', function($scope, afforms, crmApi4, crmStatus) { + var ts = $scope.ts = CRM.ts(), + ctrl = $scope.$ctrl = this; + + $scope.crmUrl = CRM.url; + + this.tabs = CRM.afAdmin.afform_type; + + this.afforms = _.transform(afforms, function(afforms, afform) { + var type = afform.type || 'system'; + afforms[type] = afforms[type] || []; + afforms[type].push(afform); + }, {}); + + $scope.$bindToRoute({ + expr: '$ctrl.tab', + param: 'tab', + format: 'raw', + default: ctrl.tabs[0].name + }); + + this.revert = function(afform) { + var index = _.findIndex(ctrl.afforms[ctrl.tab], {name: afform.name}); + if (index > -1) { + var apiOps = [['Afform', 'revert', {where: [['name', '=', afform.name]]}]]; + if (afform.has_base) { + apiOps.push(['Afform', 'get', { + where: [['name', '=', afform.name]], + select: ['name', 'title', 'type', 'is_public', 'server_route', 'has_local', 'has_base'] + }, 0]); + } + var apiCall = crmStatus( + afform.has_base ? {start: ts('Reverting...')} : {start: ts('Deleting...'), success: ts('Deleted')}, + crmApi4(apiOps) + ); + if (afform.has_base) { + afform.has_local = false; + apiCall.then(function(result) { + ctrl.afforms[ctrl.tab][index] = result[1]; + }); + } else { + ctrl.afforms[ctrl.tab].splice(index, 1); + } + } + }; + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/afform/admin/ang/afAdmin/afAdminList.html b/ext/afform/admin/ang/afAdmin/afAdminList.html new file mode 100644 index 0000000000..80af2d876c --- /dev/null +++ b/ext/afform/admin/ang/afAdmin/afAdminList.html @@ -0,0 +1,59 @@ +
+

{{:: ts('Configurable Forms') }}

+ + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
{{:: ts('Title') }}{{:: ts('Name') }}{{:: ts('Server Route') }}{{:: ts('Frontend?') }}
{{afform.title}} + {{afform.name}} + + + {{afform.server_route}} + + {{afform.is_public ? ts('Frontend') : ts('Backend')}} + {{ ts('Edit') }} + + {{ afform.has_base ? ts('Revert') : ts('Delete') }} + +
+

+ {{:: ts('None Found')}} +

+
+
diff --git a/ext/afform/admin/ang/afAdminList.aff.html b/ext/afform/admin/ang/afAdminList.aff.html deleted file mode 100644 index 8f572b4f8b..0000000000 --- a/ext/afform/admin/ang/afAdminList.aff.html +++ /dev/null @@ -1,55 +0,0 @@ - - {{:: ts('New Form') }} - -
- -
- {{:: ts('None found.') }} -
- - - - - - - - - - - - - - - - - - - - -
{{:: ts('Name') }}{{:: ts('Title') }}{{:: ts('Server Route') }}{{:: ts('Frontend?') }}
- {{availForm.name}} - {{availForm.title}} - - {{availForm.server_route}} - - {{availForm.is_public ? ts('Frontend') : ts('Backend')}} - - {{:: ts('Revert') }} - {{:: ts('Delete') }} -
- -
diff --git a/ext/afform/admin/ang/afGuiEditor.ang.php b/ext/afform/admin/ang/afGuiEditor.ang.php index 99c6a5e70a..4cca733066 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' => ['CRM_AfformAdmin_Utils', 'getAngularSettings'], + 'settingsFactory' => ['CRM_AfformAdmin_Utils', 'getGuiSettings'], 'basePages' => [], 'exports' => [ 'af-gui-editor' => 'E', diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js index 428b903684..5d43f11f4a 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.component.js @@ -5,6 +5,7 @@ angular.module('afGuiEditor').component('afGuiEditor', { templateUrl: '~/afGuiEditor/afGuiEditor.html', bindings: { + type: '<', name: '<' }, controllerAs: 'editor', @@ -18,6 +19,7 @@ var newForm = { title: '', permission: 'access CiviCRM', + type: 'form', layout: [{ '#tag': 'af-form', ctrl: 'afform', @@ -36,7 +38,7 @@ $scope.afform = _.findWhere(afforms, {name: editor.name}); if (!$scope.afform) { $scope.afform = _.cloneDeep(newForm); - if (editor.name != '0') { + if (editor.name) { alert('Error: unknown form "' + editor.name + '"'); } } @@ -45,13 +47,13 @@ editor.layout = afGui.findRecursive($scope.afform.layout, {'#tag': 'af-form'})[0]; $scope.entities = afGui.findRecursive(editor.layout['#children'], {'#tag': 'af-entity'}, 'name'); - if (editor.name == '0') { + if (!editor.name) { editor.addEntity('Individual'); editor.layout['#children'].push(afGui.meta.elements.submit.element); } // Set changesSaved to true on initial load, false thereafter whenever changes are made to the model - $scope.changesSaved = editor.name == '0' ? false : 1; + $scope.changesSaved = !editor.name ? false : 1; $scope.$watch('afform', function () { $scope.changesSaved = $scope.changesSaved === 1; }, true); @@ -154,8 +156,7 @@ .then(function (data) { $scope.saving = false; $scope.afform.name = data[0].name; - // FIXME: This causes an unnecessary reload when saving a new form - $location.search('name', data[0].name); + $location.url('/edit/' + data[0].name); }); }; diff --git a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.html b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.html index acd32c0056..72e4ea3b7b 100644 --- a/ext/afform/admin/ang/afGuiEditor/afGuiEditor.html +++ b/ext/afform/admin/ang/afGuiEditor/afGuiEditor.html @@ -1,4 +1,6 @@ -
-
-
+
+
+
+
+
diff --git a/ext/afform/admin/templates/CRM/AfformAdmin/Page/Base.tpl b/ext/afform/admin/templates/CRM/AfformAdmin/Page/Base.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ext/afform/admin/xml/Menu/afform_admin.xml b/ext/afform/admin/xml/Menu/afform_admin.xml new file mode 100644 index 0000000000..61400dcb25 --- /dev/null +++ b/ext/afform/admin/xml/Menu/afform_admin.xml @@ -0,0 +1,8 @@ + + + + civicrm/admin/afform + CRM_AfformAdmin_Page_Base + administer CiviCRM + + diff --git a/ext/afform/core/Civi/Api4/Afform.php b/ext/afform/core/Civi/Api4/Afform.php index a69ee5c292..ae2a328d0e 100644 --- a/ext/afform/core/Civi/Api4/Afform.php +++ b/ext/afform/core/Civi/Api4/Afform.php @@ -125,6 +125,9 @@ class Afform extends Generic\AbstractEntity { [ 'name' => 'name', ], + [ + 'name' => 'type', + ], [ 'name' => 'requires', ], diff --git a/ext/afform/core/ang/afblockNameHousehold.aff.json b/ext/afform/core/ang/afblockNameHousehold.aff.json index dc0847fc13..c4eee679c6 100644 --- a/ext/afform/core/ang/afblockNameHousehold.aff.json +++ b/ext/afform/core/ang/afblockNameHousehold.aff.json @@ -1,4 +1,5 @@ { "title": "Household Name (default)", + "type": "block", "block": "Household" } diff --git a/ext/afform/core/ang/afblockNameIndividual.aff.json b/ext/afform/core/ang/afblockNameIndividual.aff.json index 3d05402fb0..51c4596ea6 100644 --- a/ext/afform/core/ang/afblockNameIndividual.aff.json +++ b/ext/afform/core/ang/afblockNameIndividual.aff.json @@ -1,4 +1,5 @@ { "title": "Individual Name (default)", + "type": "block", "block": "Individual" } diff --git a/ext/afform/core/ang/afblockNameOrganization.aff.json b/ext/afform/core/ang/afblockNameOrganization.aff.json index 34ab2fd53d..e3ac17c246 100644 --- a/ext/afform/core/ang/afblockNameOrganization.aff.json +++ b/ext/afform/core/ang/afblockNameOrganization.aff.json @@ -1,4 +1,5 @@ { "title": "Organization Name (default)", + "type": "block", "block": "Organization" } diff --git a/ext/afform/core/ang/afjoinAddressDefault.aff.json b/ext/afform/core/ang/afjoinAddressDefault.aff.json index 2a26888f4a..27775770b6 100644 --- a/ext/afform/core/ang/afjoinAddressDefault.aff.json +++ b/ext/afform/core/ang/afjoinAddressDefault.aff.json @@ -1,5 +1,6 @@ { "title": "Address Block (default)", + "type": "block", "block": "Contact", "join": "Address", "repeat": true diff --git a/ext/afform/core/ang/afjoinEmailDefault.aff.json b/ext/afform/core/ang/afjoinEmailDefault.aff.json index b09da6a075..7c50c579dc 100644 --- a/ext/afform/core/ang/afjoinEmailDefault.aff.json +++ b/ext/afform/core/ang/afjoinEmailDefault.aff.json @@ -1,5 +1,6 @@ { "title": "Email (default)", + "type": "block", "block": "Contact", "join": "Email", "repeat": true diff --git a/ext/afform/core/ang/afjoinIMDefault.aff.json b/ext/afform/core/ang/afjoinIMDefault.aff.json index 2ef6b577cb..3ec912975b 100644 --- a/ext/afform/core/ang/afjoinIMDefault.aff.json +++ b/ext/afform/core/ang/afjoinIMDefault.aff.json @@ -1,5 +1,6 @@ { "title": "IM (default)", + "type": "block", "block": "Contact", "join": "IM", "repeat": true diff --git a/ext/afform/core/ang/afjoinPhoneDefault.aff.json b/ext/afform/core/ang/afjoinPhoneDefault.aff.json index c40af8dcb2..821d288840 100644 --- a/ext/afform/core/ang/afjoinPhoneDefault.aff.json +++ b/ext/afform/core/ang/afjoinPhoneDefault.aff.json @@ -1,5 +1,6 @@ { "title": "Phone (default)", + "type": "block", "block": "Contact", "join": "Phone", "repeat": true diff --git a/ext/afform/core/ang/afjoinWebsiteDefault.aff.json b/ext/afform/core/ang/afjoinWebsiteDefault.aff.json index 4819bc92d8..b39dd9cd73 100644 --- a/ext/afform/core/ang/afjoinWebsiteDefault.aff.json +++ b/ext/afform/core/ang/afjoinWebsiteDefault.aff.json @@ -1,5 +1,6 @@ { "title": "Website (default)", + "type": "block", "block": "Contact", "join": "Website", "repeat": true -- 2.25.1