From 7d527c18ae9d14bca832dd4d8771fd404259f628 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sat, 11 Sep 2021 10:56:57 -0400 Subject: [PATCH] SearchKit - Add pseudo-fields for row number and current user --- .../SearchDisplay/AbstractRunAction.php | 64 ++++++++++++++++-- ext/search_kit/Civi/Search/Admin.php | 2 + ext/search_kit/ang/crmSearchAdmin.module.js | 4 ++ .../ang/crmSearchAdmin/compose.html | 2 +- .../crmSearchAdmin.component.js | 65 ++++++++++++------- .../crmSearchAdminTokenSelect.component.js | 2 +- .../crmSearchAdminResultsTable.component.js | 2 +- .../crmSearchAdminResultsTable.html | 2 +- 8 files changed, 109 insertions(+), 34 deletions(-) diff --git a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php index f1578097d2..9505e452f0 100644 --- a/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php +++ b/ext/search_kit/Civi/Api4/Action/SearchDisplay/AbstractRunAction.php @@ -121,20 +121,43 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction { $select[$expr->getAlias()] = $item; } $formatted = []; - foreach ($result as $data) { + foreach ($result as $index => $data) { $row = []; foreach ($select as $key => $item) { - $raw = $data[$key] ?? NULL; - $row[$key] = [ - 'raw' => $raw, - 'view' => $this->formatViewValue($item['dataType'], $raw), - ]; + $row[$key] = $this->getValue($key, $data, $item['dataType'], $index); } $formatted[] = $row; } return $formatted; } + /** + * @param $key + * @param $data + * @param $dataType + * @param $index + * @return array + */ + private function getValue($key, $data, $dataType, $index) { + // Get value from api result unless this is a pseudo-field which gets a calculated value + switch ($key) { + case 'result_row_num': + $raw = $index + 1 + ($this->savedSearch['api_params']['offset'] ?? 0); + break; + + case 'user_contact_id': + $raw = \CRM_Core_Session::getLoggedInContactID(); + break; + + default: + $raw = $data[$key] ?? NULL; + } + return [ + 'raw' => $raw, + 'view' => $this->formatViewValue($dataType, $raw), + ]; + } + /** * Returns field definition for a given field or NULL if not found * @param $fieldName @@ -467,4 +490,33 @@ abstract class AbstractRunAction extends \Civi\Api4\Generic\AbstractAction { return $this->_afform; } + /** + * Extra calculated fields provided by SearchKit + * @return array[] + */ + public static function getPseudoFields(): array { + return [ + [ + 'name' => 'result_row_num', + 'fieldName' => 'result_row_num', + 'title' => ts('Row Number'), + 'label' => ts('Row Number'), + 'description' => ts('Index of each row, starting from 1 on the first page'), + 'type' => 'Pseudo', + 'data_type' => 'Integer', + 'readonly' => TRUE, + ], + [ + 'name' => 'user_contact_id', + 'fieldName' => 'result_row_num', + 'title' => ts('Current User ID'), + 'label' => ts('Current User ID'), + 'description' => ts('Contact ID of the current user if logged in'), + 'type' => 'Pseudo', + 'data_type' => 'Integer', + 'readonly' => TRUE, + ], + ]; + } + } diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php index 9e557cbc85..9036b1c1b5 100644 --- a/ext/search_kit/Civi/Search/Admin.php +++ b/ext/search_kit/Civi/Search/Admin.php @@ -11,6 +11,7 @@ namespace Civi\Search; +use Civi\Api4\Action\SearchDisplay\AbstractRunAction; use Civi\Api4\Tag; use CRM_Search_ExtensionUtil as E; @@ -29,6 +30,7 @@ class Admin { return [ 'schema' => self::addImplicitFKFields($schema), 'joins' => self::getJoins($schema), + 'pseudoFields' => AbstractRunAction::getPseudoFields(), 'operators' => \CRM_Utils_Array::makeNonAssociative(self::getOperators()), 'functions' => \CRM_Api4_Page_Api4Explorer::getSqlFunctions(), 'displayTypes' => Display::getDisplayTypes(['id', 'name', 'label', 'description', 'icon']), diff --git a/ext/search_kit/ang/crmSearchAdmin.module.js b/ext/search_kit/ang/crmSearchAdmin.module.js index 867d6952b9..207c10670d 100644 --- a/ext/search_kit/ang/crmSearchAdmin.module.js +++ b/ext/search_kit/ang/crmSearchAdmin.module.js @@ -137,6 +137,10 @@ if (!field && join && join.bridge) { field = _.find(getEntity(join.bridge).fields, {name: name}); } + // Might be a pseudoField + if (!field) { + field = _.cloneDeep(_.find(CRM.crmSearchAdmin.pseudoFields, {name: name})); + } if (field) { field.baseEntity = entityName; return {field: field, join: join}; diff --git a/ext/search_kit/ang/crmSearchAdmin/compose.html b/ext/search_kit/ang/crmSearchAdmin/compose.html index 64b1694b26..6bcf443b91 100644 --- a/ext/search_kit/ang/crmSearchAdmin/compose.html +++ b/ext/search_kit/ang/crmSearchAdmin/compose.html @@ -41,7 +41,7 @@ {{:: ts('Field Transformations') }}
-
+
diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js index ebc00da41f..f06792b4dd 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js @@ -6,7 +6,7 @@ savedSearch: '<' }, templateUrl: '~/crmSearchAdmin/crmSearchAdmin.html', - controller: function($scope, $element, $location, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) { + controller: function($scope, $element, $location, $timeout, crmApi4, dialogService, searchMeta) { var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), ctrl = this, fieldsForJoinGetters = {}; @@ -453,34 +453,37 @@ this.getAllFields = function(suffix, allowedTypes, disabledIf, topJoin) { disabledIf = disabledIf || _.noop; - function formatFields(entityName, join) { + + function formatEntityFields(entityName, join) { var prefix = join ? join.alias + '.' : '', result = []; - function addFields(fields) { - _.each(fields, function(field) { - var item = { - id: prefix + field.name + (field.options ? suffix : ''), - text: field.label, - description: field.description - }; - if (disabledIf(item.id)) { - item.disabled = true; - } - if (!allowedTypes || _.includes(allowedTypes, field.type)) { - result.push(item); - } - }); - } - // Add extra searchable fields from bridge entity if (join && join.bridge) { - addFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) { + formatFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) { return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && !field.fk_entity); - })); + }), result, prefix); } - addFields(searchMeta.getEntity(entityName).fields); + formatFields(searchMeta.getEntity(entityName).fields, result, prefix); + return result; + } + + function formatFields(fields, result, prefix) { + prefix = typeof prefix === 'undefined' ? '' : prefix; + _.each(fields, function(field) { + var item = { + id: prefix + field.name + (field.options ? suffix : ''), + text: field.label, + description: field.description + }; + if (disabledIf(item.id)) { + item.disabled = true; + } + if (!allowedTypes || _.includes(allowedTypes, field.type)) { + result.push(item); + } + }); return result; } @@ -495,7 +498,7 @@ text: joinInfo.label, description: joinInfo.description, icon: joinEntity.icon, - children: formatFields(joinEntity.name, joinInfo) + children: formatEntityFields(joinEntity.name, joinInfo) }); } @@ -508,8 +511,18 @@ result.push({ text: mainEntity.title_plural, icon: mainEntity.icon, - children: formatFields(ctrl.savedSearch.api_entity) + children: formatEntityFields(ctrl.savedSearch.api_entity) }); + + // Include SearchKit's pseudo-fields if specifically requested + if (allowedTypes && _.includes(allowedTypes, 'Pseudo')) { + result.push({ + text: ts('Extra'), + icon: 'fa-gear', + children: formatFields(CRM.crmSearchAdmin.pseudoFields, []) + }); + } + _.each(joinEntities, addJoin); return result; }; @@ -530,6 +543,10 @@ }); }; + this.isPseudoField = function(name) { + return _.findIndex(CRM.crmSearchAdmin.pseudoFields, {name: name}) >= 0; + }; + /** * Fetch pseudoconstants for main entity + joined entities * @@ -646,7 +663,7 @@ _.each(ctrl.savedSearch.api_params.select, function(fieldName) { if (!_.includes(fieldName, ' AS ')) { var info = searchMeta.parseExpr(fieldName); - if (info.field && !info.suffix && !info.fn && (info.field.fk_entity || info.field.name !== info.field.fieldName)) { + if (info.field && !info.suffix && !info.fn && info.field.type === 'Field' && (info.field.fk_entity || info.field.name !== info.field.fieldName)) { var idFieldName = info.field.fk_entity ? fieldName : fieldName.substr(0, fieldName.lastIndexOf('.')), idField = searchMeta.parseExpr(idFieldName).field; if (!ctrl.canAggregate(idFieldName)) { diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js index f364ebf66d..b1f69ba2f2 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdminTokenSelect.component.js @@ -27,7 +27,7 @@ }; this.getTokens = function() { - var allFields = ctrl.admin.getAllFields(ctrl.suffix || '', ['Field', 'Custom', 'Extra']); + var allFields = ctrl.admin.getAllFields(ctrl.suffix || '', ['Field', 'Custom', 'Extra', 'Pseudo']); _.eachRight(ctrl.admin.savedSearch.api_params.select, function(fieldName) { allFields.unshift({ id: fieldName, diff --git a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js index 0a1f994fc7..2329ade4ea 100644 --- a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.component.js @@ -111,7 +111,7 @@ }; $scope.fieldsForSelect = function() { - return {results: ctrl.crmSearchAdmin.getAllFields(':label', ['Field', 'Custom', 'Extra'], function(key) { + return {results: ctrl.crmSearchAdmin.getAllFields(':label', ['Field', 'Custom', 'Extra', 'Pseudo'], function(key) { return _.contains(ctrl.search.api_params.select, key); }) }; diff --git a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html index 56614f2c3d..ab98ae8534 100644 --- a/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html +++ b/ext/search_kit/ang/crmSearchAdmin/resultsTable/crmSearchAdminResultsTable.html @@ -19,7 +19,7 @@ - -- 2.25.1