Commit | Line | Data |
---|---|---|
25523059 CW |
1 | (function(angular, $, _) { |
2 | "use strict"; | |
3 | ||
4 | // Shared between router and searchMeta service | |
2c7e2f4b | 5 | var searchEntity, |
4f0729ed | 6 | joinIndex, |
2c7e2f4b | 7 | undefined; |
25523059 CW |
8 | |
9 | // Declare module and route/controller/services | |
493f83d4 | 10 | angular.module('crmSearchAdmin', CRM.angRequires('crmSearchAdmin')) |
25523059 CW |
11 | |
12 | .config(function($routeProvider) { | |
475029f6 CW |
13 | $routeProvider.when('/list', { |
14 | controller: 'searchList', | |
493f83d4 | 15 | templateUrl: '~/crmSearchAdmin/searchList.html', |
475029f6 CW |
16 | resolve: { |
17 | // Load data for lists | |
18 | savedSearches: function(crmApi4) { | |
19 | return crmApi4('SavedSearch', 'get', { | |
7620b30a CW |
20 | select: [ |
21 | 'id', | |
22 | 'name', | |
23 | 'label', | |
24 | 'api_entity', | |
b40e49df | 25 | 'api_params', |
7620b30a CW |
26 | 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name', |
27 | 'GROUP_CONCAT(display.label ORDER BY display.id) AS display_label', | |
28 | 'GROUP_CONCAT(display.type:icon ORDER BY display.id) AS display_icon', | |
d861cc3f | 29 | 'GROUP_CONCAT(DISTINCT group.title) AS groups' |
7620b30a CW |
30 | ], |
31 | join: [['SearchDisplay AS display'], ['Group AS group']], | |
475029f6 CW |
32 | where: [['api_entity', 'IS NOT NULL']], |
33 | groupBy: ['id'] | |
34 | }); | |
35 | } | |
36 | } | |
37 | }); | |
2894db84 CW |
38 | $routeProvider.when('/create/:entity', { |
39 | controller: 'searchCreate', | |
fb687f04 | 40 | reloadOnSearch: false, |
964ecb17 | 41 | template: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>', |
2894db84 CW |
42 | }); |
43 | $routeProvider.when('/edit/:id', { | |
44 | controller: 'searchEdit', | |
964ecb17 | 45 | template: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>', |
2c7e2f4b | 46 | resolve: { |
2894db84 CW |
47 | // Load saved search |
48 | savedSearch: function($route, crmApi4) { | |
49 | var params = $route.current.params; | |
50 | return crmApi4('SavedSearch', 'get', { | |
51 | where: [['id', '=', params.id]], | |
52 | chain: { | |
2badf248 | 53 | groups: ['Group', 'get', {select: ['id', 'title', 'description', 'visibility', 'group_type'], where: [['saved_search_id', '=', '$id']]}], |
2894db84 CW |
54 | displays: ['SearchDisplay', 'get', {where: [['saved_search_id', '=', '$id']]}] |
55 | } | |
56 | }, 0); | |
2c7e2f4b CW |
57 | } |
58 | } | |
25523059 CW |
59 | }); |
60 | }) | |
61 | ||
2894db84 CW |
62 | // Controller for creating a new search |
63 | .controller('searchCreate', function($scope, $routeParams, $location) { | |
64 | searchEntity = $routeParams.entity; | |
2c7e2f4b | 65 | $scope.$ctrl = this; |
2894db84 CW |
66 | this.savedSearch = { |
67 | api_entity: searchEntity, | |
68 | }; | |
25523059 | 69 | // Changing entity will refresh the angular page |
2894db84 | 70 | $scope.$watch('$ctrl.savedSearch.api_entity', function(newEntity, oldEntity) { |
25523059 | 71 | if (newEntity && oldEntity && newEntity !== oldEntity) { |
2c7e2f4b | 72 | $location.url('/create/' + newEntity); |
25523059 CW |
73 | } |
74 | }); | |
75 | }) | |
76 | ||
2894db84 CW |
77 | // Controller for editing a SavedSearch |
78 | .controller('searchEdit', function($scope, savedSearch) { | |
79 | searchEntity = savedSearch.api_entity; | |
80 | this.savedSearch = savedSearch; | |
81 | $scope.$ctrl = this; | |
82 | }) | |
83 | ||
25523059 CW |
84 | .factory('searchMeta', function() { |
85 | function getEntity(entityName) { | |
86 | if (entityName) { | |
4f0729ed | 87 | return _.find(CRM.crmSearchAdmin.schema, {name: entityName}); |
25523059 CW |
88 | } |
89 | } | |
994168e1 | 90 | // Get join metadata matching a given expression like "Email AS Contact_Email_contact_id_01" |
4f0729ed | 91 | function getJoin(fullNameOrAlias) { |
994168e1 CW |
92 | var alias = _.last(fullNameOrAlias.split(' AS ')), |
93 | path = alias, | |
94 | baseEntity = searchEntity, | |
95 | label = [], | |
96 | join, | |
97 | result; | |
98 | while (path.length) { | |
99 | /* jshint -W083 */ | |
100 | join = _.find(CRM.crmSearchAdmin.joins[baseEntity], function(join) { | |
101 | return new RegExp('^' + join.alias + '_\\d\\d').test(path); | |
102 | }); | |
103 | if (!join) { | |
104 | console.warn( 'Join ' + fullNameOrAlias + ' not found.'); | |
105 | return; | |
106 | } | |
107 | path = path.replace(join.alias + '_', ''); | |
108 | var num = parseInt(path.substr(0, 2), 10); | |
994168e1 CW |
109 | label.push(join.label + (num > 1 ? ' ' + num : '')); |
110 | path = path.replace(/^\d\d_?/, ''); | |
25786bd0 CW |
111 | if (path.length) { |
112 | baseEntity = join.entity; | |
113 | } | |
994168e1 | 114 | } |
25786bd0 | 115 | result = _.assign(_.cloneDeep(join), {label: label.join(' - '), alias: alias, baseEntity: baseEntity}); |
994168e1 CW |
116 | // Add the numbered suffix to the join conditions |
117 | // If this is a deep join, also add the base entity prefix | |
118 | var prefix = alias.replace(new RegExp('_?' + join.alias + '_?\\d?\\d?$'), ''); | |
2f616560 | 119 | function replaceRefs(condition) { |
994168e1 CW |
120 | if (_.isArray(condition)) { |
121 | _.each(condition, function(ref, side) { | |
2f616560 CW |
122 | if (side !== 1 && typeof ref === 'string') { |
123 | if (_.includes(ref, '.')) { | |
124 | condition[side] = ref.replace(join.alias + '.', alias + '.'); | |
125 | } else if (prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) { | |
126 | condition[side] = prefix + '.' + ref; | |
127 | } | |
994168e1 CW |
128 | } |
129 | }); | |
130 | } | |
2f616560 CW |
131 | } |
132 | _.each(result.conditions, replaceRefs); | |
133 | _.each(result.defaults, replaceRefs); | |
994168e1 | 134 | return result; |
4f0729ed | 135 | } |
85b00bbf | 136 | function getFieldAndJoin(fieldName, entityName) { |
c419e6ed | 137 | var dotSplit = fieldName.split('.'), |
25523059 | 138 | joinEntity = dotSplit.length > 1 ? dotSplit[0] : null, |
f9cf8797 | 139 | name = _.last(dotSplit).split(':')[0], |
4f0729ed | 140 | join, |
f9cf8797 | 141 | field; |
27bed3cb CW |
142 | // Custom fields contain a dot in their fieldname |
143 | // If 3 segments, the first is the joinEntity and the last 2 are the custom field | |
144 | if (dotSplit.length === 3) { | |
c419e6ed | 145 | name = dotSplit[1] + '.' + name; |
27bed3cb CW |
146 | } |
147 | // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first. | |
148 | if (dotSplit.length === 2) { | |
f9cf8797 | 149 | field = _.find(getEntity(entityName).fields, {name: dotSplit[0] + '.' + name}); |
27bed3cb | 150 | if (field) { |
f9cf8797 | 151 | field.entity = entityName; |
85b00bbf | 152 | return {field: field}; |
27bed3cb CW |
153 | } |
154 | } | |
25523059 | 155 | if (joinEntity) { |
4f0729ed CW |
156 | join = getJoin(joinEntity); |
157 | entityName = getJoin(joinEntity).entity; | |
25523059 | 158 | } |
f9cf8797 | 159 | field = _.find(getEntity(entityName).fields, {name: name}); |
4f0729ed CW |
160 | if (!field && join && join.bridge) { |
161 | field = _.find(getEntity(join.bridge).fields, {name: name}); | |
162 | } | |
f9cf8797 CW |
163 | if (field) { |
164 | field.entity = entityName; | |
85b00bbf | 165 | return {field: field, join: join}; |
f9cf8797 | 166 | } |
25523059 | 167 | } |
03b55607 | 168 | function parseExpr(expr) { |
2ca319b3 CW |
169 | if (!expr) { |
170 | return; | |
171 | } | |
172 | var splitAs = expr.split(' AS '), | |
173 | info = {fn: null, modifier: ''}, | |
174 | fieldName = splitAs[0], | |
175 | bracketPos = splitAs[0].indexOf('('); | |
03b55607 | 176 | if (bracketPos >= 0) { |
2ca319b3 | 177 | var parsed = splitAs[0].substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/); |
03b55607 | 178 | fieldName = parsed[2]; |
2ca319b3 CW |
179 | info.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)}); |
180 | info.modifier = _.trim(parsed[1]); | |
03b55607 | 181 | } |
2ca319b3 CW |
182 | var fieldAndJoin = getFieldAndJoin(fieldName, searchEntity); |
183 | if (fieldAndJoin) { | |
03b55607 | 184 | var split = fieldName.split(':'), |
85b00bbf | 185 | prefixPos = split[0].lastIndexOf(fieldAndJoin.field.name); |
2ca319b3 CW |
186 | info.path = split[0]; |
187 | info.prefix = prefixPos > 0 ? info.path.substring(0, prefixPos) : ''; | |
188 | info.suffix = !split[1] ? '' : ':' + split[1]; | |
189 | info.field = fieldAndJoin.field; | |
190 | info.join = fieldAndJoin.join; | |
28c02286 | 191 | info.alias = splitAs[1] || (info.fn ? info.fn.name + ':' + info.path : split[0]); |
03b55607 | 192 | } |
2ca319b3 | 193 | return info; |
03b55607 | 194 | } |
25523059 CW |
195 | return { |
196 | getEntity: getEntity, | |
85b00bbf CW |
197 | getField: function(fieldName, entityName) { |
198 | return getFieldAndJoin(fieldName, entityName).field; | |
199 | }, | |
4f0729ed | 200 | getJoin: getJoin, |
03b55607 CW |
201 | parseExpr: parseExpr, |
202 | getDefaultLabel: function(col) { | |
203 | var info = parseExpr(col), | |
204 | label = info.field.label; | |
205 | if (info.fn) { | |
206 | label = '(' + info.fn.title + ') ' + label; | |
c419e6ed | 207 | } |
85b00bbf CW |
208 | if (info.join) { |
209 | label = info.join.label + ': ' + label; | |
210 | } | |
03b55607 | 211 | return label; |
4b01551f CW |
212 | }, |
213 | // Find all possible search columns that could serve as contact_id for a smart group | |
214 | getSmartGroupColumns: function(api_entity, api_params) { | |
25786bd0 | 215 | var joins = _.pluck((api_params.join || []), 0); |
4b01551f CW |
216 | return _.transform([api_entity].concat(joins), function(columns, joinExpr) { |
217 | var joinName = joinExpr.split(' AS '), | |
25786bd0 CW |
218 | joinInfo = joinName[1] ? getJoin(joinName[1]) : {entity: joinName[0]}, |
219 | entity = getEntity(joinInfo.entity), | |
220 | prefix = joinInfo.alias ? joinInfo.alias + '.' : ''; | |
4b01551f | 221 | _.each(entity.fields, function(field) { |
25786bd0 | 222 | if ((entity.name === 'Contact' && field.name === 'id') || (field.fk_entity === 'Contact' && joinInfo.baseEntity !== 'Contact')) { |
4b01551f CW |
223 | columns.push({ |
224 | id: prefix + field.name, | |
25786bd0 | 225 | text: (joinInfo.label ? joinInfo.label + ': ' : '') + field.label, |
4b01551f CW |
226 | icon: entity.icon |
227 | }); | |
228 | } | |
229 | }); | |
4b01551f | 230 | }); |
25523059 CW |
231 | } |
232 | }; | |
25523059 CW |
233 | }); |
234 | ||
235 | })(angular, CRM.$, CRM._); |