From ef1cbd865976e4e34905d0a178b8d03373c90766 Mon Sep 17 00:00:00 2001 From: colemanw Date: Sat, 27 May 2023 23:21:26 -0400 Subject: [PATCH] SearchKit - Refactor bulk task management into a class --- .../crmSearchAdminResultsTable.component.js | 8 +- .../crmSearchAdminResultsTable.html | 2 +- .../crmSearchAdminSearchListing.component.js | 10 +- .../crmSearchAdminSegmentListing.component.js | 2 +- .../ang/crmSearchDisplay/colType/field.html | 2 +- .../traits/searchDisplayBaseTrait.service.js | 20 ++-- .../crmSearchDisplayTable.component.js | 8 +- .../crmSearchDisplayTable.html | 2 +- .../crmSearchTaskDownload.ctrl.js | 2 +- .../crmSearchTasks/crmSearchTaskDownload.html | 2 +- .../crmSearchTasks.component.js | 107 ------------------ .../crmSearchTasksMenu.component.js | 46 ++++++++ ...archTasks.html => crmSearchTasksMenu.html} | 12 +- .../traits/searchDisplayTasksTrait.service.js | 103 +++++++++++++++-- .../traits/searchTaskBaseTrait.service.js | 2 +- 15 files changed, 175 insertions(+), 153 deletions(-) delete mode 100644 ext/search_kit/ang/crmSearchTasks/crmSearchTasks.component.js create mode 100644 ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.component.js rename ext/search_kit/ang/crmSearchTasks/{crmSearchTasks.html => crmSearchTasksMenu.html} (51%) diff --git a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js index a9ba405422..68c2dbda74 100644 --- a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js @@ -42,12 +42,12 @@ }; // Add callbacks for pre & post run - this.onPreRun.push(function(apiParams) { - apiParams.debug = true; + this.onPreRun.push(function(apiCalls) { + apiCalls.run.debug = true; }); - this.onPostRun.push(function(result) { - ctrl.debug = _.extend(_.pick(ctrl.debug, 'apiParams'), result.debug); + this.onPostRun.push(function(apiResults) { + ctrl.debug = _.extend(_.pick(ctrl.debug, 'apiParams'), apiResults.run.debug); }); $scope.sortableColumnOptions = { diff --git a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html index da300b4a96..34fcf8dd84 100644 --- a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html +++ b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html @@ -2,7 +2,7 @@
- +
diff --git a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js index 8c23e2f94b..977e965f69 100644 --- a/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/searchListing/crmSearchAdminSearchListing.component.js @@ -87,8 +87,8 @@ })); } - this.onPostRun.push(function(result) { - _.each(result, function(row) { + this.onPostRun.push(function(apiResults) { + _.each(apiResults.run, function(row) { row.permissionToEdit = CRM.checkPerm('all CiviCRM permissions and ACLs') || !_.includes(row.data.display_acl_bypass, true); // If main entity doesn't exist, no can edit if (!row.data['api_entity:label']) { @@ -161,7 +161,7 @@ this.deleteSearch = function(row) { ctrl.runSearch( - [['SavedSearch', 'delete', {where: [['id', '=', row.key]]}]], + {deleteSearch: ['SavedSearch', 'delete', {where: [['id', '=', row.key]]}]}, {start: ts('Deleting...'), success: ts('Search Deleted')}, row ); @@ -169,13 +169,13 @@ this.revertSearch = function(row) { ctrl.runSearch( - [['SavedSearch', 'revert', { + {revertSearch: ['SavedSearch', 'revert', { where: [['id', '=', row.key]], chain: { revertDisplays: ['SearchDisplay', 'revert', {'where': [['saved_search_id', '=', '$id'], ['has_base', '=', true]]}], deleteDisplays: ['SearchDisplay', 'delete', {'where': [['saved_search_id', '=', '$id'], ['has_base', '=', false]]}] } - }]], + }]}, {start: ts('Reverting...'), success: ts('Search Reverted')}, row ); diff --git a/ext/search_kit/ang/crmSearchAdmin/searchSegmentListing/crmSearchAdminSegmentListing.component.js b/ext/search_kit/ang/crmSearchAdmin/searchSegmentListing/crmSearchAdminSegmentListing.component.js index c72ef73ada..9cf52d621b 100644 --- a/ext/search_kit/ang/crmSearchAdmin/searchSegmentListing/crmSearchAdminSegmentListing.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/searchSegmentListing/crmSearchAdminSegmentListing.component.js @@ -40,7 +40,7 @@ this.deleteSegment = function(row) { ctrl.runSearch( - [['SearchSegment', 'delete', {where: [['id', '=', row.key]]}]], + {deleteSegment: ['SearchSegment', 'delete', {where: [['id', '=', row.key]]}]}, {start: ts('Deleting...'), success: ts('Segment Deleted')}, row ); diff --git a/ext/search_kit/ang/crmSearchDisplay/colType/field.html b/ext/search_kit/ang/crmSearchDisplay/colType/field.html index e0493dc0ea..f2d6d2cd61 100644 --- a/ext/search_kit/ang/crmSearchDisplay/colType/field.html +++ b/ext/search_kit/ang/crmSearchDisplay/colType/field.html @@ -1,4 +1,4 @@ - + {{:: $ctrl.formatFieldValue(colData) }} diff --git a/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js b/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js index d40b30ce6b..617a8a841a 100644 --- a/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js +++ b/ext/search_kit/ang/crmSearchDisplay/traits/searchDisplayBaseTrait.service.js @@ -10,6 +10,7 @@ page: 1, rowCount: null, // Arrays may contain callback functions for various events + onInitialize: [], onChangeFilters: [], onPreRun: [], onPostRun: [], @@ -26,6 +27,9 @@ for (var p=0; p < placeholderCount; ++p) { this.placeholders.push({}); } + _.each(ctrl.onInitialize, function(callback) { + callback.call(ctrl, $scope, $element); + }); // _.debounce used here to trigger the initial search immediately but prevent subsequent launches within 300ms this.getResultsPronto = _.debounce(ctrl.runSearch, 300, {leading: true, trailing: false}); @@ -81,9 +85,9 @@ if (this.afFieldset) { $scope.$watch(this.afFieldset.getFieldData, onChangeFilters, true); // Add filter title to Afform - this.onPostRun.push(function(results) { - if (results.labels && results.labels.length && $scope.$parent.addTitle) { - $scope.$parent.addTitle(results.labels.join(' ')); + this.onPostRun.push(function(apiResults) { + if (apiResults.run.labels && apiResults.run.labels.length && $scope.$parent.addTitle) { + $scope.$parent.addTitle(apiResults.run.labels.join(' ')); } }); } @@ -145,17 +149,17 @@ if (!statusParams) { this.loading = true; } + apiCalls = apiCalls || {}; + apiCalls.run = ['SearchDisplay', 'run', apiParams]; _.each(ctrl.onPreRun, function(callback) { - callback.call(ctrl, apiParams); + callback.call(ctrl, apiCalls); }); - apiCalls = apiCalls || []; - apiCalls.push(['SearchDisplay', 'run', apiParams]); var apiRequest = crmApi4(apiCalls); apiRequest.then(function(apiResults) { if (requestId < ctrl._runCount) { return; // Another request started after this one } - ctrl.results = _.last(apiResults); + ctrl.results = apiResults.run; ctrl.editing = ctrl.loading = false; // Update rowCount if running for the first time or during an update op if (!ctrl.rowCount || editedRow) { @@ -178,7 +182,7 @@ } } _.each(ctrl.onPostRun, function(callback) { - callback.call(ctrl, ctrl.results, 'success', editedRow); + callback.call(ctrl, apiResults, 'success', editedRow); }); }, function(error) { if (requestId < ctrl._runCount) { diff --git a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js index 04f8120ffa..59372abd76 100644 --- a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js +++ b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js @@ -23,12 +23,12 @@ var tallyParams; if (ctrl.settings.tally) { - ctrl.onPreRun.push(function (apiParams) { + ctrl.onPreRun.push(function (apiCalls) { ctrl.tally = null; - tallyParams = _.cloneDeep(apiParams); + tallyParams = _.cloneDeep(apiCalls.run); }); - ctrl.onPostRun.push(function (results, status) { + ctrl.onPostRun.push(function (apiResults, status) { ctrl.tally = null; if (status === 'success' && tallyParams) { tallyParams.return = 'tally'; @@ -65,7 +65,7 @@ updateParams = {where: [['id', '=', movedItem.data.id]], values: {}}; if (newPosition > -1 && oldPosition !== newPosition) { updateParams.values[weightColumn] = displacedItem.data[weightColumn]; - ctrl.runSearch([[ctrl.apiEntity, 'update', updateParams]], {}, movedItem); + ctrl.runSearch({updateWeight: [ctrl.apiEntity, 'update', updateParams]}, {}, movedItem); } }); } diff --git a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html index d9df2cda75..8f9437ea41 100644 --- a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html +++ b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.html @@ -2,7 +2,7 @@
{{:: $ctrl.settings.description }}
- +
diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.ctrl.js b/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.ctrl.js index 8482eca9fe..3166bce5f6 100644 --- a/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.ctrl.js +++ b/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.ctrl.js @@ -19,7 +19,7 @@ ctrl.progress += 10; } }, 1000); - var apiParams = ctrl.displayController.getApiParams(); + var apiParams = ctrl.taskManager.getApiParams(); delete apiParams.return; delete apiParams.limit; apiParams.filters.id = ctrl.ids || null; diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.html b/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.html index 1271deda0c..5a510d16db 100644 --- a/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.html +++ b/ext/search_kit/ang/crmSearchTasks/crmSearchTaskDownload.html @@ -2,7 +2,7 @@

{{:: ts('Download %1 %2', {1: $ctrl.ids.length, 2: $ctrl.entityTitle}) }} - {{:: ts('Download %1 %2', {1: $ctrl.displayController.rowCount, 2: $ctrl.entityTitle}) }} + {{:: ts('Download %1 %2', {1: $ctrl.taskManager.getRowCount(), 2: $ctrl.entityTitle}) }}

diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchTasks.component.js b/ext/search_kit/ang/crmSearchTasks/crmSearchTasks.component.js deleted file mode 100644 index 6b1d7d59bf..0000000000 --- a/ext/search_kit/ang/crmSearchTasks/crmSearchTasks.component.js +++ /dev/null @@ -1,107 +0,0 @@ -(function(angular, $, _) { - "use strict"; - - angular.module('crmSearchTasks').component('crmSearchTasks', { - bindings: { - entity: '<', - refresh: '&', - search: '<', - display: '<', - displayController: '<', - ids: '<' - }, - templateUrl: '~/crmSearchTasks/crmSearchTasks.html', - controller: function($scope, crmApi4, dialogService, $window) { - var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), - ctrl = this, - initialized = false, - unwatchIDs = $scope.$watch('$ctrl.ids.length', watchIDs); - - function watchIDs() { - if (ctrl.ids && ctrl.ids.length) { - unwatchIDs(); - ctrl.getTasks(); - } - } - - this.getTasks = function() { - if (initialized) { - return; - } - initialized = true; - crmApi4({ - entityInfo: ['Entity', 'get', {select: ['name', 'title', 'title_plural', 'primary_key'], where: [['name', '=', ctrl.entity]]}, 0], - tasks: ['SearchDisplay', 'getSearchTasks', {entity: ctrl.entity, savedSearch: ctrl.search, display: ctrl.display}] - }).then(function(result) { - ctrl.entityInfo = result.entityInfo; - ctrl.tasks = result.tasks; - }); - }; - - this.isActionAllowed = function(action) { - return $scope.$eval('' + ctrl.ids.length + action.number); - }; - - this.updateActionData = function() { - if (this.entity === 'RelationshipCache') { - this.entity = 'Relationship'; - this.entityInfo.title = ts('Relationship'); - this.entityInfo.title_plural = ts('Relationships'); - } - }; - - this.getActionTitle = function(action) { - if (ctrl.isActionAllowed(action)) { - ctrl.updateActionData(); - return ctrl.ids.length ? - ts('Perform action on %1 %2', {1: ctrl.ids.length, 2: ctrl.entityInfo[ctrl.ids.length === 1 ? 'title' : 'title_plural']}) : - ts('Perform action on all %1', {1: ctrl.entityInfo.title_plural}); - } - return ts('Selected number must be %1', {1: action.number.replace('===', '')}); - }; - - this.doAction = function(action) { - if (!ctrl.isActionAllowed(action)) { - return; - } - // Update data specific to entity actions. - ctrl.updateActionData(); - - var data = { - ids: ctrl.ids, - entity: ctrl.entity, - search: ctrl.search, - display: ctrl.display, - displayController: ctrl.displayController, - entityInfo: ctrl.entityInfo, - taskTitle: action.title, - apiBatch: _.cloneDeep(action.apiBatch) - }; - // If action uses a crmPopup form - if (action.crmPopup) { - var path = $scope.$eval(action.crmPopup.path, data), - query = action.crmPopup.query && $scope.$eval(action.crmPopup.query, data); - CRM.loadForm(CRM.url(path, query), {post: action.crmPopup.data && $scope.$eval(action.crmPopup.data, data)}) - .on('crmFormSuccess', ctrl.refresh); - } - else if (action.redirect) { - var redirectPath = $scope.$eval(action.redirect.path, data), - redirectQuery = action.redirect.query && $scope.$eval(action.redirect.query, data) && $scope.$eval(action.redirect.data, data); - $window.open(CRM.url(redirectPath, redirectQuery), '_blank'); - } - // If action uses dialogService - else { - var options = CRM.utils.adjustDialogDefaults({ - autoOpen: false, - dialogClass: 'crm-search-task-dialog', - title: action.title - }); - dialogService.open('crmSearchTask', (action.uiDialog && action.uiDialog.templateUrl) || '~/crmSearchTasks/crmSearchTaskApiBatch.html', data, options) - // Reload results on success, do nothing on cancel - .then(ctrl.refresh, _.noop); - } - }; - } - }); - -})(angular, CRM.$, CRM._); diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.component.js b/ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.component.js new file mode 100644 index 0000000000..f8fd366bf0 --- /dev/null +++ b/ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.component.js @@ -0,0 +1,46 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('crmSearchTasks').component('crmSearchTasksMenu', { + bindings: { + taskManager: '<', + ids: '<' + }, + templateUrl: '~/crmSearchTasks/crmSearchTasksMenu.html', + controller: function($scope) { + var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), + ctrl = this; + + this.$onInit = function() { + // When a row is selected for bulk actions, load the actions menu + var unwatchIDs = $scope.$watch('$ctrl.ids.length', function (idsLength) { + if (idsLength) { + unwatchIDs(); + ctrl.taskManager.getMetadata(); + } + }); + }; + + this.isActionAllowed = function(action) { + return $scope.$eval('' + ctrl.ids.length + action.number); + }; + + this.getActionTitle = function(action) { + if (ctrl.isActionAllowed(action)) { + return ctrl.ids.length ? + ts('Perform action on %1 %2', {1: ctrl.ids.length, 2: ctrl.taskManager.entityInfo[ctrl.ids.length === 1 ? 'title' : 'title_plural']}) : + ts('Perform action on all %1', {1: ctrl.taskManager.entityInfo.title_plural}); + } + return ts('Selected number must be %1', {1: action.number.replace('===', '')}); + }; + + this.doAction = function(action) { + if (!ctrl.isActionAllowed(action)) { + return; + } + ctrl.taskManager.doTask(action, ctrl.ids); + }; + } + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/search_kit/ang/crmSearchTasks/crmSearchTasks.html b/ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.html similarity index 51% rename from ext/search_kit/ang/crmSearchTasks/crmSearchTasks.html rename to ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.html index e2b5e4dc42..8f460d1519 100644 --- a/ext/search_kit/ang/crmSearchTasks/crmSearchTasks.html +++ b/ext/search_kit/ang/crmSearchTasks/crmSearchTasksMenu.html @@ -1,7 +1,7 @@
-