From 91b611856c27bda989ed26045cabbdfca379f2eb Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Sat, 3 Jul 2021 17:47:19 -0400 Subject: [PATCH] SearchKit - Allow multi-valued contact ref fields to be formatted as links This handles multivalued fields by looping through each item and formatting it as a link --- ext/search_kit/Civi/Search/Admin.php | 2 +- .../crmSearchAdmin.component.js | 39 ++++++++++--------- ext/search_kit/ang/crmSearchDisplay.module.js | 35 ++++++++++++----- .../ang/crmSearchDisplay/colType/field.html | 10 +++-- .../crmSearchDisplayList.component.js | 8 ++++ .../crmSearchDisplayTable.component.js | 8 ++++ 6 files changed, 70 insertions(+), 32 deletions(-) diff --git a/ext/search_kit/Civi/Search/Admin.php b/ext/search_kit/Civi/Search/Admin.php index 05ae166e76..046ea0fe8c 100644 --- a/ext/search_kit/Civi/Search/Admin.php +++ b/ext/search_kit/Civi/Search/Admin.php @@ -124,7 +124,7 @@ class Admin { foreach ($schema as &$entity) { if ($entity['searchable'] !== 'bridge') { foreach (array_reverse($entity['fields'], TRUE) as $index => $field) { - if (!empty($field['fk_entity']) && !$field['options'] && empty($field['serialize']) && !empty($schema[$field['fk_entity']]['label_field'])) { + if (!empty($field['fk_entity']) && !$field['options'] && !empty($schema[$field['fk_entity']]['label_field'])) { $isCustom = strpos($field['name'], '.'); // Custom fields: append "Contact ID" to original field label if ($isCustom) { diff --git a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js index 1d56e21cda..80a8cf39d2 100644 --- a/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js +++ b/ext/search_kit/ang/crmSearchAdmin/crmSearchAdmin.component.js @@ -671,18 +671,11 @@ if (info.fn && info.fn.name === 'COUNT') { return value; } - // Output user-facing name/label fields as a link, if possible - if (info.field && info.field.fieldName === searchMeta.getEntity(info.field.entity).label_field && !info.fn && typeof value === 'string') { - var link = getEntityUrl(row, info); - if (link) { - return '' + formatFieldValue(info.field, value) + ''; - } - } - return formatFieldValue(info.field, value); + return formatFieldValue(row, info, value); }; // Attempts to construct a view url for a given entity - function getEntityUrl(row, info) { + function getEntityUrl(row, info, index) { var entity = searchMeta.getEntity(info.field.entity), path = _.result(_.findWhere(entity.paths, {action: 'view'}), 'path'); // Only proceed if the path metadata exists for this entity @@ -696,27 +689,28 @@ if (fieldName === 'id' && info.field.name !== info.field.fieldName) { fieldName = info.field.name.substr(0, info.field.name.lastIndexOf('.')); } - fieldName = prefix + fieldName; - if (row[fieldName]) { - replacements.push(row[fieldName]); + var replacement = row[prefix + fieldName]; + if (replacement) { + replacements.push(_.isArray(replacement) ? replacement[index] : replacement); } }); // Only proceed if the row contains all the necessary data to resolve tokens if (tokens.length === replacements.length) { - _.each(tokens, function(token, index) { - path = path.replace(token, replacements[index]); + _.each(tokens, function(token, key) { + path = path.replace(token, replacements[key]); }); return {url: CRM.url(path), title: path.title}; } } } - function formatFieldValue(field, value) { - var type = field.data_type, - result = value; + function formatFieldValue(row, info, value, index) { + var type = info.field.data_type, + result = value, + link; if (_.isArray(value)) { - return _.map(value, function(val) { - return formatFieldValue(field, val); + return _.map(value, function(val, idx) { + return formatFieldValue(row, info, val, idx); }).join(', '); } if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) { @@ -728,6 +722,13 @@ else if (type === 'Money' && typeof value === 'number') { result = CRM.formatMoney(value); } + // Output user-facing name/label fields as a link, if possible + if (info.field.fieldName === searchMeta.getEntity(info.field.entity).label_field && !info.fn) { + link = getEntityUrl(row, info, index || 0); + } + if (link) { + return '' + _.escape(result) + ''; + } return _.escape(result); } diff --git a/ext/search_kit/ang/crmSearchDisplay.module.js b/ext/search_kit/ang/crmSearchDisplay.module.js index 24c96974b3..64b6bd3c86 100644 --- a/ext/search_kit/ang/crmSearchDisplay.module.js +++ b/ext/search_kit/ang/crmSearchDisplay.module.js @@ -8,22 +8,23 @@ // Replace tokens keyed to rowData. // If rowMeta is provided, values will be formatted; if omitted, raw values will be provided. - function replaceTokens(str, rowData, rowMeta) { + function replaceTokens(str, rowData, rowMeta, index) { if (!str) { return ''; } _.each(rowData, function(value, key) { if (str.indexOf('[' + key + ']') >= 0) { var column = rowMeta && _.findWhere(rowMeta, {key: key}), - replacement = column ? formatRawValue(column, value) : value; + val = column ? formatRawValue(column, value) : value, + replacement = angular.isArray(val) ? val[index || 0] : val; str = str.replace(new RegExp(_.escapeRegExp('[' + key + ']', 'g')), replacement); } }); return str; } - function getUrl(link, rowData) { - var url = replaceTokens(link, rowData); + function getUrl(link, rowData, index) { + var url = replaceTokens(link, rowData, null, index); if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') { url = CRM.url(url); } @@ -31,10 +32,25 @@ } // Returns display value for a single column in a row - function formatDisplayValue(rowData, key, rowMeta) { - var column = _.findWhere(rowMeta, {key: key}), - displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, rowMeta) : formatRawValue(column, rowData[key]); - return displayValue; + function formatDisplayValue(rowData, key, columns) { + var column = _.findWhere(columns, {key: key}), + displayValue = column.rewrite ? replaceTokens(column.rewrite, rowData, columns) : formatRawValue(column, rowData[key]); + return angular.isArray(displayValue) ? displayValue.join(', ') : displayValue; + } + + // Returns value and url for a column formatted as link(s) + function formatLinks(rowData, key, columns) { + var column = _.findWhere(columns, {key: key}), + value = formatRawValue(column, rowData[key]), + values = angular.isArray(value) ? value : [value], + links = []; + _.each(values, function(value, index) { + links.push({ + value: value, + url: getUrl(column.link.path, rowData, index) + }); + }); + return links; } // Formats raw field value according to data type @@ -44,7 +60,7 @@ if (_.isArray(value)) { return _.map(value, function(val) { return formatRawValue(column, val); - }).join(', '); + }); } if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) { result = CRM.utils.formatDate(value, null, type === 'Timestamp'); @@ -88,6 +104,7 @@ return { formatDisplayValue: formatDisplayValue, + formatLinks: formatLinks, getApiParams: getApiParams, getResults: getResults, replaceTokens: replaceTokens, diff --git a/ext/search_kit/ang/crmSearchDisplay/colType/field.html b/ext/search_kit/ang/crmSearchDisplay/colType/field.html index 0d75587970..342f9cae44 100644 --- a/ext/search_kit/ang/crmSearchDisplay/colType/field.html +++ b/ext/search_kit/ang/crmSearchDisplay/colType/field.html @@ -2,6 +2,10 @@ {{:: $ctrl.formatFieldValue(row, col) }} - - {{:: $ctrl.formatFieldValue(row, col) }} - + + + + {{:: link.value }}, + + + diff --git a/ext/search_kit/ang/crmSearchDisplayList/crmSearchDisplayList.component.js b/ext/search_kit/ang/crmSearchDisplayList/crmSearchDisplayList.component.js index 4c18ad614f..a43b020982 100644 --- a/ext/search_kit/ang/crmSearchDisplayList/crmSearchDisplayList.component.js +++ b/ext/search_kit/ang/crmSearchDisplayList/crmSearchDisplayList.component.js @@ -61,6 +61,14 @@ return searchDisplayUtils.formatDisplayValue(rowData, col.key, ctrl.settings.columns); }; + this.getLinks = function(rowData, col) { + rowData._links = rowData._links || {}; + if (!(col.key in rowData._links)) { + rowData._links[col.key] = searchDisplayUtils.formatLinks(rowData, col.key, ctrl.settings.columns); + } + return rowData._links[col.key]; + }; + } }); diff --git a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js index 5b824e944f..5118c0024f 100644 --- a/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js +++ b/ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js @@ -109,6 +109,14 @@ return searchDisplayUtils.formatDisplayValue(rowData, col.key, ctrl.settings.columns); }; + this.getLinks = function(rowData, col) { + rowData._links = rowData._links || {}; + if (!(col.key in rowData._links)) { + rowData._links[col.key] = searchDisplayUtils.formatLinks(rowData, col.key, ctrl.settings.columns); + } + return rowData._links[col.key]; + }; + $scope.selectAllRows = function() { // Deselect all if (ctrl.allRowsSelected) { -- 2.25.1