Search kit: Add UI support for multiple, nested joins
[civicrm-core.git] / ext / search / ang / crmSearchDisplay.module.js
CommitLineData
e78d6a2d
CW
1(function(angular, $, _) {
2 "use strict";
3
4 // Declare module
03b55607
CW
5 angular.module('crmSearchDisplay', CRM.angRequires('crmSearchDisplay'))
6
b1603dbd 7 .factory('searchDisplayUtils', function() {
03b55607
CW
8 function getUrl(link, row) {
9 var url = replaceTokens(link, row);
10 if (url.slice(0, 1) !== '/' && url.slice(0, 4) !== 'http') {
11 url = CRM.url(url);
12 }
13 return _.escape(url);
14 }
15
16 function replaceTokens(str, data) {
17 _.each(data, function(value, key) {
18 str = str.replace('[' + key + ']', value);
19 });
20 return str;
21 }
22
b1603dbd 23 function formatSearchValue(row, col, value) {
03b55607
CW
24 var type = col.dataType,
25 result = value;
26 if (_.isArray(value)) {
27 return _.map(value, function(val) {
28 return formatSearchValue(col, val);
29 }).join(', ');
30 }
31 if (value && (type === 'Date' || type === 'Timestamp') && /^\d{4}-\d{2}-\d{2}/.test(value)) {
32 result = CRM.utils.formatDate(value, null, type === 'Timestamp');
33 }
34 else if (type === 'Boolean' && typeof value === 'boolean') {
35 result = value ? ts('Yes') : ts('No');
36 }
37 else if (type === 'Money' && typeof value === 'number') {
38 result = CRM.formatMoney(value);
39 }
40 result = _.escape(result);
41 if (col.link) {
42 result = '<a href="' + getUrl(col.link, row) + '">' + result + '</a>';
43 }
44 return result;
b1603dbd 45 }
03b55607 46
b1603dbd 47 function canAggregate(fieldName, prefix, apiParams) {
03b55607
CW
48 // If the query does not use grouping, never
49 if (!apiParams.groupBy.length) {
50 return false;
51 }
52 // If the column is used for a groupBy, no
53 if (apiParams.groupBy.indexOf(prefix + fieldName) > -1) {
54 return false;
55 }
56 // If the entity this column belongs to is being grouped by id, then also no
57 return apiParams.groupBy.indexOf(prefix + 'id') < 0;
b1603dbd
CW
58 }
59
60 function prepareColumns(columns, apiParams) {
61 columns = _.cloneDeep(columns);
62 _.each(columns, function(col, num) {
63 var index = apiParams.select.indexOf(col.expr);
64 if (_.includes(col.expr, '(') && !_.includes(col.expr, ' AS ')) {
65 col.expr += ' AS column_' + num;
66 apiParams.select[index] += ' AS column_' + num;
67 }
68 col.key = _.last(col.expr.split(' AS '));
69 });
70 return columns;
71 }
72
73 function prepareParams(apiParams, filters, page) {
74 var params = _.cloneDeep(apiParams);
75 if (_.isEmpty(params.where)) {
76 params.where = [];
77 }
78 // Select the ids of joined entities (helps with displaying links)
79 _.each(params.join, function(join) {
80 var joinEntity = join[0].split(' AS ')[1],
81 idField = joinEntity + '.id';
82 if (!_.includes(params.select, idField) && !searchDisplayUtils.canAggregate('id', joinEntity + '.', params)) {
83 params.select.push(idField);
84 }
85 });
86 _.each(filters, function(value, key) {
87 if (value) {
88 params.where.push([key, 'CONTAINS', value]);
89 }
90 });
91 if (page) {
92 params.offset = (page - 1) * apiParams.limit;
93 params.select.push('row_count');
94 }
95 return params;
96 }
97
98 return {
99 formatSearchValue: formatSearchValue,
100 canAggregate: canAggregate,
101 prepareColumns: prepareColumns,
102 prepareParams: prepareParams
03b55607
CW
103 };
104 });
e78d6a2d
CW
105
106})(angular, CRM.$, CRM._);