e7a8d24efb90bd958b4e80c2cf8632784ccdd357
[civicrm-core.git] / ext / search / ang / crmSearchAdmin.module.js
1 (function(angular, $, _) {
2 "use strict";
3
4 // Shared between router and searchMeta service
5 var searchEntity,
6 undefined;
7
8 // Declare module and route/controller/services
9 angular.module('crmSearchAdmin', CRM.angRequires('crmSearchAdmin'))
10
11 .config(function($routeProvider) {
12 $routeProvider.when('/list', {
13 controller: 'searchList',
14 templateUrl: '~/crmSearchAdmin/searchList.html',
15 resolve: {
16 // Load data for lists
17 savedSearches: function(crmApi4) {
18 return crmApi4('SavedSearch', 'get', {
19 select: [
20 'id',
21 'name',
22 'label',
23 'api_entity',
24 'api_params',
25 'GROUP_CONCAT(display.name ORDER BY display.id) AS display_name',
26 'GROUP_CONCAT(display.label ORDER BY display.id) AS display_label',
27 'GROUP_CONCAT(display.type:icon ORDER BY display.id) AS display_icon',
28 'GROUP_CONCAT(DISTINCT group.title) AS groups'
29 ],
30 join: [['SearchDisplay AS display'], ['Group AS group']],
31 where: [['api_entity', 'IS NOT NULL']],
32 groupBy: ['id']
33 });
34 }
35 }
36 });
37 $routeProvider.when('/create/:entity', {
38 controller: 'searchCreate',
39 reloadOnSearch: false,
40 template: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>',
41 });
42 $routeProvider.when('/edit/:id', {
43 controller: 'searchEdit',
44 template: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>',
45 resolve: {
46 // Load saved search
47 savedSearch: function($route, crmApi4) {
48 var params = $route.current.params;
49 return crmApi4('SavedSearch', 'get', {
50 where: [['id', '=', params.id]],
51 chain: {
52 groups: ['Group', 'get', {select: ['id', 'title', 'description', 'visibility', 'group_type'], where: [['saved_search_id', '=', '$id']]}],
53 displays: ['SearchDisplay', 'get', {where: [['saved_search_id', '=', '$id']]}]
54 }
55 }, 0);
56 }
57 }
58 });
59 })
60
61 // Controller for creating a new search
62 .controller('searchCreate', function($scope, $routeParams, $location) {
63 searchEntity = $routeParams.entity;
64 $scope.$ctrl = this;
65 this.savedSearch = {
66 api_entity: searchEntity,
67 };
68 // Changing entity will refresh the angular page
69 $scope.$watch('$ctrl.savedSearch.api_entity', function(newEntity, oldEntity) {
70 if (newEntity && oldEntity && newEntity !== oldEntity) {
71 $location.url('/create/' + newEntity);
72 }
73 });
74 })
75
76 // Controller for editing a SavedSearch
77 .controller('searchEdit', function($scope, savedSearch) {
78 searchEntity = savedSearch.api_entity;
79 this.savedSearch = savedSearch;
80 $scope.$ctrl = this;
81 })
82
83 .factory('searchMeta', function() {
84 function getEntity(entityName) {
85 if (entityName) {
86 return _.find(CRM.vars.search.schema, {name: entityName});
87 }
88 }
89 function getField(fieldName, entityName) {
90 var dotSplit = fieldName.split('.'),
91 joinEntity = dotSplit.length > 1 ? dotSplit[0] : null,
92 name = _.last(dotSplit).split(':')[0],
93 field;
94 // Custom fields contain a dot in their fieldname
95 // If 3 segments, the first is the joinEntity and the last 2 are the custom field
96 if (dotSplit.length === 3) {
97 name = dotSplit[1] + '.' + name;
98 }
99 // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first.
100 if (dotSplit.length === 2) {
101 field = _.find(getEntity(entityName).fields, {name: dotSplit[0] + '.' + name});
102 if (field) {
103 field.entity = entityName;
104 return field;
105 }
106 }
107 if (joinEntity) {
108 entityName = _.find(CRM.vars.search.links[entityName], {alias: joinEntity}).entity;
109 }
110 field = _.find(getEntity(entityName).fields, {name: name});
111 if (field) {
112 field.entity = entityName;
113 return field;
114 }
115 }
116 function parseExpr(expr) {
117 var result = {fn: null, modifier: ''},
118 fieldName = expr,
119 bracketPos = expr.indexOf('(');
120 if (bracketPos >= 0) {
121 var parsed = expr.substr(bracketPos).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/);
122 fieldName = parsed[2];
123 result.fn = _.find(CRM.crmSearchAdmin.functions, {name: expr.substring(0, bracketPos)});
124 result.modifier = _.trim(parsed[1]);
125 }
126 result.field = expr ? getField(fieldName, searchEntity) : undefined;
127 if (result.field) {
128 var split = fieldName.split(':'),
129 prefixPos = split[0].lastIndexOf(result.field.name);
130 result.path = split[0];
131 result.prefix = prefixPos > 0 ? result.path.substring(0, prefixPos) : '';
132 result.suffix = !split[1] ? '' : ':' + split[1];
133 }
134 return result;
135 }
136 return {
137 getEntity: getEntity,
138 getField: getField,
139 parseExpr: parseExpr,
140 getDefaultLabel: function(col) {
141 var info = parseExpr(col),
142 label = info.field.label;
143 if (info.fn) {
144 label = '(' + info.fn.title + ') ' + label;
145 }
146 return label;
147 },
148 // Find all possible search columns that could serve as contact_id for a smart group
149 getSmartGroupColumns: function(api_entity, api_params) {
150 var joins = _.pluck((api_params.join || []), 0),
151 entityCount = {};
152 return _.transform([api_entity].concat(joins), function(columns, joinExpr) {
153 var joinName = joinExpr.split(' AS '),
154 entityName = joinName[0],
155 entity = getEntity(entityName),
156 prefix = joinName[1] ? joinName[1] + '.' : '';
157 _.each(entity.fields, function(field) {
158 if ((entityName === 'Contact' && field.name === 'id') || field.fk_entity === 'Contact') {
159 columns.push({
160 id: prefix + field.name,
161 text: entity.title_plural + (entityCount[entityName] ? ' ' + entityCount[entityName] : '') + ': ' + field.label,
162 icon: entity.icon
163 });
164 }
165 });
166 entityCount[entityName] = 1 + (entityCount[entityName] || 1);
167 });
168 }
169 };
170 });
171
172 })(angular, CRM.$, CRM._);