SearchKit - Allow funcitons in WHERE clause
authorColeman Watts <coleman@civicrm.org>
Mon, 13 Dec 2021 16:18:25 +0000 (11:18 -0500)
committerColeman Watts <coleman@civicrm.org>
Mon, 13 Dec 2021 16:18:25 +0000 (11:18 -0500)
ext/search_kit/ang/crmSearchAdmin/compose.html
ext/search_kit/ang/crmSearchAdmin/crmSearchClause.component.js
ext/search_kit/ang/crmSearchAdmin/crmSearchClause.html
ext/search_kit/ang/crmSearchAdmin/crmSearchFunction.component.js
ext/search_kit/ang/crmSearchAdmin/crmSearchFunction.html

index 6437a92b5782c63aa050f2db07e8ec6d20b69a01..04f42d6956b091413c820cb1509187a97e8f668a 100644 (file)
@@ -39,7 +39,7 @@
   </div>
   <div class="crm-search-criteria-column">
     <fieldset class="api4-clause-fieldset">
-      <crm-search-clause clauses="$ctrl.savedSearch.api_params.where" format="string" op="AND" label="{{:: ts('Where') }}" fields="fieldsForWhere" ></crm-search-clause>
+      <crm-search-clause clauses="$ctrl.savedSearch.api_params.where" format="string" op="AND" label="{{:: ts('Where') }}" fields="fieldsForWhere" allow-functions="true" ></crm-search-clause>
     </fieldset>
     <fieldset ng-if="$ctrl.paramExists('having') && $ctrl.savedSearch.api_params.groupBy.length" class="api4-clause-fieldset">
       <crm-search-clause clauses="$ctrl.savedSearch.api_params.having" format="string" op="AND" label="{{:: ts('Having') }}" fields="fieldsForHaving" ></crm-search-clause>
@@ -54,7 +54,7 @@
   <div ng-if="!!controls.showFunctions">
     <!-- Must use track by $index with an array of primitives, and manually refresh this loop when indexes change -->
     <fieldset ng-repeat="col in $ctrl.savedSearch.api_params.select track by $index" ng-if="!$ctrl.isPseudoField(col)">
-      <crm-search-function expr="$ctrl.savedSearch.api_params.select[$index]"></crm-search-function>
+      <crm-search-function class="form-inline" mode="select" expr="$ctrl.savedSearch.api_params.select[$index]"></crm-search-function>
     </fieldset>
   </div>
 </fieldset>
index 558e4817b6556116512f6481a583fb44f2b39737..3140b0becc5ba5966b50a9be874c4424e9c80efa 100644 (file)
@@ -7,6 +7,7 @@
       clauses: '<',
       format: '@',
       op: '@',
+      allowFunctions: '<',
       skip: '<',
       label: '@',
       hideLabel: '@',
         return arg.suffix ? arg.suffix.slice(1) : 'id';
       };
 
+      this.hasFunction = function(expr) {
+        return expr.indexOf('(') > -1;
+      };
+
       this.addGroup = function(op) {
         ctrl.clauses.push([op, []]);
       };
index 4fc255e6ebb2ca0a95ccceee376629c4b997a2ea..fc7384068b32af309a496d51f4df098758b5b00d 100644 (file)
         </span>
       </div>
       <div ng-if="!$ctrl.conjunctions[clause[0]]" class="api4-input-group">
-        <input class="form-control collapsible-optgroups" ng-model="clause[0]" crm-ui-select="{data: $ctrl.fields, allowClear: true, placeholder: 'Field'}" ng-change="$ctrl.changeClauseField(clause, index)" />
+        <crm-search-function ng-if="$ctrl.allowFunctions" class="form-group" expr="clause[0]" mode="clause"></crm-search-function>
+        <span ng-if="!$ctrl.hasFunction(clause[0])">
+          <input class="form-control collapsible-optgroups" ng-model="clause[0]" crm-ui-select="{data: $ctrl.fields, allowClear: true, placeholder: 'Field'}" ng-change="$ctrl.changeClauseField(clause, index)" />
+        </span>
         <select class="form-control api4-operator" ng-model="clause[1]" ng-options="o.key as o.value for o in $ctrl.getOperators(clause)" ng-change="$ctrl.changeClauseOperator(clause)" ></select>
         <crm-search-input ng-if="$ctrl.operatorTakesInput(clause[1])" ng-model="clause[2]" field="$ctrl.getField(clause[0])" option-key="$ctrl.getOptionKey(clause[0])" op="clause[1]" format="$ctrl.format" class="form-group"></crm-search-input>
       </div>
       <fieldset class="clearfix" ng-if="$ctrl.conjunctions[clause[0]]">
-        <crm-search-clause clauses="clause[1]" format="{{ $ctrl.format }}" op="{{ clause[0] }}" fields="$ctrl.fields" delete-group="$ctrl.deleteRow(index)" ></crm-search-clause>
+        <crm-search-clause allow-functions="$ctrl.allowFunctions" clauses="clause[1]" format="{{ $ctrl.format }}" op="{{ clause[0] }}" fields="$ctrl.fields" delete-group="$ctrl.deleteRow(index)" ></crm-search-clause>
       </fieldset>
     </div>
   </div>
index 3fda35afe004bb38c7ad97ce04bf855633d03943..847c4c2d854e63ed1538ca7f8f3653a1884595eb 100644 (file)
@@ -3,6 +3,7 @@
 
   angular.module('crmSearchAdmin').component('crmSearchFunction', {
     bindings: {
+      mode: '@',
       expr: '='
     },
     require: {
           // Replace fake function "e"
           ctrl.expr = (ctrl.fnName === 'e' ? '' : ctrl.fnName) + '(';
           ctrl.expr += args.join('');
-          ctrl.expr += ') AS ' + makeAlias();
+          ctrl.expr += ')';
+          if (ctrl.mode === 'select') {
+            ctrl.expr += ' AS ' + makeAlias();
+          }
         } else {
           ctrl.expr = ctrl.args[0].value;
         }
index 2983e106c73db1bb34f1a64072448d6855424d7b..4ffb4f13263c8d9068baab0f7f47a47bc405c3e1 100644 (file)
@@ -1,30 +1,30 @@
-<div class="form-inline">
+<span title="{{:: ts('Transform field using a function') }}">
   <input class="form-control fa-crm-formula" style="min-width: 20px" ng-model="$ctrl.fnName" crm-ui-select="{data: $ctrl.getFunctions, placeholder: ' ', width: 'off', dropdownCss: {width: '275px'}}" ng-change="$ctrl.selectFunction()">
-  <label>{{ $ctrl.fieldArg.field.label }}</label>
-  <label ng-repeat="(val, label) in $ctrl.fn.params[0].flag_before">
-    <input type="checkbox" ng-checked="$ctrl.fieldArg.flag_before === val" ng-click="$ctrl.fieldArg.flag_before = ($ctrl.fieldArg.flag_before === val ? null : val); $ctrl.writeExpr();" >
-    {{ label }}
-  </label>
-  <div class="form-group" ng-repeat="arg in $ctrl.args" ng-if="arg !== $ctrl.fieldArg">
-    <select class="form-control" ng-if="$index && $ctrl.getParam($index).flag_before" ng-model="arg.flag_before" ng-change="$ctrl.writeExpr();">
-      <option ng-repeat="(val, label) in $ctrl.getParam($index).flag_before" value="{{ val }}">
-        {{ label }}
-      </option>
-    </select>
-    <span ng-switch="arg.type">
-      <input ng-switch-when="number" class="form-control" type="number" ng-model="arg.value" placeholder="{{ $ctrl.getParam($index).label }}" ng-change="$ctrl.changeArg($index)" ng-model-options="{updateOn: 'blur'}">
-      <input ng-switch-when="string" class="form-control" ng-model="arg.value" placeholder="{{ $ctrl.getParam($index).label }}" ng-change="$ctrl.changeArg($index)" ng-trim="false" ng-model-options="{updateOn: 'blur'}">
-      <input ng-switch-default class="form-control" ng-model="arg.value" crm-ui-select="{data: $ctrl.getFields, placeholder: $ctrl.getParam($index).label}" ng-change="$ctrl.changeArg($index)">
-    </span>
-  </div>
-  <div class="btn-group" ng-if="$ctrl.canAddArg()">
-    <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
-      <i class="crm-i fa-plus"></i> <span class="caret"></span>
-    </button>
-    <ul class="dropdown-menu">
-      <li ng-repeat="(name, type) in $ctrl.exprTypes" ng-show="$ctrl.getParam($ctrl.args.length).must_be.indexOf(name) >= 0">
-        <a href ng-click="$ctrl.addArg(name)">{{ type.label }}</a>
-      </li>
-    </ul>
-  </div>
+</span>
+<label ng-hide="$ctrl.mode !== 'select' && !$ctrl.fn">{{ $ctrl.fieldArg.field.label }}</label>
+<label ng-repeat="(val, label) in $ctrl.fn.params[0].flag_before">
+  <input type="checkbox" ng-checked="$ctrl.fieldArg.flag_before === val" ng-click="$ctrl.fieldArg.flag_before = ($ctrl.fieldArg.flag_before === val ? null : val); $ctrl.writeExpr();" >
+  {{ label }}
+</label>
+<div class="form-group" ng-repeat="arg in $ctrl.args" ng-if="arg !== $ctrl.fieldArg">
+  <select class="form-control" ng-if="$index && $ctrl.getParam($index).flag_before" ng-model="arg.flag_before" ng-change="$ctrl.writeExpr();">
+    <option ng-repeat="(val, label) in $ctrl.getParam($index).flag_before" value="{{ val }}">
+      {{ label }}
+    </option>
+  </select>
+  <span ng-switch="arg.type">
+    <input ng-switch-when="number" class="form-control" type="number" ng-model="arg.value" placeholder="{{ $ctrl.getParam($index).label }}" ng-change="$ctrl.changeArg($index)" ng-model-options="{updateOn: 'blur'}">
+    <input ng-switch-when="string" class="form-control" ng-model="arg.value" placeholder="{{ $ctrl.getParam($index).label }}" ng-change="$ctrl.changeArg($index)" ng-trim="false" ng-model-options="{updateOn: 'blur'}">
+    <input ng-switch-default class="form-control" ng-model="arg.value" crm-ui-select="{data: $ctrl.getFields, placeholder: $ctrl.getParam($index).label}" ng-change="$ctrl.changeArg($index)">
+  </span>
+</div>
+<div class="btn-group" ng-if="$ctrl.canAddArg()">
+  <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+    <i class="crm-i fa-plus"></i> <span class="caret"></span>
+  </button>
+  <ul class="dropdown-menu">
+    <li ng-repeat="(name, type) in $ctrl.exprTypes" ng-show="$ctrl.getParam($ctrl.args.length).must_be.indexOf(name) >= 0">
+      <a href ng-click="$ctrl.addArg(name)">{{ type.label }}</a>
+    </li>
+  </ul>
 </div>