diff --git a/ext/search/ang/crmSearchActions.ang.php b/ext/search/ang/crmSearchActions.ang.php index 6f62570aa282..31e6290bf312 100644 --- a/ext/search/ang/crmSearchActions.ang.php +++ b/ext/search/ang/crmSearchActions.ang.php @@ -9,6 +9,9 @@ 'partials' => [ 'ang/crmSearchActions', ], + 'css' => [ + 'css/crmSearchActions.css', + ], 'basePages' => [], 'requires' => ['crmUi', 'crmUtil', 'dialogService', 'api4', 'checklist-model'], 'settingsFactory' => ['\Civi\Search\Actions', 'getActionSettings'], diff --git a/ext/search/ang/crmSearchActions/crmSearchActionDelete.ctrl.js b/ext/search/ang/crmSearchActions/crmSearchActionDelete.ctrl.js index d7c3f4828213..6408a5918fc6 100644 --- a/ext/search/ang/crmSearchActions/crmSearchActionDelete.ctrl.js +++ b/ext/search/ang/crmSearchActions/crmSearchActionDelete.ctrl.js @@ -1,7 +1,7 @@ (function(angular, $, _) { "use strict"; - angular.module('crmSearchActions').controller('crmSearchActionDelete', function($scope, crmApi4, dialogService) { + angular.module('crmSearchActions').controller('crmSearchActionDelete', function($scope, dialogService) { var ts = $scope.ts = CRM.ts(), model = $scope.model, ctrl = $scope.$ctrl = this; @@ -13,11 +13,18 @@ }; this.delete = function() { - crmApi4(model.entity, 'Delete', { - where: [['id', 'IN', model.ids]], - }).then(function() { - dialogService.close('crmSearchAction'); - }); + $('.ui-dialog-titlebar button').hide(); + ctrl.run = {}; + }; + + this.onSuccess = function() { + CRM.alert(ts('Successfully deleted %1 %2.', {1: model.ids.length, 2: ctrl.entityTitle}), ts('Deleted'), 'success'); + dialogService.close('crmSearchAction'); + }; + + this.onError = function() { + CRM.alert(ts('An error occurred while attempting to delete %1 %2.', {1: model.ids.length, 2: ctrl.entityTitle}), ts('Error'), 'error'); + dialogService.close('crmSearchAction'); }; }); diff --git a/ext/search/ang/crmSearchActions/crmSearchActionDelete.html b/ext/search/ang/crmSearchActions/crmSearchActionDelete.html index 40d657cfef43..340fdae9e13e 100644 --- a/ext/search/ang/crmSearchActions/crmSearchActionDelete.html +++ b/ext/search/ang/crmSearchActions/crmSearchActionDelete.html @@ -2,9 +2,19 @@

{{:: ts('Are you sure you want to delete %1 %2?', {1: model.ids.length, 2: $ctrl.entityTitle}) }}


-
- - +
+
{{:: ts('Deleting %1 %2...', {1: model.ids.length, 2: $ctrl.entityTitle}) }}
+ +
+
+
+ +
-
diff --git a/ext/search/ang/crmSearchActions/crmSearchActionUpdate.ctrl.js b/ext/search/ang/crmSearchActions/crmSearchActionUpdate.ctrl.js index 01bd9e67ee98..0a3af265eaac 100644 --- a/ext/search/ang/crmSearchActions/crmSearchActionUpdate.ctrl.js +++ b/ext/search/ang/crmSearchActions/crmSearchActionUpdate.ctrl.js @@ -29,7 +29,16 @@ // Debounce the onchange event using timeout $timeout(function() { if (ctrl.add) { - ctrl.values.push([ctrl.add, '']); + var field = ctrl.getField(ctrl.add), + value = ''; + if (field.serialize) { + value = []; + } else if (field.data_type === 'Boolean') { + value = true; + } else if (field.options && field.options.length) { + value = field.options[0].id; + } + ctrl.values.push([ctrl.add, value]); } ctrl.add = null; }); @@ -61,12 +70,20 @@ }; this.save = function() { - crmApi4(model.entity, 'Update', { - where: [['id', 'IN', model.ids]], + $('.ui-dialog-titlebar button').hide(); + ctrl.run = { values: _.zipObject(ctrl.values) - }).then(function() { - dialogService.close('crmSearchAction'); - }); + }; + }; + + this.onSuccess = function() { + CRM.alert(ts('Successfully updated %1 %2.', {1: model.ids.length, 2: ctrl.entityTitle}), ts('Saved'), 'success'); + dialogService.close('crmSearchAction'); + }; + + this.onError = function() { + CRM.alert(ts('An error occurred while attempting to update %1 %2.', {1: model.ids.length, 2: ctrl.entityTitle}), ts('Error'), 'error'); + dialogService.close('crmSearchAction'); }; }); diff --git a/ext/search/ang/crmSearchActions/crmSearchActionUpdate.html b/ext/search/ang/crmSearchActions/crmSearchActionUpdate.html index d3bae2a6bffe..06d5ef51d912 100644 --- a/ext/search/ang/crmSearchActions/crmSearchActionUpdate.html +++ b/ext/search/ang/crmSearchActions/crmSearchActionUpdate.html @@ -2,16 +2,26 @@

{{:: ts('Update the %1 selected %2 with the following values:', {1: model.ids.length, 2: $ctrl.entityTitle}) }}

- +
-
+
+
+
{{:: ts('Updating %1 %2...', {1: model.ids.length, 2: $ctrl.entityTitle}) }}
+ +

-
- - +
+ +
diff --git a/ext/search/ang/crmSearchActions/crmSearchBatchRunner.component.js b/ext/search/ang/crmSearchActions/crmSearchBatchRunner.component.js new file mode 100644 index 000000000000..0eaca53384d3 --- /dev/null +++ b/ext/search/ang/crmSearchActions/crmSearchBatchRunner.component.js @@ -0,0 +1,75 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('crmSearchActions').component('crmSearchBatchRunner', { + bindings: { + entity: '<', + action: '@', + ids: '<', + params: '<', + success: '&', + error: '&' + }, + templateUrl: '~/crmSearchActions/crmSearchBatchRunner.html', + controller: function($scope, $timeout, $interval, crmApi4) { + var ts = $scope.ts = CRM.ts(), + ctrl = this, + currentBatch = 0, + totalBatches, + incrementer; + + this.progress = 0; + + // Number of records to process in each batch + var BATCH_SIZE = 500, + // Extimated number of seconds each batch will take (for auto-incrementing the progress bar) + EST_BATCH_TIME = 5; + + this.$onInit = function() { + totalBatches = Math.ceil(ctrl.ids.length / BATCH_SIZE); + runBatch(); + }; + + this.$onDestroy = function() { + stopIncrementer(); + }; + + function runBatch() { + ctrl.first = currentBatch * BATCH_SIZE; + ctrl.last = (currentBatch + 1) * BATCH_SIZE; + if (ctrl.last > ctrl.ids.length) { + ctrl.last = ctrl.ids.length; + } + var params = _.cloneDeep(ctrl.params); + params.where = params.where || []; + params.where.push(['id', 'IN', ctrl.ids.slice(ctrl.first, ctrl.last)]); + crmApi4(ctrl.entity, ctrl.action, params).then( + function(result) { + stopIncrementer(); + ctrl.progress = Math.floor(100 * ++currentBatch / totalBatches); + if (ctrl.last >= ctrl.ids.length) { + $timeout(ctrl.success, 500); + } else { + runBatch(); + } + }, function(error) { + ctrl.error(); + }); + // Move the bar every second to simulate progress between batches + incrementer = $interval(function(i) { + var est = Math.floor(100 * (currentBatch + (i / EST_BATCH_TIME)) / totalBatches); + ctrl.progress = est > 100 ? 100 : est; + }, 1000, EST_BATCH_TIME); + } + + function stopIncrementer() { + if (angular.isDefined(incrementer)) { + $interval.cancel(incrementer); + incrementer = undefined; + } + } + + } + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchActions/crmSearchBatchRunner.html b/ext/search/ang/crmSearchActions/crmSearchBatchRunner.html new file mode 100644 index 000000000000..23d864d9f505 --- /dev/null +++ b/ext/search/ang/crmSearchActions/crmSearchBatchRunner.html @@ -0,0 +1,8 @@ +
+
+ {{ ts('%1% Complete', {1: $ctrl.progress}) }} +
+
+

+ {{ ts('Processing records %1 - %2', {1: 1 + $ctrl.first, 2: $ctrl.last}) }} +

diff --git a/ext/search/ang/crmSearchAdmin.ang.php b/ext/search/ang/crmSearchAdmin.ang.php index 9d5ee8e52132..29413f3fef71 100644 --- a/ext/search/ang/crmSearchAdmin.ang.php +++ b/ext/search/ang/crmSearchAdmin.ang.php @@ -7,7 +7,7 @@ 'ang/crmSearchAdmin/*/*.js', ], 'css' => [ - 'css/*.css', + 'css/crmSearchAdmin.css', ], 'partials' => [ 'ang/crmSearchAdmin', diff --git a/ext/search/css/crmSearchActions.css b/ext/search/css/crmSearchActions.css new file mode 100644 index 000000000000..877e7ddaaaa4 --- /dev/null +++ b/ext/search/css/crmSearchActions.css @@ -0,0 +1,5 @@ +.crm-search-action-progress { + padding: 10px; + margin-top: 10px; + border: 1px solid lightgrey; +} diff --git a/ext/search/css/search.css b/ext/search/css/crmSearchAdmin.css similarity index 100% rename from ext/search/css/search.css rename to ext/search/css/crmSearchAdmin.css