From d102f836425b9b0aa2f9afffd24632d59d02cc04 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 26 Aug 2021 11:29:12 -0400 Subject: [PATCH] SearchKit - Use a searchDisplay instead of ad-hoc table to display main list of saved searches --- CRM/Contact/DAO/SavedSearch.php | 3 +- Civi/Api4/SavedSearch.php | 2 +- ext/search_kit/ang/crmSearchAdmin.module.js | 34 +--- .../crmSearchAdmin/searchList.controller.js | 76 --------- .../ang/crmSearchAdmin/searchList.html | 124 -------------- .../crmSearchAdmin/searchListing/afforms.html | 26 +++ .../crmSearchAdmin/searchListing/buttons.html | 9 + .../crmSearchAdminSearchListing.component.js | 161 ++++++++++++++++++ .../crmSearchAdminSearchListing.html | 12 ++ .../searchListing/displays.html | 16 ++ ext/search_kit/css/crmSearchAdmin.css | 10 -- xml/schema/Contact/SavedSearch.xml | 1 + 12 files changed, 233 insertions(+), 241 deletions(-) delete mode 100644 ext/search_kit/ang/crmSearchAdmin/searchList.controller.js delete mode 100644 ext/search_kit/ang/crmSearchAdmin/searchList.html create mode 100644 ext/search_kit/ang/crmSearchAdmin/searchListing/afforms.html create mode 100644 ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html create mode 100644 ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js create mode 100644 ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html create mode 100644 ext/search_kit/ang/crmSearchAdmin/searchListing/displays.html diff --git a/CRM/Contact/DAO/SavedSearch.php b/CRM/Contact/DAO/SavedSearch.php index 84f3e161e6..c8fff17301 100644 --- a/CRM/Contact/DAO/SavedSearch.php +++ b/CRM/Contact/DAO/SavedSearch.php @@ -6,7 +6,7 @@ * * Generated from xml/schema/CRM/Contact/SavedSearch.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:5e5799b7755c435363f2c8cdafd13055) + * (GenCodeChecksum:bca384578ea59c0cf4f0a80617c788fe) */ /** @@ -219,6 +219,7 @@ class CRM_Contact_DAO_SavedSearch extends CRM_Core_DAO { 'localizable' => 0, 'html' => [ 'type' => 'Text', + 'label' => ts("Label"), ], 'add' => '5.32', ], diff --git a/Civi/Api4/SavedSearch.php b/Civi/Api4/SavedSearch.php index ddf6704add..74dd21cef1 100644 --- a/Civi/Api4/SavedSearch.php +++ b/Civi/Api4/SavedSearch.php @@ -16,7 +16,7 @@ namespace Civi\Api4; * Stores search parameters for populating smart groups with live results. * * @see https://docs.civicrm.org/user/en/latest/organising-your-data/smart-groups/ - * @searchable none + * @searchable secondary * @since 5.24 * @package Civi\Api4 */ diff --git a/ext/search_kit/ang/crmSearchAdmin.module.js b/ext/search_kit/ang/crmSearchAdmin.module.js index 52bad76c33..265d5708ac 100644 --- a/ext/search_kit/ang/crmSearchAdmin.module.js +++ b/ext/search_kit/ang/crmSearchAdmin.module.js @@ -11,34 +11,10 @@ .config(function($routeProvider) { $routeProvider.when('/list', { - controller: 'searchList', - templateUrl: '~/crmSearchAdmin/searchList.html', - resolve: { - // Load data for lists - savedSearches: function(crmApi4) { - return crmApi4('SavedSearch', 'get', { - select: [ - 'id', - 'name', - 'label', - 'api_entity', - 'api_params', - 'created_id.display_name', - 'modified_id.display_name', - 'created_date', - 'modified_date', - 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name', - 'GROUP_CONCAT(display.label ORDER BY display.id) AS display_label', - 'GROUP_CONCAT(display.type:icon ORDER BY display.id) AS display_icon', - 'GROUP_CONCAT(display.acl_bypass ORDER BY display.id) AS display_acl_bypass', - 'GROUP_CONCAT(DISTINCT group.title) AS groups' - ], - join: [['SearchDisplay AS display'], ['Group AS group']], - where: [['api_entity', 'IS NOT NULL']], - groupBy: ['id'] - }); - } - } + controller: function() { + searchEntity = 'SavedSearch'; + }, + template: '', }); $routeProvider.when('/create/:entity', { controller: 'searchCreate', @@ -205,7 +181,7 @@ key: info.alias, dataType: (info.fn && info.fn.dataType) || (info.field && info.field.data_type) }, defaults); - if (defaults.label) { + if (defaults.label === true) { values.label = getDefaultLabel(fieldExpr); } return values; diff --git a/ext/search_kit/ang/crmSearchAdmin/searchList.controller.js b/ext/search_kit/ang/crmSearchAdmin/searchList.controller.js deleted file mode 100644 index 2266b65d7c..0000000000 --- a/ext/search_kit/ang/crmSearchAdmin/searchList.controller.js +++ /dev/null @@ -1,76 +0,0 @@ -(function(angular, $, _) { - "use strict"; - - angular.module('crmSearchAdmin').controller('searchList', function($scope, savedSearches, crmApi4, searchMeta) { - var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), - ctrl = $scope.$ctrl = this; - $scope.formatDate = CRM.utils.formatDate; - this.savedSearches = savedSearches; - this.sortField = 'modified_date'; - this.sortDir = true; - this.afformEnabled = CRM.crmSearchAdmin.afformEnabled; - this.afformAdminEnabled = CRM.crmSearchAdmin.afformAdminEnabled; - - _.each(savedSearches, function(search) { - search.entity_title = searchMeta.getEntity(search.api_entity).title_plural; - search.permissionToEdit = CRM.checkPerm('all CiviCRM permissions and ACLs') || !_.includes(search.display_acl_bypass, true); - search.afform_count = 0; - }); - - this.searchPath = CRM.url('civicrm/search'); - this.afformPath = CRM.url('civicrm/admin/afform'); - - this.encode = function(params) { - return encodeURI(angular.toJson(params)); - }; - - // Change sort field/direction when clicking a column header - this.sortBy = function(col) { - ctrl.sortDir = ctrl.sortField === col ? !ctrl.sortDir : false; - ctrl.sortField = col; - }; - - this.deleteSearch = function(search) { - var index = _.findIndex(savedSearches, {id: search.id}); - if (index > -1) { - crmApi4([ - ['Group', 'delete', {where: [['saved_search_id', '=', search.id]]}], - ['SavedSearch', 'delete', {where: [['id', '=', search.id]]}] - ]); - savedSearches.splice(index, 1); - } - }; - - this.loadAfforms = function() { - if (ctrl.afforms || ctrl.afforms === null) { - return; - } - ctrl.afforms = null; - crmApi4('Afform', 'get', { - select: ['layout', 'name', 'title', 'server_route'], - where: [['type', '=', 'search']], - layoutFormat: 'html' - }).then(function(afforms) { - ctrl.afforms = {}; - _.each(afforms, function(afform) { - var searchName = afform.layout.match(/]+search-name[ ]*=[ ]*['"]([^"']+)/); - if (searchName) { - var search = _.find(ctrl.savedSearches, {name: searchName[1]}); - if (search) { - search.afform_count++; - ctrl.afforms[searchName[1]] = ctrl.afforms[searchName[1]] || []; - ctrl.afforms[searchName[1]].push({ - title: afform.title, - name: afform.name, - // FIXME: This is the view url, currently not exposed to the UI, as BS3 doesn't support submenus. - url: afform.server_route ? CRM.url(afform.server_route) : null - }); - } - } - }); - }); - }; - - }); - -})(angular, CRM.$, CRM._); diff --git a/ext/search_kit/ang/crmSearchAdmin/searchList.html b/ext/search_kit/ang/crmSearchAdmin/searchList.html deleted file mode 100644 index 9d27013163..0000000000 --- a/ext/search_kit/ang/crmSearchAdmin/searchList.html +++ /dev/null @@ -1,124 +0,0 @@ - diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/afforms.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/afforms.html new file mode 100644 index 0000000000..12f6a40387 --- /dev/null +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/afforms.html @@ -0,0 +1,26 @@ +
+ + +
diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html new file mode 100644 index 0000000000..ee5eff3621 --- /dev/null +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/buttons.html @@ -0,0 +1,9 @@ + + {{:: ts('Edit') }} + + + {{:: ts('Clone') }} + + + {{:: ts('Delete') }} + diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js new file mode 100644 index 0000000000..038ec00c2e --- /dev/null +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js @@ -0,0 +1,161 @@ +(function(angular, $, _) { + "use strict"; + + // Specialized searchDisplay, only used by Admins + angular.module('crmSearchAdmin').component('crmSearchAdminSearchListing', { + templateUrl: '~/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html', + controller: function($scope, crmApi4, crmStatus, searchMeta, searchDisplayBaseTrait, searchDisplaySortableTrait) { + var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), + // Mix in traits to this controller + ctrl = angular.extend(this, searchDisplayBaseTrait, searchDisplaySortableTrait); + + this.searchDisplayPath = CRM.url('civicrm/search'); + this.afformPath = CRM.url('civicrm/admin/afform'); + this.afformEnabled = CRM.crmSearchAdmin.afformEnabled; + this.afformAdminEnabled = CRM.crmSearchAdmin.afformAdminEnabled; + + this.apiEntity = 'SavedSearch'; + this.search = { + api_entity: 'SavedSearch', + api_params: { + version: 4, + select: [ + 'id', + 'name', + 'label', + 'api_entity', + 'api_entity:label', + 'api_params', + 'created_date', + 'modified_date', + 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name', + 'GROUP_CONCAT(display.label ORDER BY display.id) AS display_label', + 'GROUP_CONCAT(display.type:icon ORDER BY display.id) AS display_icon', + 'GROUP_CONCAT(display.acl_bypass ORDER BY display.id) AS display_acl_bypass', + 'GROUP_CONCAT(DISTINCT group.title) AS groups' + ], + join: [['SearchDisplay AS display'], ['Group AS group']], + where: [['api_entity', 'IS NOT NULL']], + groupBy: ['id'] + } + }; + + this.$onInit = function() { + buildDisplaySettings(); + this.initializeDisplay($scope, $()); + }; + + this.onPostRun.push(function(result) { + _.each(result, function(row) { + row.permissionToEdit = CRM.checkPerm('all CiviCRM permissions and ACLs') || !_.includes(row.display_acl_bypass.raw, true); + // Saves rendering cycles to not show an empty menu of search displays + if (!row.display_name.raw) { + row.openDisplayMenu = false; + } + }); + updateAfformCounts(); + }); + + this.encode = function(params) { + return encodeURI(angular.toJson(params)); + }; + + this.deleteSearch = function(search) { + crmStatus({start: ts('Deleting...'), success: ts('Search Deleted')}, + crmApi4('SavedSearch', 'delete', {where: [['id', '=', search.id.raw]]}).then(function() { + ctrl.rowCount = null; + ctrl.runSearch(); + }) + ); + }; + + function buildDisplaySettings() { + ctrl.display = { + type: 'table', + settings: { + limit: CRM.crmSearchAdmin.defaultPagerSize, + pager: {show_count: true, expose_limit: true}, + actions: false, + sort: [['modified_date', 'DESC']], + columns: [ + searchMeta.fieldToColumn('label', { + label: true, + title: ts('Edit Label'), + editable: {entity: 'SavedSearch', id: 'id', name: 'label', value: 'label'} + }), + searchMeta.fieldToColumn('api_entity:label', { + label: ts('For'), + }), + { + 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'), + dataType: 'Date', + rewrite: ts('%1 by %2', {1: '[created_date]', 2: '[created_id.display_name]'}) + }), + searchMeta.fieldToColumn('modified_date', { + label: ts('Last Modified'), + dataType: 'Date', + rewrite: ts('%1 by %2', {1: '[modified_date]', 2: '[modified_id.display_name]'}) + }), + { + type: 'include', + alignment: 'text-right', + path: '~/crmSearchAdmin/searchListing/buttons.html' + } + ] + } + }; + if (ctrl.afformEnabled) { + ctrl.display.settings.columns.splice(3, 0, { + type: 'include', + label: ts('Forms'), + path: '~/crmSearchAdmin/searchListing/afforms.html' + }); + } + ctrl.settings = ctrl.display.settings; + } + + this.loadAfforms = function() { + if (ctrl.afforms || ctrl.afforms === null) { + return; + } + ctrl.afforms = null; + crmApi4('Afform', 'get', { + select: ['layout', 'name', 'title', 'server_route'], + where: [['type', '=', 'search']], + layoutFormat: 'html' + }).then(function(afforms) { + ctrl.afforms = {}; + _.each(afforms, function(afform) { + var searchName = afform.layout.match(/]+search-name[ ]*=[ ]*['"]([^"']+)/); + if (searchName) { + ctrl.afforms[searchName[1]] = ctrl.afforms[searchName[1]] || []; + ctrl.afforms[searchName[1]].push({ + title: afform.title, + name: afform.name, + // FIXME: This is the view url, currently not exposed to the UI, as BS3 doesn't support submenus. + url: afform.server_route ? CRM.url(afform.server_route) : null + }); + } + }); + updateAfformCounts(); + }); + }; + + function updateAfformCounts() { + _.each(ctrl.results, function(row) { + row.afform_count = ctrl.afforms && ctrl.afforms[row.name.raw] && ctrl.afforms[row.name.raw].length || 0; + }); + } + + } + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html new file mode 100644 index 0000000000..b2e0db8b3f --- /dev/null +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.html @@ -0,0 +1,12 @@ + diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/displays.html b/ext/search_kit/ang/crmSearchAdmin/searchListing/displays.html new file mode 100644 index 0000000000..c16c66eb9b --- /dev/null +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/displays.html @@ -0,0 +1,16 @@ +
+ + + +
diff --git a/ext/search_kit/css/crmSearchAdmin.css b/ext/search_kit/css/crmSearchAdmin.css index cf3b0743c9..bdcf4123c5 100644 --- a/ext/search_kit/css/crmSearchAdmin.css +++ b/ext/search_kit/css/crmSearchAdmin.css @@ -1,13 +1,3 @@ -#bootstrap-theme.crm-search-admin-list th[ng-click] { - cursor: pointer; -} -#bootstrap-theme.crm-search-admin-list th i.fa-sort-desc, -#bootstrap-theme.crm-search-admin-list th i.fa-sort-asc { - color: #1a5a82; -} -#bootstrap-theme.crm-search-admin-list th:not(:hover) i.fa-sort { - opacity: .5; -} #bootstrap-theme .crm-search-criteria-column { min-width: 500px; diff --git a/xml/schema/Contact/SavedSearch.xml b/xml/schema/Contact/SavedSearch.xml index d0146e6709..77f5b37229 100644 --- a/xml/schema/Contact/SavedSearch.xml +++ b/xml/schema/Contact/SavedSearch.xml @@ -49,6 +49,7 @@ NULL Administrative label for search + Text 5.32 -- 2.25.1