From ed6d5cb9e6a6d83d41e06ea203e18e986ac43775 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 3 Feb 2022 23:28:35 -0500 Subject: [PATCH] SearchKit - Enable import to update as well as create Before: Only create was possible in the "Import" popup; minimal user feedback. After: Create and update both work, with better user-feedback about what will happen. --- .../crmSearchAdminExport.component.js | 10 +- .../crmSearchAdminImport.component.js | 129 +++++++++++++++--- .../crmSearchAdmin/crmSearchAdminImport.html | 29 ++-- 3 files changed, 132 insertions(+), 36 deletions(-) diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminExport.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminExport.component.js index 8d60739432..542f9b3e19 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminExport.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminExport.component.js @@ -49,9 +49,13 @@ var data = []; _.each(ctrl.types, function(type) { if (type.enabled) { - _.each(type.values, function(values) { - data.push([type.entity, 'create', {values: values}]); - }); + var params = {records: type.values}; + // Afform always matches on 'name', no need to add it to the API 'save' params + if (type.entity !== 'Afform') { + // Group and SavedSearch match by 'name', SearchDisplay also matches by 'saved_search_id'. + params.match = type.entity === 'SearchDisplay' ? ['name', 'saved_search_id'] : ['name']; + } + data.push([type.entity, 'save', params]); } }); ctrl.output = JSON.stringify(data, null, 2); diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.component.js index cfc3b7d349..987a52f0c9 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.component.js @@ -9,31 +9,116 @@ this.values = ''; + var checkInput = _.debounce(function() { + $scope.$apply(function() { + if (!ctrl.values) { + ctrl.checking = false; + return; + } + try { + var apiCalls = JSON.parse(ctrl.values), + allowedEntities = ['SavedSearch', 'SearchDisplay', 'Group']; + if (CRM.crmSearchAdmin.afformEnabled) { + allowedEntities.push('Afform'); + } + // Get entity titles for use in status message + var getCalls = { + Entity: ['Entity', 'get', { + select: ['title', 'title_plural'], + where: [['name', 'IN', allowedEntities]] + }, 'name'] + }; + // Get count of existing matches for each import entity + _.each(apiCalls, function (apiCall) { + var entity = apiCall[0]; + if (apiCall[1] !== 'save' || ('chain' in apiCall[2] && !_.isEmpty(apiCall[2].chain))) { + throw ts('Unsupported API action: only "save" is allowed.'); + } + if (!_.includes(allowedEntities, entity)) { + throw ts('Unsupported API entity "' + entity + '".'); + } + if (entity in getCalls) { + throw ts('Duplicate API entity "' + entity + '".'); + } + var names = _.map(apiCall[2].records, 'name'), + where = [['name', 'IN', names]]; + if (entity === 'SearchDisplay') { + where.push(['saved_search_id.name', '=', apiCall[2].records[0]['saved_search_id.name']]); + } + if (names.length) { + getCalls[entity] = [entity, 'get', {select: ['row_count'], where: where}]; + } + }); + if (_.keys(getCalls).length < 2) { + throw ts('No records to import.'); + } + crmApi4(getCalls) + .then(function (results) { + ctrl.checking = false; + ctrl.error = ''; + ctrl.preview = ''; + _.each(allowedEntities, function (entity) { + if (results[entity]) { + var info = results.Entity[entity], + count = getCalls[entity][2].where[0][2].length, + existing = results[entity].count, + saveCall = _.findWhere(apiCalls, {0: entity}); + // Unless it's an afform, the api save params must include `match` or an update is not possible + if (existing && entity !== 'Afform' && (!saveCall[2].match || !saveCall[2].match.length)) { + ctrl.error += ' ' + ts('Cannot create %1 %2 because an existing one with the same name already exists.', { + 1: existing, + 2: existing === 1 ? info.title : info.title_plural + }); + ctrl.error += ' ' + ts('To update existing records, include "match" in the API params.'); + } else if (existing) { + ctrl.preview += ' ' + ts('%1 existing %2 will be updated.', { + 1: existing, + 2: existing === 1 ? info.title : info.title_plural + }); + } + if (existing < count) { + ctrl.preview += ' ' + ts('%1 new %2 will be created.', { + 1: count - existing, + 2: (count - existing) === 1 ? info.title : info.title_plural + }); + } + } + }); + }, function (error) { + ctrl.running = false; + ctrl.error = error.error_message; + ctrl.checking = false; + }); + } catch (e) { + ctrl.error = e; + ctrl.checking = false; + } + }); + }, 500); + + this.onChangeInput = function() { + ctrl.checking = true; + ctrl.error = ''; + ctrl.preview = null; + checkInput(); + }; + this.run = function() { ctrl.running = true; - try { - var apiCalls = JSON.parse(ctrl.values); - _.each(apiCalls, function(apiCall) { - if (apiCall[1] !== 'create' || ('chain' in apiCall[2] && !_.isEmpty(apiCall[2].chain))) { - throw ts('Unsupported API action: only "create" is allowed.'); - } + ctrl.preview = null; + var apiCalls = JSON.parse(ctrl.values); + crmApi4(apiCalls) + .then(function(result) { + CRM.alert( + result.length === 1 ? ts('1 record successfully imported.') : ts('%1 records successfully imported.', {1: results.length}), + ts('Saved'), + 'success' + ); + dialogService.close('crmSearchAdminImport'); + }, function(error) { + ctrl.running = false; + ctrl.error = ts('Processing Error:') + ' ' + error.error_message; }); - crmApi4(apiCalls) - .then(function(result) { - CRM.alert( - result.length === 1 ? ts('1 record successfully imported.') : ts('%1 records successfully imported.', {1: results.length}), - ts('Saved'), - 'success' - ); - dialogService.close('crmSearchAdminImport'); - }, function(error) { - ctrl.running = false; - alert(ts('Processing Error') + "\n" + error.error_message); - }); - } catch(e) { - ctrl.running = false; - alert(ts('Input Error') + "\n" + e); - } }; } }); diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.html b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.html index 153b5dc2cb..0f4480bafd 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.html +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminImport.html @@ -1,14 +1,21 @@
-
-

- - {{:: ts('Search configuration copied from the "Export" action can be pasted here.') }} -

-

- {{:: ts('Note: a Saved Search with the same name must not already exist.') }} -

-
- - +
+ + {{:: ts('Search configuration copied from the "Export" action can be pasted here.') }} +
+
+ + {{ $ctrl.checking ? ts('Checking input...') : ts('Importing Saved Search...') }} +
+
+ + {{ $ctrl.error }} +
+
+ + {{ $ctrl.preview }} +
+ +
-- 2.25.1