From 3bbce6e9bf77316a91c78317a0c2e14b4535cb04 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 5 Nov 2021 15:49:28 -0400 Subject: [PATCH] SearchKit - tabbed display for custom vs packaged searches Splits the SearchKit admin UI in to 2 tabs - one for custom (locally-defined) searches, and one for packaged searches. Adds a "revert" button for packaged searches. --- CRM/Core/ManagedEntities.php | 1 + ext/search_kit/Civi/Api4/SearchDisplay.php | 2 +- ext/search_kit/Civi/Search/Admin.php | 2 + .../crmSearchAdmin/searchListing/buttons.html | 4 +- .../crmSearchAdminSearchListing.component.js | 158 +++++++++++++----- .../crmSearchAdminSearchListing.html | 23 ++- 6 files changed, 148 insertions(+), 42 deletions(-) diff --git a/CRM/Core/ManagedEntities.php b/CRM/Core/ManagedEntities.php index a4d2096ae7..9b54a26ff9 100644 --- a/CRM/Core/ManagedEntities.php +++ b/CRM/Core/ManagedEntities.php @@ -143,6 +143,7 @@ class CRM_Core_ManagedEntities { $mgd = new \CRM_Core_DAO_Managed(); $mgd->copyValues($params); $mgd->find(TRUE); + $this->loadDeclarations(); $declarations = CRM_Utils_Array::findAll($this->declarations, [ 'module' => $mgd->module, 'name' => $mgd->name, diff --git a/ext/search_kit/Civi/Api4/SearchDisplay.php b/ext/search_kit/Civi/Api4/SearchDisplay.php index 8ab71220e8..58e37d483c 100644 --- a/ext/search_kit/Civi/Api4/SearchDisplay.php +++ b/ext/search_kit/Civi/Api4/SearchDisplay.php @@ -11,7 +11,7 @@ namespace Civi\Api4; */ class SearchDisplay extends Generic\DAOEntity { - use \Civi\Api4\Generic\Traits\ManagedEntity; + use Generic\Traits\ManagedEntity; /** * @param bool $checkPermissions diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php index 96ecf41db4..01de7d95ae 100644 --- a/ext/search_kit/Civi/Search/Admin.php +++ b/ext/search_kit/Civi/Search/Admin.php @@ -42,6 +42,8 @@ class Admin { 'defaultDisplay' => SearchDisplay::getDefault(FALSE)->setSavedSearch(['id' => NULL])->execute()->first(), 'afformEnabled' => $extensions->isActiveModule('afform'), 'afformAdminEnabled' => $extensions->isActiveModule('afform_admin'), + // TODO: Add v4 API for Extensions + 'modules' => array_column(civicrm_api3('Extension', 'get', ['status' => "installed"])['values'], 'label', 'key'), 'tags' => Tag::get() ->addSelect('id', 'name', 'color', 'is_selectable', 'description') ->addWhere('used_for', 'CONTAINS', 'civicrm_saved_search') diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html index 29cf615ec7..61611498c3 100644 --- a/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html @@ -7,6 +7,6 @@ {{:: ts('Clone') }} - - {{:: ts('Delete') }} + + {{ row.data['base_module:label'] ? ts('Revert') : ts('Delete') }} diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js index 6509a16569..82d690bea9 100644 --- a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js @@ -15,6 +15,12 @@ this.afformEnabled = CRM.crmSearchAdmin.afformEnabled; this.afformAdminEnabled = CRM.crmSearchAdmin.afformAdminEnabled; this.entitySelect = searchMeta.getPrimaryAndSecondaryEntitySelect(); + this.modules = _.sortBy(_.transform((CRM.crmSearchAdmin.modules), function(modules, label, key) { + modules.push({text: label, id: key}); + }, []), 'text'); + + this.filters = {has_base: false}; + this.totals = {}; this.apiEntity = 'SavedSearch'; this.search = { @@ -33,6 +39,8 @@ 'modified_id.display_name', 'created_date', 'modified_date', + 'has_base', + 'base_module:label', 'DATE(created_date) AS date_created', 'DATE(modified_date) AS date_modified', 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name', @@ -56,6 +64,27 @@ this.$onInit = function() { buildDisplaySettings(); this.initializeDisplay($scope, $()); + // Keep tab counts up-to-date - put rowCount in current tab if there are no other filters + $scope.$watch('$ctrl.rowCount', function(val) { + if (typeof val === 'number' && angular.equals({has_base: true}, ctrl.filters)) { + ctrl.totals.has_base = val; + } + else if (typeof val === 'number' && angular.equals({has_base: false}, ctrl.filters)) { + ctrl.totals.no_base = val; + } + }); + // Initialize count for inactive tab + var params = ctrl.getApiParams('row_count'); + params.filters.has_base = true; + crmApi4('SearchDisplay', 'run', params).then(function(result) { + ctrl.totals.has_base = result.count; + }); + }; + + // Change tabs and clear other filters + this.setHasBaseFilter = function(val) { + ctrl.filters = {has_base: val}; + buildDisplaySettings(); }; this.onPostRun.push(function(result) { @@ -73,43 +102,77 @@ return encodeURI(angular.toJson(params)); }; - this.confirmDelete = function(search) { - function getConfirmationMsg() { - var msg = '

' + _.escape(ts('Permanently delete this saved search?')) + '

' + + this.deleteOrRevert = function(row) { + var search = row.data, + revert = !!search['base_module:label']; + function getMessage() { + var title = revert ? ts('Revert this search to its packaged settings?') : ts('Permanently delete this saved search?'), + msg = '

' + _.escape(title) + '

' + ''; } var dialog = CRM.confirm({ - title: ts('Delete %1', {1: search.data.label}), - message: getConfirmationMsg(), + title: revert ? ts('Revert %1', {1: search.label}) : ts('Delete %1', {1: search.label}), + message: getMessage(), }).on('crmConfirm:yes', function() { $scope.$apply(function() { - ctrl.deleteSearch(search); + return revert ? ctrl.revertSearch(search) : ctrl.deleteSearch(search); }); }).block(); ctrl.loadAfforms().then(function() { - dialog.html(getConfirmationMsg()).unblock(); + dialog.html(getMessage()).unblock(); }); }; this.deleteSearch = function(search) { crmStatus({start: ts('Deleting...'), success: ts('Search Deleted')}, - crmApi4('SavedSearch', 'delete', {where: [['id', '=', search.data.id]]}).then(function() { + crmApi4('SavedSearch', 'delete', {where: [['id', '=', search.id]]}).then(function() { + ctrl.rowCount = null; + ctrl.runSearch(); + }) + ); + }; + + this.revertSearch = function(search) { + crmStatus({start: ts('Reverting...'), success: ts('Search Reverted')}, + crmApi4('SavedSearch', 'revert', { + where: [['id', '=', search.id]], + chain: { + revertDisplays: ['SearchDisplay', 'revert', {'where': [['saved_search_id', '=', '$id'], ['has_base', '=', true]]}], + deleteDisplays: ['SearchDisplay', 'delete', {'where': [['saved_search_id', '=', '$id'], ['has_base', '=', false]]}] + } + }).then(function() { ctrl.rowCount = null; ctrl.runSearch(); }) @@ -147,35 +210,54 @@ type: 'include', label: ts('Displays'), path: '~/crmSearchAdmin/searchListing/displays.html' - }, - searchMeta.fieldToColumn('GROUP_CONCAT(DISTINCT group.title) AS groups', { - label: ts('Smart Group') - }), - searchMeta.fieldToColumn('created_date', { - label: ts('Created'), - title: '[created_date]', - rewrite: ts('%1 by %2', {1: '[date_created]', 2: '[created_id.display_name]'}) - }), - searchMeta.fieldToColumn('modified_date', { - label: ts('Last Modified'), - title: '[modified_date]', - rewrite: ts('%1 by %2', {1: '[date_modified]', 2: '[modified_id.display_name]'}) - }), - { - type: 'include', - alignment: 'text-right', - path: '~/crmSearchAdmin/searchListing/buttons.html' } ] } }; if (ctrl.afformEnabled) { - ctrl.display.settings.columns.splice(4, 0, { + ctrl.display.settings.columns.push({ type: 'include', label: ts('Forms'), path: '~/crmSearchAdmin/searchListing/afforms.html' }); } + ctrl.display.settings.columns.push( + searchMeta.fieldToColumn('GROUP_CONCAT(DISTINCT group.title) AS groups', { + label: ts('Smart Group') + }) + ); + if (ctrl.filters.has_base) { + ctrl.display.settings.columns.push( + searchMeta.fieldToColumn('base_module:label', { + label: ts('Package'), + title: '[base_module]', + empty_value: ts('Missing'), + cssRules: [ + ['font-italic', 'base_module:label', 'IS EMPTY'] + ] + }) + ); + } else { + ctrl.display.settings.columns.push( + searchMeta.fieldToColumn('created_date', { + label: ts('Created'), + title: '[created_date]', + rewrite: ts('%1 by %2', {1: '[date_created]', 2: '[created_id.display_name]'}) + }) + ); + } + ctrl.display.settings.columns.push( + searchMeta.fieldToColumn('modified_date', { + label: ts('Last Modified'), + title: '[modified_date]', + rewrite: ts('%1 by %2', {1: '[date_modified]', 2: '[modified_id.display_name]'}) + }) + ); + ctrl.display.settings.columns.push({ + type: 'include', + alignment: 'text-right', + path: '~/crmSearchAdmin/searchListing/buttons.html' + }); ctrl.settings = ctrl.display.settings; } diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html index 9bab879770..84e8358a4f 100644 --- a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html @@ -1,8 +1,29 @@