Commit | Line | Data |
---|---|---|
e7515b5b CW |
1 | (function(angular, $, _) { |
2 | "use strict"; | |
3 | ||
493f83d4 | 4 | angular.module('crmSearchAdmin').component('crmSearchAdminDisplay', { |
e7515b5b CW |
5 | bindings: { |
6 | savedSearch: '<', | |
7 | display: '<' | |
8 | }, | |
9e827e8e CW |
9 | require: { |
10 | crmSearchAdmin: '^crmSearchAdmin' | |
11 | }, | |
e7515b5b CW |
12 | template: function() { |
13 | // Dynamic template generates switch condition for each display type | |
14 | var html = | |
493f83d4 | 15 | '<div ng-include="\'~/crmSearchAdmin/crmSearchAdminDisplay.html\'"></div>\n' + |
e7515b5b | 16 | '<div ng-switch="$ctrl.display.type">\n'; |
493f83d4 | 17 | _.each(CRM.crmSearchAdmin.displayTypes, function(type) { |
e7515b5b | 18 | html += |
ecb9d1eb CW |
19 | '<div ng-switch-when="' + type.id + '">\n' + |
20 | ' <search-admin-display-' + type.id + ' api-entity="$ctrl.savedSearch.api_entity" api-params="$ctrl.savedSearch.api_params" display="$ctrl.display"></search-admin-display-' + type.id + '>\n' + | |
e7515b5b CW |
21 | ' <hr>\n' + |
22 | ' <button type="button" class="btn btn-{{ !$ctrl.stale ? \'success\' : $ctrl.preview ? \'warning\' : \'primary\' }}" ng-click="$ctrl.previewDisplay()" ng-disabled="!$ctrl.stale">\n' + | |
23 | ' <i class="crm-i ' + type.icon + '"></i>' + | |
24 | ' {{ $ctrl.preview && $ctrl.stale ? ts("Refresh") : ts("Preview") }}\n' + | |
25 | ' </button>\n' + | |
26 | ' <hr>\n' + | |
27 | ' <div ng-if="$ctrl.preview">\n' + | |
406f1014 | 28 | ' <' + type.name + ' api-entity="{{:: $ctrl.savedSearch.api_entity }}" search="$ctrl.savedSearch" display="$ctrl.display" settings="$ctrl.display.settings"></' + type.name + '>\n' + |
e7515b5b CW |
29 | ' </div>\n' + |
30 | '</div>\n'; | |
31 | }); | |
32 | html += '</div>'; | |
33 | return html; | |
34 | }, | |
03b55607 | 35 | controller: function($scope, $timeout, searchMeta) { |
33e81cf6 | 36 | var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'), |
488560cc | 37 | ctrl = this; |
e7515b5b | 38 | |
5623bf2a CW |
39 | this.isSuperAdmin = CRM.checkPerm('all CiviCRM permissions and ACLs'); |
40 | this.aclBypassHelp = ts('Only users with "all CiviCRM permissions and ACLs" can disable permission checks.'); | |
41 | ||
9e827e8e CW |
42 | this.preview = this.stale = false; |
43 | ||
cc7246dd | 44 | // Extra (non-field) colum types |
daa4e55a CW |
45 | this.colTypes = { |
46 | links: { | |
47 | label: ts('Links'), | |
48 | icon: 'fa-link', | |
49 | defaults: { | |
50 | links: [] | |
51 | } | |
52 | }, | |
53 | buttons: { | |
54 | label: ts('Buttons'), | |
55 | icon: 'fa-square-o', | |
56 | defaults: { | |
57 | size: 'btn-sm', | |
58 | links: [] | |
59 | } | |
60 | }, | |
61 | menu: { | |
62 | label: ts('Menu'), | |
63 | icon: 'fa-bars', | |
64 | defaults: { | |
65 | text: ts('Actions'), | |
66 | style: 'default', | |
67 | size: 'btn-sm', | |
68 | icon: 'fa-bars', | |
69 | links: [] | |
70 | } | |
71 | }, | |
2ee83785 CW |
72 | include: { |
73 | label: ts('Custom Code'), | |
74 | icon: 'fa-code', | |
75 | defaults: { | |
76 | path: '' | |
77 | } | |
78 | } | |
daa4e55a CW |
79 | }; |
80 | ||
0458a613 | 81 | // Drag-n-drop settings for reordering columns |
969245e4 CW |
82 | this.sortableOptions = { |
83 | connectWith: '.crm-search-admin-edit-columns', | |
0458a613 CW |
84 | containment: '.crm-search-admin-edit-columns-wrapper', |
85 | cancel: 'input,textarea,button,select,option,a,label' | |
969245e4 CW |
86 | }; |
87 | ||
daa4e55a CW |
88 | this.styles = CRM.crmSearchAdmin.styles; |
89 | ||
90 | this.addCol = function(type) { | |
91 | var col = _.cloneDeep(this.colTypes[type].defaults); | |
92 | col.type = type; | |
93 | if (this.display.type === 'table') { | |
94 | col.alignment = 'text-right'; | |
95 | } | |
96 | ctrl.display.settings.columns.push(col); | |
97 | }; | |
98 | ||
969245e4 | 99 | this.removeCol = function(index) { |
daa4e55a CW |
100 | if (ctrl.display.settings.columns[index].type === 'field') { |
101 | ctrl.hiddenColumns.push(ctrl.display.settings.columns[index]); | |
102 | } | |
969245e4 CW |
103 | ctrl.display.settings.columns.splice(index, 1); |
104 | }; | |
105 | ||
106 | this.restoreCol = function(index) { | |
107 | ctrl.display.settings.columns.push(ctrl.hiddenColumns[index]); | |
108 | ctrl.hiddenColumns.splice(index, 1); | |
109 | }; | |
110 | ||
a3caaf9e CW |
111 | this.getExprFromSelect = function(key) { |
112 | var match; | |
113 | _.each(ctrl.savedSearch.api_params.select, function(expr) { | |
114 | var parts = expr.split(' AS '); | |
115 | if (_.includes(parts, key)) { | |
116 | match = parts[0]; | |
117 | return false; | |
118 | } | |
119 | }); | |
120 | return match; | |
121 | }; | |
122 | ||
123 | this.getFieldLabel = function(key) { | |
124 | var expr = ctrl.getExprFromSelect(key); | |
125 | return searchMeta.getDefaultLabel(expr); | |
126 | }; | |
127 | ||
daa4e55a | 128 | this.getColLabel = function(col) { |
cc7246dd | 129 | if (col.type === 'field' || col.type === 'image') { |
daa4e55a CW |
130 | return ctrl.getFieldLabel(col.key); |
131 | } | |
132 | return ctrl.colTypes[col.type].label; | |
133 | }; | |
134 | ||
cc7246dd CW |
135 | this.toggleEmptyVal = function(col) { |
136 | if (col.empty_value) { | |
137 | delete col.empty_value; | |
138 | } else { | |
139 | col.empty_value = ts('None'); | |
140 | } | |
141 | }; | |
142 | ||
b2ee26f0 CW |
143 | this.toggleRewrite = function(col) { |
144 | if (col.rewrite) { | |
145 | col.rewrite = ''; | |
146 | } else { | |
147 | col.rewrite = '[' + col.key + ']'; | |
148 | delete col.editable; | |
149 | } | |
150 | }; | |
151 | ||
a58b7052 | 152 | this.toggleImage = function(col) { |
cc7246dd | 153 | if (col.type === 'image') { |
a58b7052 | 154 | delete col.image; |
cc7246dd | 155 | col.type = 'field'; |
a58b7052 KJ |
156 | } else { |
157 | col.image = { | |
158 | alt: this.getColLabel(col) | |
159 | }; | |
160 | delete col.editable; | |
cc7246dd | 161 | col.type = 'image'; |
a58b7052 KJ |
162 | } |
163 | }; | |
164 | ||
cc7246dd CW |
165 | this.canBeImage = function(col) { |
166 | var expr = ctrl.getExprFromSelect(col.key), | |
167 | info = searchMeta.parseExpr(expr); | |
168 | return info.args[0] && info.args[0].field && info.args[0].field.input_type === 'File'; | |
169 | }; | |
170 | ||
b2ee26f0 CW |
171 | this.toggleEditable = function(col) { |
172 | if (col.editable) { | |
173 | delete col.editable; | |
c0fcc640 CW |
174 | } else { |
175 | col.editable = true; | |
b2ee26f0 | 176 | } |
b2ee26f0 CW |
177 | }; |
178 | ||
c0fcc640 | 179 | this.canBeEditable = function(col) { |
b2ee26f0 CW |
180 | var expr = ctrl.getExprFromSelect(col.key), |
181 | info = searchMeta.parseExpr(expr); | |
cc7246dd | 182 | return !col.rewrite && !col.link && !info.fn && info.args[0] && info.args[0].field && !info.args[0].field.readonly; |
b2ee26f0 CW |
183 | }; |
184 | ||
f808224c CW |
185 | // Checks if a column contains a sortable value |
186 | // Must be a real sql expression (not a pseudo-field like `result_row_num`) | |
850492de | 187 | this.canBeSortable = function(col) { |
488560cc CW |
188 | // Column-header sorting is incompatible with draggable sorting |
189 | if (ctrl.display.settings.draggable) { | |
190 | return false; | |
191 | } | |
850492de CW |
192 | var expr = ctrl.getExprFromSelect(col.key), |
193 | info = searchMeta.parseExpr(expr), | |
f808224c | 194 | arg = (info && info.args && _.findWhere(info.args, {type: 'field'})) || {}; |
850492de CW |
195 | return arg.field && arg.field.type !== 'Pseudo'; |
196 | }; | |
197 | ||
2fe33e6c CW |
198 | // Aggregate functions (COUNT, AVG, MAX) cannot display as links, except for GROUP_CONCAT |
199 | // which gets special treatment in APIv4 to convert it to an array. | |
200 | this.canBeLink = function(col) { | |
201 | var expr = ctrl.getExprFromSelect(col.key), | |
202 | info = searchMeta.parseExpr(expr); | |
203 | return !info.fn || info.fn.category !== 'aggregate' || info.fn.name === 'GROUP_CONCAT'; | |
204 | }; | |
205 | ||
9446fbaa CW |
206 | var linkProps = ['path', 'entity', 'action', 'join', 'target']; |
207 | ||
2dbf2d72 CW |
208 | this.toggleLink = function(column) { |
209 | if (column.link) { | |
9446fbaa | 210 | ctrl.onChangeLink(column, {}); |
2dbf2d72 | 211 | } else { |
c0fcc640 | 212 | delete column.editable; |
d6704532 | 213 | var defaultLink = ctrl.getLinks(column.key)[0]; |
9446fbaa | 214 | ctrl.onChangeLink(column, defaultLink || {path: 'civicrm/'}); |
2dbf2d72 CW |
215 | } |
216 | }; | |
217 | ||
9446fbaa CW |
218 | this.onChangeLink = function(column, afterLink) { |
219 | column.link = column.link || {}; | |
220 | var beforeLink = column.link.action && _.findWhere(ctrl.getLinks(column.key), {action: column.link.action}); | |
221 | if (!afterLink.action && !afterLink.path) { | |
222 | if (beforeLink && beforeLink.text === column.title) { | |
2dbf2d72 CW |
223 | delete column.title; |
224 | } | |
225 | delete column.link; | |
9446fbaa CW |
226 | return; |
227 | } | |
228 | if (afterLink.text && ((!column.title && !beforeLink) || (beforeLink && beforeLink.text === column.title))) { | |
229 | column.title = afterLink.text; | |
230 | } else if (!afterLink.text && (beforeLink && beforeLink.text === column.title)) { | |
2dbf2d72 CW |
231 | delete column.title; |
232 | } | |
9446fbaa CW |
233 | _.each(linkProps, function(prop) { |
234 | column.link[prop] = afterLink[prop] || ''; | |
235 | }); | |
2dbf2d72 CW |
236 | }; |
237 | ||
d6704532 | 238 | this.getLinks = function(columnKey) { |
daa4e55a | 239 | if (!ctrl.links) { |
957358aa | 240 | ctrl.links = {'*': ctrl.crmSearchAdmin.buildLinks()}; |
daa4e55a | 241 | } |
d6704532 CW |
242 | if (!columnKey) { |
243 | return ctrl.links['*']; | |
244 | } | |
245 | var expr = ctrl.getExprFromSelect(columnKey), | |
246 | info = searchMeta.parseExpr(expr), | |
957358aa | 247 | joinEntity = searchMeta.getJoinEntity(info); |
d6704532 | 248 | if (!ctrl.links[joinEntity]) { |
957358aa | 249 | ctrl.links[joinEntity] = _.filter(ctrl.links['*'], {join: joinEntity}); |
d6704532 CW |
250 | } |
251 | return ctrl.links[joinEntity]; | |
daa4e55a CW |
252 | }; |
253 | ||
daa4e55a CW |
254 | this.pickIcon = function(model, key) { |
255 | searchMeta.pickIcon().then(function(icon) { | |
256 | model[key] = icon; | |
257 | }); | |
258 | }; | |
259 | ||
03b55607 | 260 | // Helper function to sort active from hidden columns and initialize each column with defaults |
69f0bd2b | 261 | this.initColumns = function(defaults) { |
03b55607 CW |
262 | if (!ctrl.display.settings.columns) { |
263 | ctrl.display.settings.columns = _.transform(ctrl.savedSearch.api_params.select, function(columns, fieldExpr) { | |
28218ad6 | 264 | columns.push(searchMeta.fieldToColumn(fieldExpr, defaults)); |
03b55607 | 265 | }); |
969245e4 | 266 | ctrl.hiddenColumns = []; |
03b55607 | 267 | } else { |
a3caaf9e CW |
268 | var activeColumns = _.collect(ctrl.display.settings.columns, 'key'), |
269 | selectAliases = _.map(ctrl.savedSearch.api_params.select, function(select) { | |
270 | return _.last(select.split(' AS ')); | |
271 | }); | |
969245e4 | 272 | ctrl.hiddenColumns = _.transform(ctrl.savedSearch.api_params.select, function(hiddenColumns, fieldExpr) { |
a3caaf9e CW |
273 | var key = _.last(fieldExpr.split(' AS ')); |
274 | if (!_.includes(activeColumns, key)) { | |
28218ad6 | 275 | hiddenColumns.push(searchMeta.fieldToColumn(fieldExpr, defaults)); |
03b55607 CW |
276 | } |
277 | }); | |
a3caaf9e | 278 | _.eachRight(activeColumns, function(key, index) { |
daa4e55a | 279 | if (key && !_.includes(selectAliases, key)) { |
03b55607 CW |
280 | ctrl.display.settings.columns.splice(index, 1); |
281 | } | |
282 | }); | |
03b55607 CW |
283 | } |
284 | }; | |
285 | ||
e7515b5b CW |
286 | this.previewDisplay = function() { |
287 | ctrl.preview = !ctrl.preview; | |
288 | ctrl.stale = false; | |
289 | if (!ctrl.preview) { | |
290 | $timeout(function() { | |
291 | ctrl.preview = true; | |
292 | }, 100); | |
293 | } | |
294 | }; | |
295 | ||
9e827e8e CW |
296 | this.fieldsForSort = function() { |
297 | function disabledIf(key) { | |
298 | return _.findIndex(ctrl.display.settings.sort, [key]) >= 0; | |
299 | } | |
300 | return { | |
9a0f174b CW |
301 | results: [ |
302 | { | |
303 | text: ts('Random'), | |
304 | icon: 'crm-i fa-random', | |
305 | id: 'RAND()', | |
306 | disabled: disabledIf('RAND()') | |
307 | }, | |
308 | { | |
309 | text: ts('Columns'), | |
310 | children: ctrl.crmSearchAdmin.getSelectFields(disabledIf) | |
311 | } | |
312 | ].concat(ctrl.crmSearchAdmin.getAllFields('', ['Field', 'Custom'], disabledIf)) | |
9e827e8e CW |
313 | }; |
314 | }; | |
315 | ||
316 | // Generic function to add to a setting array if the item is not already there | |
317 | this.pushSetting = function(name, value) { | |
318 | ctrl.display.settings[name] = ctrl.display.settings[name] || []; | |
319 | if (_.findIndex(ctrl.display.settings[name], value) < 0) { | |
320 | ctrl.display.settings[name].push(value); | |
321 | } | |
322 | }; | |
323 | ||
e7515b5b CW |
324 | $scope.$watch('$ctrl.display.settings', function() { |
325 | ctrl.stale = true; | |
326 | }, true); | |
327 | } | |
328 | }); | |
329 | ||
330 | })(angular, CRM.$, CRM._); |