From: Coleman Watts Date: Thu, 19 Nov 2020 18:25:26 +0000 (-0500) Subject: Search ext: Add List display X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=03b55607c01d7e540860c03fbe8ed2246d1cb796;p=civicrm-core.git Search ext: Add List display Breaks search displays into their own modules, one per display type. This gives finer-grained dependency management per display type. --- diff --git a/css/civicrm.css b/css/civicrm.css index 1a685a2729..997e77b8b0 100644 --- a/css/civicrm.css +++ b/css/civicrm.css @@ -23,6 +23,10 @@ box-sizing: content-box; } +.crm-container .crm-inline-block { + display: inline-block; +} + div.crm-container label { font-weight: normal; display: inline; diff --git a/ext/search/ang/crmSearchAdmin.ang.php b/ext/search/ang/crmSearchAdmin.ang.php index 2a3b21d6b9..9176b14b29 100644 --- a/ext/search/ang/crmSearchAdmin.ang.php +++ b/ext/search/ang/crmSearchAdmin.ang.php @@ -13,6 +13,6 @@ return [ 'ang/crmSearchAdmin', ], 'basePages' => ['civicrm/admin/search'], - 'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'ui.sortable', 'ui.bootstrap', 'api4', 'crmSearchDisplay', 'crmSearchActions', 'crmSearchKit', 'crmRouteBinder'], + 'requires' => ['crmUi', 'crmUtil', 'ngRoute', 'ui.sortable', 'ui.bootstrap', 'api4', 'crmSearchActions', 'crmSearchKit', 'crmRouteBinder'], 'settingsFactory' => ['\Civi\Search\Admin', 'getAdminSettings'], ]; diff --git a/ext/search/ang/crmSearchAdmin.module.js b/ext/search/ang/crmSearchAdmin.module.js index c844282e26..d189b4865a 100644 --- a/ext/search/ang/crmSearchAdmin.module.js +++ b/ext/search/ang/crmSearchAdmin.module.js @@ -113,28 +113,37 @@ return field; } } + function parseExpr(expr) { + var result = {fn: null, modifier: ''}, + fieldName = expr, + bracketPos = expr.indexOf('('); + if (bracketPos >= 0) { + var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/); + fieldName = parsed[2]; + result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)}); + result.modifier = _.trim(parsed[1]); + } + result.field = expr ? getField(fieldName, searchEntity) : undefined; + if (result.field) { + var split = fieldName.split(':'), + prefixPos = split[0].lastIndexOf(result.field.name); + result.path = split[0]; + result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : ''; + result.suffix = !split[1] ? '' : ':' + split[1]; + } + return result; + } return { getEntity: getEntity, getField: getField, - parseExpr: function(expr) { - var result = {fn: null, modifier: ''}, - fieldName = expr, - bracketPos = expr.indexOf('('); - if (bracketPos >= 0) { - var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/); - fieldName = parsed[2]; - result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)}); - result.modifier = _.trim(parsed[1]); - } - result.field = expr ? getField(fieldName, searchEntity) : undefined; - if (result.field) { - var split = fieldName.split(':'), - prefixPos = split[0].lastIndexOf(result.field.name); - result.path = split[0]; - result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : ''; - result.suffix = !split[1] ? '' : ':' + split[1]; + parseExpr: parseExpr, + getDefaultLabel: function(col) { + var info = parseExpr(col), + label = info.field.label; + if (info.fn) { + label = '(' + info.fn.title + ') ' + label; } - return result; + return label; }, // Find all possible search columns that could serve as contact_id for a smart group getSmartGroupColumns: function(api_entity, api_params) { diff --git a/ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js b/ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js index f9f72af6b8..61786f261d 100644 --- a/ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js +++ b/ext/search/ang/crmSearchAdmin/crmSearchAdmin.component.js @@ -518,14 +518,7 @@ return ctrl.allRowsSelected || _.includes(ctrl.selectedRows, row.id); }; - this.getFieldLabel = function(col) { - var info = searchMeta.parseExpr(col), - label = info.field.label; - if (info.fn) { - label = '(' + info.fn.title + ') ' + label; - } - return label; - }; + this.getFieldLabel = searchMeta.getDefaultLabel; // Is a column eligible to use an aggregate function? this.canAggregate = function(col) { diff --git a/ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js b/ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js index 4bdf10f9db..0def0e2770 100644 --- a/ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js +++ b/ext/search/ang/crmSearchAdmin/crmSearchAdminDisplay.component.js @@ -29,10 +29,57 @@ html += ''; return html; }, - controller: function($scope, $timeout) { + controller: function($scope, $timeout, searchMeta) { var ts = $scope.ts = CRM.ts(), ctrl = this; + function fieldToColumn(fieldExpr) { + var info = searchMeta.parseExpr(fieldExpr); + return { + expr: fieldExpr, + label: searchMeta.getDefaultLabel(fieldExpr), + dataType: (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field.data_type + }; + } + + // Helper function to sort active from hidden columns and initialize each column with defaults + this.initColumns = function() { + if (!ctrl.display.settings.columns) { + ctrl.display.settings.columns = _.transform(ctrl.savedSearch.api_params.select, function(columns, fieldExpr) { + columns.push(fieldToColumn(fieldExpr)); + }); + return []; + } else { + var activeColumns = _.collect(ctrl.display.settings.columns, 'expr'), + hiddenColumns = _.transform(ctrl.savedSearch.api_params.select, function(hiddenColumns, fieldExpr) { + if (!_.includes(activeColumns, fieldExpr)) { + hiddenColumns.push(fieldToColumn(fieldExpr)); + } + }); + _.each(activeColumns, function(fieldExpr, index) { + if (!_.includes(ctrl.savedSearch.api_params.select, fieldExpr)) { + ctrl.display.settings.columns.splice(index, 1); + } + }); + return hiddenColumns; + } + }; + + // 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() { diff --git a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.component.js b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.component.js new file mode 100644 index 0000000000..9697a687af --- /dev/null +++ b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.component.js @@ -0,0 +1,65 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('crmSearchAdmin').component('searchAdminDisplayList', { + bindings: { + display: '<', + apiEntity: '<', + apiParams: '<' + }, + require: { + crmSearchAdminDisplay: '^crmSearchAdminDisplay' + }, + templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayList.html', + controller: function($scope, searchMeta) { + var ts = $scope.ts = CRM.ts(), + ctrl = this; + this.getFieldLabel = searchMeta.getDefaultLabel; + + this.sortableOptions = { + connectWith: '.crm-search-admin-edit-columns', + containment: '.crm-search-admin-edit-columns-wrapper' + }; + + this.removeCol = function(index) { + ctrl.hiddenColumns.push(ctrl.display.settings.columns[index]); + ctrl.display.settings.columns.splice(index, 1); + }; + + this.restoreCol = function(index) { + ctrl.display.settings.columns.push(ctrl.hiddenColumns[index]); + ctrl.hiddenColumns.splice(index, 1); + }; + + this.symbols = { + ul: [ + {char: '', label: ts('Default')}, + {char: 'none', label: ts('None ( )')}, + {char: 'circle', label: ts('Circle')}, + {char: 'square', label: ts('Square')}, + ], + ol: [ + {char: '', label: ts('Numbered (1. 2. 3.)')}, + {char: 'none', label: ts('None ( )')}, + {char: 'lower-latin', label: ts('Lowercase (a. b. c.)')}, + {char: 'upper-latin', label: ts('Uppercase (A. B. C.)')}, + {char: 'upper-roman', label: ts('Roman (I. II. III.)')}, + ] + }; + + this.$onInit = function () { + if (!ctrl.display.settings) { + ctrl.display.settings = { + style: 'ul', + limit: 20, + pager: true + }; + } + ctrl.hiddenColumns = ctrl.crmSearchAdminDisplay.initColumns(); + ctrl.links = ctrl.crmSearchAdminDisplay.getLinks(); + }; + + } + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.html b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.html new file mode 100644 index 0000000000..926a595e5d --- /dev/null +++ b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayList.html @@ -0,0 +1,62 @@ +
+
+ + + + +
+
+ + + +
+
+
+
+ {{:: ts('Fields') }} +
+ {{ $ctrl.getFieldLabel(col.expr) }} +
+ + + +
+
+ + + + + +
+
+ + +
+
+ + +
+
+
+
+ {{:: ts('Hidden Fields') }} +
+ {{ $ctrl.getFieldLabel(col.expr) }} +
+ + +
+
+
+
diff --git a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js index 78fb6450da..3ef0cd702a 100644 --- a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js +++ b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.component.js @@ -8,21 +8,13 @@ apiParams: '<' }, require: { - crmSearchAdmin: '^crmSearchAdmin' + crmSearchAdminDisplay: '^crmSearchAdminDisplay' }, templateUrl: '~/crmSearchAdmin/displays/searchAdminDisplayTable.html', controller: function($scope, searchMeta) { var ts = $scope.ts = CRM.ts(), ctrl = this; - - function fieldToColumn(fieldExpr) { - var info = searchMeta.parseExpr(fieldExpr); - return { - expr: fieldExpr, - label: ctrl.getFieldLabel(fieldExpr), - dataType: (info.fn && info.fn.name === 'COUNT') ? 'Integer' : info.field.data_type - }; - } + this.getFieldLabel = searchMeta.getDefaultLabel; this.sortableOptions = { connectWith: '.crm-search-admin-edit-columns', @@ -40,41 +32,14 @@ }; this.$onInit = function () { - ctrl.getFieldLabel = ctrl.crmSearchAdmin.getFieldLabel; if (!ctrl.display.settings) { ctrl.display.settings = { limit: 20, pager: true }; } - if (!ctrl.display.settings.columns) { - ctrl.display.settings.columns = _.transform(ctrl.apiParams.select, function(columns, fieldExpr) { - columns.push(fieldToColumn(fieldExpr)); - }); - ctrl.hiddenColumns = []; - } else { - var activeColumns = _.collect(ctrl.display.settings.columns, 'expr'); - ctrl.hiddenColumns = _.transform(ctrl.apiParams.select, function(hiddenColumns, fieldExpr) { - if (!_.includes(activeColumns, fieldExpr)) { - hiddenColumns.push(fieldToColumn(fieldExpr)); - } - }); - _.each(activeColumns, function(fieldExpr, index) { - if (!_.includes(ctrl.apiParams.select, fieldExpr)) { - ctrl.display.settings.columns.splice(index, 1); - } - }); - } - ctrl.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] + '.'); - ctrl.links.push(link); - }); - }); + ctrl.hiddenColumns = ctrl.crmSearchAdminDisplay.initColumns(); + ctrl.links = ctrl.crmSearchAdminDisplay.getLinks(); }; } diff --git a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html index 66a15a4286..18e391633d 100644 --- a/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html +++ b/ext/search/ang/crmSearchAdmin/displays/searchAdminDisplayTable.html @@ -1,9 +1,9 @@
- - - - + + + +
@@ -12,17 +12,25 @@
{{ $ctrl.getFieldLabel(col.expr) }}
- +
- + + +
+
+
- +
@@ -32,7 +40,7 @@
{{ $ctrl.getFieldLabel(col.expr) }}
- + diff --git a/ext/search/ang/crmSearchDisplay.ang.php b/ext/search/ang/crmSearchDisplay.ang.php index 84d0b762b0..a70d2f1ec2 100644 --- a/ext/search/ang/crmSearchDisplay.ang.php +++ b/ext/search/ang/crmSearchDisplay.ang.php @@ -1,5 +1,5 @@ [ 'ang/crmSearchDisplay.module.js', @@ -10,7 +10,7 @@ return [ 'ang/crmSearchDisplay', ], 'basePages' => [], - 'requires' => ['ngSanitize', 'crmUi', 'api4', 'crmSearchActions', 'ui.bootstrap'], + 'requires' => ['api4', 'ngSanitize'], 'exports' => [ 'crm-search-display-table' => 'E', ], diff --git a/ext/search/ang/crmSearchDisplay.module.js b/ext/search/ang/crmSearchDisplay.module.js index beae3e52e1..9e2134e713 100644 --- a/ext/search/ang/crmSearchDisplay.module.js +++ b/ext/search/ang/crmSearchDisplay.module.js @@ -2,6 +2,62 @@ "use strict"; // Declare module - angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay')); + angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay')) + + .factory('formatSearchValue', 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) { + _.each(data, function(value, key) { + str = str.replace('[' + key + ']', value); + }); + return str; + } + + return function formatSearchValue(row, col, value) { + var type = col.dataType, + result = value; + if (_.isArray(value)) { + return _.map(value, function(val) { + return formatSearchValue(col, val); + }).join(', '); + } + if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) { + result = CRM.utils.formatDate(value, null, type === 'Timestamp'); + } + else if (type === 'Boolean' && typeof value === 'boolean') { + result = value ? ts('Yes') : ts('No'); + } + else if (type === 'Money' && typeof value === 'number') { + result = CRM.formatMoney(value); + } + result = _.escape(result); + if (col.link) { + result = '' + result + ''; + } + return result; + }; + }) + + .factory('searchDisplayFieldCanAggregate', function() { + return function searchDisplayFieldCanAggregate(fieldName, prefix, apiParams) { + // If the query does not use grouping, never + if (!apiParams.groupBy.length) { + return false; + } + // If the column is used for a groupBy, no + if (apiParams.groupBy.indexOf(prefix + fieldName) > -1) { + return false; + } + // If the entity this column belongs to is being grouped by id, then also no + return apiParams.groupBy.indexOf(prefix + 'id') < 0; + }; + }); })(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchDisplay/Pager.html b/ext/search/ang/crmSearchDisplay/Pager.html new file mode 100644 index 0000000000..84c2f8681f --- /dev/null +++ b/ext/search/ang/crmSearchDisplay/Pager.html @@ -0,0 +1,16 @@ +
+
    +
    diff --git a/ext/search/ang/crmSearchDisplayList.ang.php b/ext/search/ang/crmSearchDisplayList.ang.php new file mode 100644 index 0000000000..0538ee815d --- /dev/null +++ b/ext/search/ang/crmSearchDisplayList.ang.php @@ -0,0 +1,16 @@ + [ + 'ang/crmSearchDisplayList.module.js', + 'ang/crmSearchDisplayList/*.js', + ], + 'partials' => [ + 'ang/crmSearchDisplayList', + ], + 'basePages' => ['civicrm/search', 'civicrm/admin/search'], + 'requires' => ['crmSearchDisplay', 'crmUi', 'ui.bootstrap'], + 'exports' => [ + 'crm-search-display-list' => 'E', + ], +]; diff --git a/ext/search/ang/crmSearchDisplayList.module.js b/ext/search/ang/crmSearchDisplayList.module.js new file mode 100644 index 0000000000..ccc0274adf --- /dev/null +++ b/ext/search/ang/crmSearchDisplayList.module.js @@ -0,0 +1,7 @@ +(function(angular, $, _) { + "use strict"; + + // Declare module + angular.module('crmSearchDisplayList', CRM.angRequires('crmSearchDisplayList')); + +})(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.component.js b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.component.js new file mode 100644 index 0000000000..d5e9fa8ca7 --- /dev/null +++ b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.component.js @@ -0,0 +1,77 @@ +(function(angular, $, _) { + "use strict"; + + angular.module('crmSearchDisplayList').component('crmSearchDisplayList', { + bindings: { + apiEntity: '<', + apiParams: '<', + settings: '<', + filters: '<' + }, + templateUrl: '~/crmSearchDisplayList/crmSearchDisplayList.html', + controller: function($scope, crmApi4, formatSearchValue, searchDisplayFieldCanAggregate) { + var ts = $scope.ts = CRM.ts(), + ctrl = this; + this.page = 1; + + this.$onInit = function() { + this.limit = parseInt(ctrl.settings.limit || 0, 10); + ctrl.columns = _.cloneDeep(ctrl.settings.columns); + _.each(ctrl.columns, function(col, num) { + var index = ctrl.apiParams.select.indexOf(col.expr); + if (_.includes(col.expr, '(') && !_.includes(col.expr, ' AS ')) { + col.expr += ' AS column_' + num; + ctrl.apiParams.select[index] += ' AS column_' + num; + } + col.key = _.last(col.expr.split(' AS ')); + }); + }; + + this.getResults = function() { + var params = _.merge(_.cloneDeep(ctrl.apiParams), {limit: ctrl.limit, offset: (ctrl.page - 1) * ctrl.limit}); + if (_.isEmpty(params.where)) { + params.where = []; + } + // Select the ids of joined entities (helps with displaying links) + _.each(params.join, function(join) { + var joinEntity = join[0].split(' AS ')[1], + idField = joinEntity + '.id'; + if (!_.includes(params.select, idField) && !searchDisplayFieldCanAggregate('id', joinEntity + '.', params)) { + params.select.push(idField); + } + }); + _.each(ctrl.filters, function(value, key) { + if (value) { + params.where.push([key, 'CONTAINS', value]); + } + }); + if (ctrl.settings.pager) { + params.select.push('row_count'); + } + crmApi4(ctrl.apiEntity, 'get', params).then(function(results) { + ctrl.results = results; + ctrl.rowCount = results.count; + }); + }; + + $scope.$watch('$ctrl.filters', ctrl.getResults, true); + + $scope.formatResult = function(row, col) { + var value = row[col.key], + formatted = formatSearchValue(row, col, value), + output = ''; + if (formatted.length || (col.label && col.forceLabel)) { + if (col.label && (formatted.length || col.forceLabel)) { + output += ' '; + } + if (formatted.length) { + output += (col.prefix || '') + formatted + (col.suffix || ''); + } + } + return output; + }; + + } + }); + +})(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.html b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.html new file mode 100644 index 0000000000..00c0f55e47 --- /dev/null +++ b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayList.html @@ -0,0 +1,3 @@ +
      +
        +
        diff --git a/ext/search/ang/crmSearchDisplayList/crmSearchDisplayListItems.html b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayListItems.html new file mode 100644 index 0000000000..15b8b1674d --- /dev/null +++ b/ext/search/ang/crmSearchDisplayList/crmSearchDisplayListItems.html @@ -0,0 +1,4 @@ +
      • +
        +
        +
      • diff --git a/ext/search/ang/crmSearchDisplayTable.ang.php b/ext/search/ang/crmSearchDisplayTable.ang.php new file mode 100644 index 0000000000..ea24b14ccc --- /dev/null +++ b/ext/search/ang/crmSearchDisplayTable.ang.php @@ -0,0 +1,16 @@ + [ + 'ang/crmSearchDisplayTable.module.js', + 'ang/crmSearchDisplayTable/*.js', + ], + 'partials' => [ + 'ang/crmSearchDisplayTable', + ], + 'basePages' => ['civicrm/search', 'civicrm/admin/search'], + 'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchActions', 'ui.bootstrap'], + 'exports' => [ + 'crm-search-display-table' => 'E', + ], +]; diff --git a/ext/search/ang/crmSearchDisplayTable.module.js b/ext/search/ang/crmSearchDisplayTable.module.js new file mode 100644 index 0000000000..dab6001d13 --- /dev/null +++ b/ext/search/ang/crmSearchDisplayTable.module.js @@ -0,0 +1,7 @@ +(function(angular, $, _) { + "use strict"; + + // Declare module + angular.module('crmSearchDisplayTable', CRM.angRequires('crmSearchDisplayTable')); + +})(angular, CRM.$, CRM._); diff --git a/ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.component.js b/ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js similarity index 65% rename from ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.component.js rename to ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js index c3d4e5e884..c9ebaeeda9 100644 --- a/ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.component.js +++ b/ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.component.js @@ -1,15 +1,15 @@ (function(angular, $, _) { "use strict"; - angular.module('crmSearchDisplay').component('crmSearchDisplayTable', { + angular.module('crmSearchDisplayTable').component('crmSearchDisplayTable', { bindings: { apiEntity: '<', apiParams: '<', settings: '<', filters: '<' }, - templateUrl: '~/crmSearchDisplay/crmSearchDisplayTable.html', - controller: function($scope, crmApi4) { + templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayTable.html', + controller: function($scope, crmApi4, formatSearchValue, searchDisplayFieldCanAggregate) { var ts = $scope.ts = CRM.ts(), ctrl = this; @@ -40,7 +40,7 @@ _.each(params.join, function(join) { var joinEntity = join[0].split(' AS ')[1], idField = joinEntity + '.id'; - if (!_.includes(params.select, idField) && !canAggregate('id', joinEntity + '.')) { + if (!_.includes(params.select, idField) && !searchDisplayFieldCanAggregate('id', joinEntity + '.', params)) { params.select.push(idField); } }); @@ -89,61 +89,9 @@ $scope.formatResult = function(row, col) { var value = row[col.key]; - return formatFieldValue(row, col, value); + return formatSearchValue(row, col, value); }; - function formatFieldValue(row, col, value) { - var type = col.dataType, - result = value; - if (_.isArray(value)) { - return _.map(value, function(val) { - return formatFieldValue(col, val); - }).join(', '); - } - if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) { - result = CRM.utils.formatDate(value, null, type === 'Timestamp'); - } - else if (type === 'Boolean' && typeof value === 'boolean') { - result = value ? ts('Yes') : ts('No'); - } - else if (type === 'Money' && typeof value === 'number') { - result = CRM.formatMoney(value); - } - result = _.escape(result); - if (col.link) { - result = '' + result + ''; - } - return result; - } - - 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) { - _.each(data, function(value, key) { - str = str.replace('[' + key + ']', value); - }); - return str; - } - - function canAggregate(fieldName, prefix) { - // If the query does not use grouping, never - if (!ctrl.apiParams.groupBy.length) { - return false; - } - // If the column is used for a groupBy, no - if (ctrl.apiParams.groupBy.indexOf(prefix + fieldName) > -1) { - return false; - } - // If the entity this column belongs to is being grouped by id, then also no - return ctrl.apiParams.groupBy.indexOf(prefix + 'id') < 0; - } - $scope.selectAllRows = function() { // Deselect all if (ctrl.allRowsSelected) { diff --git a/ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.html b/ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.html similarity index 67% rename from ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.html rename to ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.html index 6b086334fe..59d55f2d5f 100644 --- a/ext/search/ang/crmSearchDisplay/crmSearchDisplayTable.html +++ b/ext/search/ang/crmSearchDisplayTable/crmSearchDisplayTable.html @@ -18,25 +18,10 @@ - + -
        -
          -
          +
          diff --git a/ext/search/ang/crmSearchKit.ang.php b/ext/search/ang/crmSearchKit.ang.php index e48bd14ce0..1827d07ce8 100644 --- a/ext/search/ang/crmSearchKit.ang.php +++ b/ext/search/ang/crmSearchKit.ang.php @@ -10,5 +10,5 @@ return [ 'ang/crmSearchKit', ], 'basePages' => [], - 'requires' => ['api4'], + 'requires' => [], ]; diff --git a/ext/search/ang/crmSearchPage.ang.php b/ext/search/ang/crmSearchPage.ang.php index b806777464..12508ec61b 100644 --- a/ext/search/ang/crmSearchPage.ang.php +++ b/ext/search/ang/crmSearchPage.ang.php @@ -10,6 +10,6 @@ return [ 'ang/crmSearchPage', ], 'basePages' => ['civicrm/search'], - 'requires' => ['ngRoute', 'api4', 'crmUi', 'crmSearchDisplay'], + 'requires' => ['ngRoute', 'api4', 'crmUi'], 'settingsFactory' => ['\Civi\Search\Display', 'getPageSettings'], ]; diff --git a/ext/search/managed/SearchDisplayType.mgd.php b/ext/search/managed/SearchDisplayType.mgd.php index b8e3fa99b8..0e7930adf5 100644 --- a/ext/search/managed/SearchDisplayType.mgd.php +++ b/ext/search/managed/SearchDisplayType.mgd.php @@ -21,4 +21,15 @@ return [ 'icon' => 'fa-table', ], ], + [ + 'name' => 'SearchDisplayType:list', + 'entity' => 'OptionValue', + 'params' => [ + 'option_group_id' => 'search_display_type', + 'name' => 'list', + 'value' => 'list', + 'label' => 'List', + 'icon' => 'fa-list', + ], + ], ];