Search ext: load saved searches
authorColeman Watts <coleman@civicrm.org>
Fri, 11 Sep 2020 15:58:52 +0000 (11:58 -0400)
committerColeman Watts <coleman@civicrm.org>
Mon, 14 Sep 2020 19:08:36 +0000 (15:08 -0400)
ext/search/CRM/Search/Page/Ang.php
ext/search/ang/search.module.js
ext/search/ang/search/SaveSmartGroup.ctrl.js
ext/search/ang/search/crmSearch.component.js
ext/search/ang/search/crmSearch/criteria.html

index 2549f622d5b0f6307c359e4126a85b87f7429aec..e38271a694cd5f70ff4f19bcac39c472605d911f 100644 (file)
@@ -49,7 +49,7 @@ class CRM_Search_Page_Ang extends CRM_Core_Page {
     $loader->setModules(['search']);
     $loader->setPageName('civicrm/search');
     $loader->useApp([
-      'defaultRoute' => '/Contact',
+      'defaultRoute' => '/create/Contact',
     ]);
     $loader->load();
     parent::run();
index 3b2eeb34ffe1545bd93824d8613bc54faaa0e7cc..ed60680e0020ac76b20a3c1de9ace9fe694fe492 100644 (file)
@@ -2,27 +2,67 @@
   "use strict";
 
   // Shared between router and searchMeta service
-  var searchEntity;
+  var searchEntity,
+    // For loading saved search
+    savedSearch,
+    undefined;
 
   // Declare module and route/controller/services
   angular.module('search', CRM.angRequires('search'))
 
     .config(function($routeProvider) {
-      $routeProvider.when('/:entity', {
+      $routeProvider.when('/:mode/:entity/:name?', {
         controller: 'searchRoute',
-        template: '<div id="bootstrap-theme" class="crm-search"><crm-search entity="entity"></crm-search></div>',
-        reloadOnSearch: false
+        template: '<div id="bootstrap-theme" class="crm-search"><crm-search ng-if="$ctrl.mode === \'create\'" entity="$ctrl.entity" load=":: $ctrl.savedSearch"></crm-search></div>',
+        reloadOnSearch: false,
+        resolve: {
+          // For paths like /load/Group/MySmartGroup, load the group, stash it in the savedSearch variable, and then redirect
+          // For paths like /create/Contact, return the stashed savedSearch if present
+          savedSearch: function($route, $location, $timeout, crmApi4) {
+            var retrievedSearch = savedSearch,
+              params = $route.current.params;
+            savedSearch = undefined;
+            switch (params.mode) {
+              case 'create':
+                return retrievedSearch;
+
+              case 'load':
+                // In theory savedSearches could be attached to something other than groups, but for now that's not supported
+                if (params.entity !== 'Group' || !params.name) {
+                  throw 'Failed to load ' + params.entity;
+                }
+                return crmApi4(params.entity, 'get', {
+                  select: ['id', 'title', 'saved_search_id', 'saved_search.api_entity', 'saved_search.api_params'],
+                  where: [['name', '=', params.name]]
+                }, 0).then(function(retrieved) {
+                  savedSearch = {
+                    type: params.entity,
+                    id: retrieved.id,
+                    title: retrieved.title,
+                    saved_search_id: retrieved.saved_search_id,
+                    api_params: retrieved['saved_search.api_params']
+                  };
+                  $timeout(function() {
+                    $location.url('/create/' + retrieved['saved_search.api_entity']);
+                  });
+                });
+            }
+          }
+        }
       });
     })
 
     // Controller binds entity to route
-    .controller('searchRoute', function($scope, $routeParams, $location) {
-      searchEntity = $scope.entity = $routeParams.entity;
+    .controller('searchRoute', function($scope, $routeParams, $location, savedSearch) {
+      searchEntity = this.entity = $routeParams.entity;
+      this.mode = $routeParams.mode;
+      this.savedSearch = savedSearch;
+      $scope.$ctrl = this;
 
       // Changing entity will refresh the angular page
-      $scope.$watch('entity', function(newEntity, oldEntity) {
+      $scope.$watch('$ctrl.entity', function(newEntity, oldEntity) {
         if (newEntity && oldEntity && newEntity !== oldEntity) {
-          $location.url('/' + newEntity);
+          $location.url('/create/' + newEntity);
         }
       });
     })
index 74401b984731c0a4b3d81a71216e28d0346c18ac..18b0298e96f79c3dd1a7b1721f29775485bfff34 100644 (file)
@@ -1,7 +1,7 @@
 (function(angular, $, _) {
   "use strict";
 
-  angular.module('search').controller('SaveSmartGroup', function ($scope, crmApi4, dialogService) {
+  angular.module('search').controller('SaveSmartGroup', function ($scope, $element, crmApi4, dialogService) {
     var ts = $scope.ts = CRM.ts(),
       model = $scope.model;
     $scope.groupEntityRefParams = {
       administerReservedGroups: CRM.checkPerm('administer reserved groups')
     };
     $scope.groupFields = _.indexBy(_.find(CRM.vars.search.schema, {name: 'Group'}).fields, 'name');
-    $scope.$watch('model.id', function (id) {
-      if (id) {
-        _.assign(model, $('#api-save-search-select-group').select2('data').extra);
+    $element.on('change', '#api-save-search-select-group', function() {
+      if ($(this).val()) {
+        $scope.$apply(function() {
+          var group = $('#api-save-search-select-group').select2('data').extra;
+          model.saved_search_id = group.saved_search_id;
+          model.description = group.description || '';
+          model.group_type = group.group_type || [];
+          model.visibility = group.visibility;
+        });
       }
     });
     $scope.cancel = function () {
index 039e74d99a769b51a54173cea1600d02c70d5aca..30ebfa99c352a2206c091abddf9dab2c041fc473 100644 (file)
@@ -3,7 +3,8 @@
 
   angular.module('search').component('crmSearch', {
     bindings: {
-      entity: '='
+      entity: '=',
+      load: '<'
     },
     templateUrl: '~/search/crmSearch.html',
     controller: function($scope, $element, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) {
             ctrl.stale = true;
           }
         }
+        if (ctrl.load) {
+          ctrl.load.saved = false;
+        }
       }
 
       function onChangeOrderBy() {
       function onChangeFilters() {
         ctrl.stale = true;
         ctrl.selectedRows.length = 0;
+        if (ctrl.load) {
+          ctrl.load.saved = false;
+        }
         if (ctrl.autoSearch) {
           ctrl.refreshAll();
         }
         }
         $scope.$watch('$ctrl.params.having', onChangeFilters, true);
 
+        if (this.load) {
+          this.params = this.load.api_params;
+          $timeout(function() {
+            ctrl.load.saved = true;
+          });
+        }
+
         loadFieldOptions();
       };
 
           description: '',
           visibility: 'User and User Admin Only',
           group_type: [],
-          id: null,
+          id: ctrl.load ? ctrl.load.id : null,
           entity: ctrl.entity,
-          params: angular.extend({}, ctrl.params, {version: 4, select: [selectField]})
+          params: angular.extend({}, ctrl.params, {version: 4})
         };
         delete model.params.orderBy;
         var options = CRM.utils.adjustDialogDefaults({
           autoOpen: false,
           title: ts('Save smart group')
         });
-        dialogService.open('saveSearchDialog', '~/search/saveSmartGroup.html', model, options);
+        dialogService.open('saveSearchDialog', '~/search/saveSmartGroup.html', model, options)
+          .then(function() {
+            if (ctrl.load) {
+              ctrl.load.saved = true;
+            }
+          });
       };
     }
   });
index 6bb500a7419fd798e69da25ab599c162deea0206..a142e498c59771d42532b71f2f847e06bc0a4aa1 100644 (file)
@@ -2,7 +2,7 @@
   <div>
     <div class="form-inline">
       <label for="crm-search-main-entity">{{:: ts('Search for') }}</label>
-      <input id="crm-search-main-entity" class="form-control" ng-model="$ctrl.entity" crm-ui-select="::{allowClear: false, data: entities}" ng-change="changeEntity()" />
+      <input id="crm-search-main-entity" class="form-control" ng-model="$ctrl.entity" crm-ui-select="::{allowClear: false, data: entities}" />
     </div>
     <div ng-if=":: $ctrl.paramExists('join')">
       <fieldset ng-repeat="join in $ctrl.params.join">
     </fieldset>
   </div>
   <div>
+    <div class="navbar-form clearfix" ng-if="$ctrl.load">
+      <div class="form-group pull-right">
+        <label>{{ $ctrl.load.title }}</label>
+        <button class="btn btn-default" ng-disabled="$ctrl.load.saved" ng-click="saveGroup()">{{ $ctrl.load.saved ? ts('Saved') : ts('Save') }}</button>
+      </div>
+    </div>
     <fieldset class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.where, op: 'AND', label: ts('Where'), fields: fieldsForWhere}">
     </fieldset>
     <fieldset ng-if="$ctrl.paramExists('having') && $ctrl.params.groupBy.length" class="api4-clause-fieldset" crm-search-clause="{format: 'string', clauses: $ctrl.params.having, op: 'AND', label: ts('Filter'), fields: fieldsForHaving}">