SearchKit - normalize search display column keys
authorColeman Watts <coleman@civicrm.org>
Tue, 16 Feb 2021 13:56:01 +0000 (08:56 -0500)
committerColeman Watts <coleman@civicrm.org>
Tue, 16 Feb 2021 14:30:35 +0000 (09:30 -0500)
12 files changed:
ext/search/CRM/Search/Upgrader.php
ext/search/ang/crmSearchAdmin.module.js
ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js
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 9d314e49104420394738d90e38a50617aafd6027..697e5bafbeab2fc95ee981ecb3f69b6701e0acc4 100644 (file)
@@ -31,6 +31,10 @@ class CRM_Search_Upgrader extends CRM_Search_Upgrader_Base {
       ->execute();
   }
 
+  /**
+   * Upgrade 1000 - install schema
+   * @return bool
+   */
   public function upgrade_1000() {
     $this->ctx->log->info('Applying update 1000 - install schema.');
     // For early, early adopters who installed the extension pre-beta
@@ -41,4 +45,42 @@ class CRM_Search_Upgrader extends CRM_Search_Upgrader_Base {
     return TRUE;
   }
 
+  /**
+   * Upgrade 1001 - normalize search display column keys
+   * @return bool
+   */
+  public function upgrade_1001() {
+    $this->ctx->log->info('Applying update 1001 - normalize search display columns.');
+    $savedSearches = \Civi\Api4\SavedSearch::get(FALSE)
+      ->addWhere('api_params', 'IS NOT NULL')
+      ->addChain('displays', \Civi\Api4\SearchDisplay::get()->addWhere('saved_search_id', '=', '$id'))
+      ->execute();
+    foreach ($savedSearches as $savedSearch) {
+      $newAliases = [];
+      foreach ($savedSearch['api_params']['select'] ?? [] as $i => $select) {
+        if (strstr($select, '(') && !strstr($select, ' AS ')) {
+          $alias = CRM_Utils_String::munge(str_replace(')', '', $select), '_', 256);
+          $newAliases[$select] = $alias;
+          $savedSearch['api_params']['select'][$i] = $select . ' AS ' . $alias;
+        }
+      }
+      if ($newAliases) {
+        \Civi\Api4\SavedSearch::update(FALSE)
+          ->setValues(array_diff_key($savedSearch, ['displays' => 0]))
+          ->execute();
+      }
+      foreach ($savedSearch['displays'] ?? [] as $display) {
+        foreach ($display['settings']['columns'] ?? [] as $c => $column) {
+          $key = $newAliases[$column['expr']] ?? $column['expr'];
+          unset($display['settings']['columns'][$c]['expr']);
+          $display['settings']['columns'][$c]['key'] = explode(' AS ', $key)[1] ?? $key;
+        }
+        \Civi\Api4\SearchDisplay::update(FALSE)
+          ->setValues($display)
+          ->execute();
+      }
+    }
+    return TRUE;
+  }
+
 }
index 9d4b86bca606a472422e1431f5af4b25775e5f06..6839000b48de52de0611799efc3af0bf66b78fdb 100644 (file)
           return;
         }
         var splitAs = expr.split(' AS '),
-          info = {fn: null, modifier: '', field: {}},
+          info = {fn: null, modifier: '', field: {}, alias: _.last(splitAs)},
           fieldName = splitAs[0],
           bracketPos = splitAs[0].indexOf('(');
         if (bracketPos >= 0) {
           info.suffix = !split[1] ? '' : ':' + split[1];
           info.field = fieldAndJoin.field;
           info.join = fieldAndJoin.join;
-          info.alias = splitAs[1] || (info.fn ? info.fn.name + ':' + info.path : split[0]);
         }
         return info;
       }
index 46dfde126b913af515a29b192ae36470d5cd77f9..3627c41a51d6154510816dac08baa39f0d8fc14e 100644 (file)
         ctrl.hiddenColumns.splice(index, 1);
       };
 
+      this.getExprFromSelect = function(key) {
+        var match;
+        _.each(ctrl.savedSearch.api_params.select, function(expr) {
+          var parts = expr.split(' AS ');
+          if (_.includes(parts, key)) {
+            match = parts[0];
+            return false;
+          }
+        });
+        return match;
+      };
+
+      this.getFieldLabel = function(key) {
+        var expr = ctrl.getExprFromSelect(key);
+        return searchMeta.getDefaultLabel(expr);
+      };
+
       function fieldToColumn(fieldExpr) {
         var info = searchMeta.parseExpr(fieldExpr);
         return {
-          expr: fieldExpr,
+          key: info.alias,
           label: searchMeta.getDefaultLabel(fieldExpr),
           dataType: (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field.data_type
         };
           });
           ctrl.hiddenColumns = [];
         } else {
-          var activeColumns = _.collect(ctrl.display.settings.columns, 'expr');
+          var activeColumns = _.collect(ctrl.display.settings.columns, 'key'),
+            selectAliases = _.map(ctrl.savedSearch.api_params.select, function(select) {
+              return _.last(select.split(' AS '));
+            });
           ctrl.hiddenColumns = _.transform(ctrl.savedSearch.api_params.select, function(hiddenColumns, fieldExpr) {
-            if (!_.includes(activeColumns, fieldExpr)) {
+            var key = _.last(fieldExpr.split(' AS '));
+            if (!_.includes(activeColumns, key)) {
               hiddenColumns.push(fieldToColumn(fieldExpr));
             }
           });
-          _.each(activeColumns, function(fieldExpr, index) {
-            if (!_.includes(ctrl.savedSearch.api_params.select, fieldExpr)) {
+          _.eachRight(activeColumns, function(key, index) {
+            if (!_.includes(selectAliases, key)) {
               ctrl.display.settings.columns.splice(index, 1);
             }
           });
index b2d315f8ee918ce42039b938e272632463cae186..777191ef1a352a2fa8e44726938da10f4fde2863 100644 (file)
       parent: '^crmSearchAdminDisplay'
     },
     templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayList.html',
-    controller: function($scope, searchMeta) {
+    controller: function($scope) {
       var ts = $scope.ts = CRM.ts(),
         ctrl = this;
-      this.getFieldLabel = searchMeta.getDefaultLabel;
 
       this.symbols = {
         ul: [
index 6c8f425daffe558c47aebff6bcb953cbecdc12f6..857d29131635b478a16ff963a0816d132df8c90b 100644 (file)
@@ -25,7 +25,7 @@
   <fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.display.settings.columns" ui-sortable="$ctrl.parent.sortableOptions">
     <legend>{{:: ts('Fields') }}</legend>
     <fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
-      <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+      <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
       <div class="form-inline">
         <label>{{:: ts('Label:') }}</label> <input class="form-control" type="text" ng-model="col.label" >
         <div class="form-control checkbox-inline" ng-show="col.label.length" title="{{:: ts('Show label for every record even when this field is blank') }}">
@@ -58,7 +58,7 @@
   <fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
     <legend>{{:: ts('Hidden Fields') }}</legend>
     <fieldset ng-repeat="col in $ctrl.parent.hiddenColumns" class="crm-draggable">
-      <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+      <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
       <div class="form-inline">
         <label>{{:: ts('Label:') }}</label> <input disabled class="form-control" type="text" ng-model="col.label" />
         <button class="btn-xs pull-right" ng-click="$ctrl.parent.restoreCol($index)" title="{{:: ts('Show') }}">
index c6b999d57423a881ebbe852315c235462d440733..ccb4fd5c8c22a62e7c999975e742c2973e22dac1 100644 (file)
       parent: '^crmSearchAdminDisplay'
     },
     templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayTable.html',
-    controller: function($scope, searchMeta) {
+    controller: function($scope) {
       var ts = $scope.ts = CRM.ts(),
         ctrl = this;
-      this.getFieldLabel = searchMeta.getDefaultLabel;
 
       this.$onInit = function () {
         if (!ctrl.display.settings) {
index 0a53de615d7a432a2637b3b6dd3cb8ea17a0fc1c..23714883f7f77f1f2d68bc7f98481e6f8a24575c 100644 (file)
@@ -15,7 +15,7 @@
   <fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.display.settings.columns" ui-sortable="$ctrl.parent.sortableOptions">
     <legend>{{:: ts('Columns') }}</legend>
     <fieldset ng-repeat="col in $ctrl.display.settings.columns" class="crm-draggable">
-      <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+      <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
       <div class="form-inline">
         <label>{{:: ts('Label:') }}</label> <input class="form-control" type="text" ng-model="col.label" />
         <button class="btn-xs pull-right" ng-click="$ctrl.parent.removeCol($index)" title="{{:: ts('Hide') }}">
@@ -44,7 +44,7 @@
   <fieldset class="crm-search-admin-edit-columns" ng-model="$ctrl.parent.hiddenColumns" ui-sortable="$ctrl.parent.sortableOptions">
     <legend>{{:: ts('Hidden Columns') }}</legend>
     <fieldset ng-repeat="col in $ctrl.parent.hiddenColumns" class="crm-draggable">
-      <legend>{{ $ctrl.getFieldLabel(col.expr) }}</legend>
+      <legend>{{ $ctrl.parent.getFieldLabel(col.key) }}</legend>
       <div class="form-inline">
         <label>{{:: ts('Label:') }}</label> <input disabled class="form-control" type="text" ng-model="col.label" />
         <button class="btn-xs pull-right" ng-click="$ctrl.parent.restoreCol($index)" title="{{:: ts('Show') }}">
index 56429b554a10762bf973fd5844719fb1e07b4fe3..f77de83380b081b5a2d2981505b086335b3137a2 100644 (file)
         return result;
       }
 
-      function prepareColumns(columns) {
-        columns = _.cloneDeep(columns);
-        _.each(columns, function(col) {
-          col.key = _.last(col.expr.split(' AS '));
-        });
-        return columns;
-      }
-
       function getApiParams(ctrl, mode) {
         return {
           return: mode || 'page:' + ctrl.page,
@@ -85,7 +77,6 @@
 
       return {
         formatSearchValue: formatSearchValue,
-        prepareColumns: prepareColumns,
         getApiParams: getApiParams,
         getResults: getResults,
         replaceTokens: replaceTokens
index 38ed9435802d1b35863049fe55923cc18d69090d..129d267c7f1a424d89235fbd458c884655c489d0 100644 (file)
@@ -22,7 +22,6 @@
       this.rowCount = null;
 
       this.$onInit = function() {
-        this.columns = searchDisplayUtils.prepareColumns(this.settings.columns);
         this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
         $scope.displayUtils = searchDisplayUtils;
 
index 8a6ae4936716d7a7b83b745b473e757386228deb..e6a29dd9cbd870770a685473f7739faf609905c0 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="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
+  <div ng-repeat="col in $ctrl.settings.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.break ? '' : 'crm-inline-block' }}">
   </div>
 </li>
index 3f98f63c217c7b6e2253923945c8068ad76ff2c5..6a3eca26f835244f3624201ceb0b72bf7c55c123 100644 (file)
@@ -23,7 +23,6 @@
       this.allRowsSelected = false;
 
       this.$onInit = function() {
-        this.columns = searchDisplayUtils.prepareColumns(this.settings.columns);
         this.sort = this.settings.sort ? _.cloneDeep(this.settings.sort) : [];
         $scope.displayUtils = searchDisplayUtils;
 
index 6f10531991f9c2aa4521430397696d08b4e693b5..9ad18f1fc6216abfd5488ce84fabf6710900c186 100644 (file)
@@ -7,7 +7,7 @@
       <th class="crm-search-result-select" ng-if="$ctrl.settings.actions">
         <input type="checkbox" ng-checked="$ctrl.allRowsSelected" ng-click="selectAllRows()" >
       </th>
-      <th ng-repeat="col in $ctrl.columns" ng-click="setSort(col, $event)" title="{{:: ts('Click to sort results (shift-click to sort by multiple).') }}">
+      <th ng-repeat="col in $ctrl.settings.columns" ng-click="setSort(col, $event)" title="{{:: ts('Click to sort results (shift-click to sort by multiple).') }}">
         <i class="crm-i {{ getSort(col) }}"></i>
         <span>{{ col.label }}</span>
       </th>
@@ -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="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
+      <td ng-repeat="col in $ctrl.settings.columns" ng-bind-html="formatResult(row, col)" title="{{:: displayUtils.replaceTokens(col.title, row) }}" class="{{:: col.alignment }}">
       </td>
       <td></td>
     </tr>