Search kit: Add token selector in display admin UI, allow tokens in tooltips
authorColeman Watts <coleman@civicrm.org>
Tue, 22 Dec 2020 02:45:45 +0000 (21:45 -0500)
committerColeman Watts <coleman@civicrm.org>
Wed, 23 Dec 2020 01:46:22 +0000 (20:46 -0500)
Also includes some cleanup of the crmSearchAdminLinkSelect component, making it more self-contained

14 files changed:
ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js
ext/search/ang/crmSearchAdmin/crmSearchAdminLinkSelect.component.js [moved from ext/search/ang/crmSearchAdmin/crmSearchAdminLinkSelect.directive.js with 60% similarity]
ext/search/ang/crmSearchAdmin/crmSearchAdminLinkSelect.html
ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js [new file with mode: 0644]
ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.html [new file with mode: 0644]
ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.component.js
ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.html
ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js
ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html
ext/search/ang/crmSearchDisplay.module.js
ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.component.js
ext/search/ang/crmSearchDisplayList/crmSearchDisplayListItems.html
ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js
ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.html

index 0def0e277027fddad4e809c69e51b0098f170cb3..8ab7a1cd3035f175415e22d73cae722a55d15c49 100644 (file)
         }
       };
 
-      // Return all possible links to main entity or join entities
-      this.getLinks = function() {
-        var links = _.cloneDeep(searchMeta.getEntity(ctrl.savedSearch.api_entity).paths || []);
-        _.each(ctrl.savedSearch.api_params.join, function(join) {
-          var joinName = join[0].split(' AS '),
-            joinEntity = searchMeta.getEntity(joinName[0]);
-          _.each(joinEntity.paths, function(path) {
-            var link = _.cloneDeep(path);
-            link.path = link.path.replace(/\[/g, '[' + joinName[1] + '.');
-            links.push(link);
-          });
-        });
-        return links;
-      };
-
       this.preview = this.stale = false;
 
       this.previewDisplay = function() {
similarity index 60%
rename from ext/search/ang/crmSearchAdmin/crmSearchAdminLinkSelect.directive.js
rename to ext/search/ang/crmSearchAdmin/crmSearchAdminLinkSelect.component.js
index 667605546479d5b5c5648ff4d20e72bf7983f451..1a63886985f6c450ec95505b3af0e3b9f7bf1645 100644 (file)
@@ -4,13 +4,29 @@
   angular.module('crmSearchAdmin').component('crmSearchAdminLinkSelect', {
     bindings: {
       column: '<',
-      links: '<'
+      apiEntity: '<',
+      apiParams: '<'
     },
     templateUrl: '~/crmSearchAdmin/crmSearchAdminLinkSelect.html',
-    controller: function ($scope, $element, $timeout) {
+    controller: function ($scope, $element, $timeout, searchMeta) {
       var ts = $scope.ts = CRM.ts(),
         ctrl = this;
 
+      // Return all possible links to main entity or join entities
+      function getLinks() {
+        var links = _.cloneDeep(searchMeta.getEntity(ctrl.apiEntity).paths || []);
+        _.each(ctrl.apiParams.join, function(join) {
+          var joinName = join[0].split(' AS '),
+            joinEntity = searchMeta.getEntity(joinName[0]);
+          _.each(joinEntity.paths, function(path) {
+            var link = _.cloneDeep(path);
+            link.path = link.path.replace(/\[/g, '[' + joinName[1] + '.');
+            links.push(link);
+          });
+        });
+        return links;
+      }
+
       function onChange() {
         var val = $('select', $element).val();
         if (val !== ctrl.column.link) {
@@ -31,6 +47,8 @@
       }
 
       this.$onInit = function() {
+        this.links = getLinks();
+
         $('select', $element).on('change', function() {
           $scope.$apply(onChange);
         });
index 47648e7aa4633dea5f73cf9636f970f3eeccfd28..74bc7291301762a5fc960b9e26f52c0483979329 100644 (file)
@@ -7,4 +7,7 @@
     {{ ts('Other...') }}
   </option>
 </select>
-<input class="form-control" type="text" ng-model="$ctrl.column.link" ng-model-options="{updateOn: 'blur'}" ng-show="$ctrl.column.link && !$ctrl.getLink($ctrl.column.link)" />
+<div class="form-group" ng-if="$ctrl.column.link && !$ctrl.getLink($ctrl.column.link)">
+  <input class="form-control" type="text" ng-model="$ctrl.column.link" ng-model-options="{updateOn: 'blur'}" />
+  <crm-search-admin-token-select api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="$ctrl.column" field="link"></crm-search-admin-token-select>
+</div>
diff --git a/ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js b/ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js
new file mode 100644 (file)
index 0000000..c2c4b2e
--- /dev/null
@@ -0,0 +1,51 @@
+(function(angular, $, _) {
+  "use strict";
+
+  angular.module('crmSearchAdmin').component('crmSearchAdminTokenSelect', {
+    bindings: {
+      apiEntity: '<',
+      apiParams: '<',
+      model: '<',
+      field: '@'
+    },
+    templateUrl: '~/crmSearchAdmin/crmSearchAdminTokenSelect.html',
+    controller: function ($scope, $element, searchMeta) {
+      var ts = $scope.ts = CRM.ts(),
+        ctrl = this;
+
+      this.initTokens = function() {
+        ctrl.tokens = ctrl.tokens || getTokens();
+      };
+
+      this.insertToken = function(key) {
+        ctrl.model[ctrl.field] = (ctrl.model[ctrl.field] || '') + ctrl.tokens[key].token;
+      };
+
+      function getTokens() {
+        var tokens = {
+          id: {
+            token: '[id]',
+            label: searchMeta.getField('id', ctrl.apiEntity).label
+          }
+        };
+        _.each(ctrl.apiParams.join, function(joinParams) {
+          var info = searchMeta.parseExpr(joinParams[0].split(' AS ')[1] + '.id');
+          tokens[info.alias] = {
+            token: '[' + info.alias + ']',
+            label: info.field ? info.field.label : info.alias
+          };
+        });
+        _.each(ctrl.apiParams.select, function(expr) {
+          var info = searchMeta.parseExpr(expr);
+          tokens[info.alias] = {
+            token: '[' + info.alias + ']',
+            label: info.field ? info.field.label : info.alias
+          };
+        });
+        return tokens;
+      }
+
+    }
+  });
+
+})(angular, CRM.$, CRM._);
diff --git a/ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.html b/ext/search/ang/crmSearchAdmin/crmSearchAdminTokenSelect.html
new file mode 100644 (file)
index 0000000..e30fc5d
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="btn-group btn-group-xs">
+  <button type="button" class="btn btn-default-outline dropdown-toggle" ng-click="$ctrl.initTokens()" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+    {{:: ts('Tokens') }} <span class="caret"></span>
+  </button>
+  <ul class="dropdown-menu">
+    <li ng-repeat="(id, token) in $ctrl.tokens" >
+      <a href ng-click="$ctrl.insertToken(id)">{{ token.label }}</a>
+    </li>
+  </ul>
+</div>
index 8517f149b7eb65ab16c1a79570d554e0698b93c3..d3080f6ed85164468501b00859248fb68bad0412 100644 (file)
@@ -55,7 +55,6 @@
           };
         }
         ctrl.hiddenColumns = ctrl.crmSearchAdminDisplay.initColumns();
-        ctrl.links = ctrl.crmSearchAdminDisplay.getLinks();
       };
 
     }
index de7e87eb75ed6dfa5e9f6e409a822e9b821002d1..e877777e81d3c977b5285b40ba36cb2700b2f317 100644 (file)
       </div>
       <div class="form-inline">
         <label>{{:: ts('Link:') }}</label>
-        <crm-search-admin-link-select column="col" links="$ctrl.links"></crm-search-admin-link-select>
+        <crm-search-admin-link-select column="col" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams"></crm-search-admin-link-select>
       </div>
       <div class="form-inline">
         <label>{{:: ts('Tooltip:') }}</label>
         <input class="form-control" type="text" ng-model="col.title" />
+        <crm-search-admin-token-select api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="col" field="title"></crm-search-admin-token-select>
       </div>
     </fieldset>
   </fieldset>
index 3ef0cd702a451206f9296795a7114636d5426cad..3dc937b70dc966c4f155704f7cbafbfdb0d65a1b 100644 (file)
@@ -39,7 +39,6 @@
           };
         }
         ctrl.hiddenColumns = ctrl.crmSearchAdminDisplay.initColumns();
-        ctrl.links = ctrl.crmSearchAdminDisplay.getLinks();
       };
 
     }
index b6d5092ac5c3f91c1a0628f169d8a1a6abee2db2..3589b56c03f4ea064572872021134b01f1b194f8 100644 (file)
       </div>
       <div class="form-inline">
         <label>{{:: ts('Link:') }}</label>
-        <crm-search-admin-link-select column="col" links="$ctrl.links"></crm-search-admin-link-select>
+        <crm-search-admin-link-select column="col" api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams"></crm-search-admin-link-select>
       </div>
       <div class="form-inline">
         <label>{{:: ts('Tooltip:') }}</label>
         <input class="form-control" type="text" ng-model="col.title" />
+        <crm-search-admin-token-select api-entity="$ctrl.apiEntity" api-params="$ctrl.apiParams" model="col" field="title"></crm-search-admin-token-select>
       </div>
     </fieldset>
   </fieldset>
index d9f5bf2152ac1b5390b11ad53a45981fbfee33e8..8d9ed43d58a104f6a8bbd3f73b9c5547281a2284 100644 (file)
@@ -5,21 +5,25 @@
   angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay'))
 
     .factory('searchDisplayUtils', function() {
-      function getUrl(link, row) {
-        var url = replaceTokens(link, row);
-        if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
-          url = CRM.url(url);
-        }
-        return _.escape(url);
-      }
 
       function replaceTokens(str, data) {
+        if (!str) {
+          return '';
+        }
         _.each(data, function(value, key) {
           str = str.replace('[' + key + ']', value);
         });
         return str;
       }
 
+      function getUrl(link, row) {
+        var url = replaceTokens(link, row);
+        if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
+          url = CRM.url(url);
+        }
+        return _.escape(url);
+      }
+
       function formatSearchValue(row, col, value) {
         var type = col.dataType,
           result = value;
         formatSearchValue: formatSearchValue,
         canAggregate: canAggregate,
         prepareColumns: prepareColumns,
-        prepareParams: prepareParams
+        prepareParams: prepareParams,
+        replaceTokens: replaceTokens
       };
     });
 
index caf250af116e79f196d2255c677b082a81c46e90..50157877bc728bfeb5df843ac043f4874bab1256 100644 (file)
@@ -18,6 +18,7 @@
         this.apiParams = _.cloneDeep(this.apiParams);
         this.apiParams.limit = parseInt(this.settings.limit || 0, 10);
         this.columns = searchDisplayUtils.prepareColumns(this.settings.columns, this.apiParams);
+        $scope.displayUtils = searchDisplayUtils;
       };
 
       this.getResults = function() {
index 15b8b1674d1d2b789528d713e87b09f7d5e5091c..8a6ae4936716d7a7b83b745b473e757386228deb 100644 (file)
@@ -1,4 +1,4 @@
 <li ng-repeat="row in $ctrl.results">
-  <div ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: col.title }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
+  <div ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
   </div>
 </li>
index 8ea65c7c9e614a6f64c25523b2016c24c70f163c..127d5d9c13932274750201e12dd04fb782ddce86 100644 (file)
@@ -21,6 +21,7 @@
         this.apiParams = _.cloneDeep(this.apiParams);
         this.apiParams.limit = parseInt(this.settings.limit || 0, 10);
         this.columns = searchDisplayUtils.prepareColumns(this.settings.columns, this.apiParams);
+        $scope.displayUtils = searchDisplayUtils;
       };
 
       this.getResults = function() {
index 59d55f2d5ff9dae9998c7d57510f720d7b10d41a..d23d5efe9c064cd2136b9dd890c07487513b3ac1 100644 (file)
@@ -18,7 +18,7 @@
       <td ng-if="$ctrl.settings.actions">
         <input type="checkbox" ng-checked="isRowSelected(row)" ng-click="selectRow(row)" ng-disabled="!(!loadingAllRows && row.id)">
       </td>
-      <td ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: col.title }}" class="{{:: col.alignment }}">
+      <td ng-repeat="col in $ctrl.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
       </td>
       <td></td>
     </tr>