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?$'), ''); | |
119 | _.each(result.conditions, function(condition) { | |
120 | if (_.isArray(condition)) { | |
121 | _.each(condition, function(ref, side) { | |
122 | if (side !== 1 && _.includes(ref, '.')) { | |
123 | condition[side] = ref.replace(join.alias + '.', alias + '.'); | |
124 | } else if (side !== 1 && prefix.length && !_.includes(ref, '"') && !_.includes(ref, "'")) { | |
125 | condition[side] = prefix + '.' + ref; | |
126 | } | |
127 | }); | |
128 | } | |
129 | }); | |
130 | return result; | |
4f0729ed | 131 | } |
85b00bbf | 132 | function getFieldAndJoin(fieldName, entityName) { |
c419e6ed | 133 | var dotSplit = fieldName.split('.'), |
25523059 | 134 | joinEntity = dotSplit.length > 1 ? dotSplit[0] : null, |
f9cf8797 | 135 | name = _.last(dotSplit).split(':')[0], |
4f0729ed | 136 | join, |
f9cf8797 | 137 | field; |
27bed3cb CW |
138 | // Custom fields contain a dot in their fieldname |
139 | // If 3 segments, the first is the joinEntity and the last 2 are the custom field | |
140 | if (dotSplit.length === 3) { | |
c419e6ed | 141 | name = dotSplit[1] + '.' + name; |
27bed3cb CW |
142 | } |
143 | // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first. | |
144 | if (dotSplit.length === 2) { | |
f9cf8797 | 145 | field = _.find(getEntity(entityName).fields, {name: dotSplit[0] + '.' + name}); |
27bed3cb | 146 | if (field) { |
f9cf8797 | 147 | field.entity = entityName; |
85b00bbf | 148 | return {field: field}; |
27bed3cb CW |
149 | } |
150 | } | |
25523059 | 151 | if (joinEntity) { |
4f0729ed CW |
152 | join = getJoin(joinEntity); |
153 | entityName = getJoin(joinEntity).entity; | |
25523059 | 154 | } |
f9cf8797 | 155 | field = _.find(getEntity(entityName).fields, {name: name}); |
4f0729ed CW |
156 | if (!field && join && join.bridge) { |
157 | field = _.find(getEntity(join.bridge).fields, {name: name}); | |
158 | } | |
f9cf8797 CW |
159 | if (field) { |
160 | field.entity = entityName; | |
85b00bbf | 161 | return {field: field, join: join}; |
f9cf8797 | 162 | } |
25523059 | 163 | } |
03b55607 CW |
164 | function parseExpr(expr) { |
165 | var result = {fn: null, modifier: ''}, | |
166 | fieldName = expr, | |
167 | bracketPos = expr.indexOf('('); | |
168 | if (bracketPos >= 0) { | |
169 | var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/); | |
170 | fieldName = parsed[2]; | |
171 | result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)}); | |
172 | result.modifier = _.trim(parsed[1]); | |
173 | } | |
85b00bbf CW |
174 | var fieldAndJoin = expr ? getFieldAndJoin(fieldName, searchEntity) : undefined; |
175 | if (fieldAndJoin.field) { | |
03b55607 | 176 | var split = fieldName.split(':'), |
85b00bbf | 177 | prefixPos = split[0].lastIndexOf(fieldAndJoin.field.name); |
03b55607 CW |
178 | result.path = split[0]; |
179 | result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : ''; | |
180 | result.suffix = !split[1] ? '' : ':' + split[1]; | |
85b00bbf CW |
181 | result.field = fieldAndJoin.field; |
182 | result.join = fieldAndJoin.join; | |
03b55607 CW |
183 | } |
184 | return result; | |
185 | } | |
25523059 CW |
186 | return { |
187 | getEntity: getEntity, | |
85b00bbf CW |
188 | getField: function(fieldName, entityName) { |
189 | return getFieldAndJoin(fieldName, entityName).field; | |
190 | }, | |
4f0729ed | 191 | getJoin: getJoin, |
03b55607 CW |
192 | parseExpr: parseExpr, |
193 | getDefaultLabel: function(col) { | |
194 | var info = parseExpr(col), | |
195 | label = info.field.label; | |
196 | if (info.fn) { | |
197 | label = '(' + info.fn.title + ') ' + label; | |
c419e6ed | 198 | } |
85b00bbf CW |
199 | if (info.join) { |
200 | label = info.join.label + ': ' + label; | |
201 | } | |
03b55607 | 202 | return label; |
4b01551f CW |
203 | }, |
204 | // Find all possible search columns that could serve as contact_id for a smart group | |
205 | getSmartGroupColumns: function(api_entity, api_params) { | |
25786bd0 | 206 | var joins = _.pluck((api_params.join || []), 0); |
4b01551f CW |
207 | return _.transform([api_entity].concat(joins), function(columns, joinExpr) { |
208 | var joinName = joinExpr.split(' AS '), | |
25786bd0 CW |
209 | joinInfo = joinName[1] ? getJoin(joinName[1]) : {entity: joinName[0]}, |
210 | entity = getEntity(joinInfo.entity), | |
211 | prefix = joinInfo.alias ? joinInfo.alias + '.' : ''; | |
4b01551f | 212 | _.each(entity.fields, function(field) { |
25786bd0 | 213 | if ((entity.name === 'Contact' && field.name === 'id') || (field.fk_entity === 'Contact' && joinInfo.baseEntity !== 'Contact')) { |
4b01551f CW |
214 | columns.push({ |
215 | id: prefix + field.name, | |
25786bd0 | 216 | text: (joinInfo.label ? joinInfo.label + ': ' : '') + field.label, |
4b01551f CW |
217 | icon: entity.icon |
218 | }); | |
219 | } | |
220 | }); | |
4b01551f | 221 | }); |
25523059 CW |
222 | } |
223 | }; | |
25523059 CW |
224 | }); |
225 | ||
226 | })(angular, CRM.$, CRM._); |