1 (function(angular
, $, _
) {
4 // Shared between router and searchMeta service
6 // For loading saved search
10 // Declare module and route/controller/services
11 angular
.module('search', CRM
.angRequires('search'))
13 .config(function($routeProvider
) {
14 $routeProvider
.when('/:mode/:entity/:name?', {
15 controller
: 'searchRoute',
16 template
: '<div id="bootstrap-theme" class="crm-search"><crm-search ng-if="$ctrl.mode === \'create\'" entity="$ctrl.entity" load=":: $ctrl.savedSearch"></crm-search></div>',
17 reloadOnSearch
: false,
19 // For paths like /load/Group/MySmartGroup, load the group, stash it in the savedSearch variable, and then redirect
20 // For paths like /create/Contact, return the stashed savedSearch if present
21 savedSearch: function($route
, $location
, $timeout
, crmApi4
) {
22 var retrievedSearch
= savedSearch
,
24 params
= $route
.current
.params
;
25 savedSearch
= undefined;
26 switch (params
.mode
) {
28 return retrievedSearch
;
31 // Load savedSearch by `id` (the SavedSearch entity doesn't have `name`)
32 if (params
.entity
=== 'SavedSearch' && /^\d+$/.test(params
.name
)) {
34 where
: [['id', '=', params
.name
]]
37 // Load attached entity (e.g. Smart Groups) with a join via saved_search_id
38 else if (params
.entity
=== 'Group' && params
.name
) {
40 select
: ['id', 'title', 'saved_search_id', 'saved_search.*'],
41 where
: [['name', '=', params
.name
]]
44 // In theory savedSearches could be attached to something other than groups, but for now that's not supported
46 throw 'Failed to load ' + params
.entity
;
48 return crmApi4(params
.entity
, 'get', getParams
, 0).then(function(retrieved
) {
49 savedSearch
= retrieved
;
50 savedSearch
.type
= params
.entity
;
51 if (params
.entity
!== 'SavedSearch') {
52 savedSearch
.api_entity
= retrieved
['saved_search.api_entity'];
53 savedSearch
.api_params
= retrieved
['saved_search.api_params'];
54 savedSearch
.form_values
= retrieved
['saved_search.form_values'];
57 $location
.url('/create/' + savedSearch
.api_entity
);
66 // Controller binds entity to route
67 .controller('searchRoute', function($scope
, $routeParams
, $location
, savedSearch
) {
68 searchEntity
= this.entity
= $routeParams
.entity
;
69 this.mode
= $routeParams
.mode
;
70 this.savedSearch
= savedSearch
;
73 // Changing entity will refresh the angular page
74 $scope
.$watch('$ctrl.entity', function(newEntity
, oldEntity
) {
75 if (newEntity
&& oldEntity
&& newEntity
!== oldEntity
) {
76 $location
.url('/create/' + newEntity
);
81 .factory('searchMeta', function() {
82 function getEntity(entityName
) {
84 entityName
= entityName
=== true ? searchEntity
: entityName
;
85 return _
.find(CRM
.vars
.search
.schema
, {name
: entityName
});
88 function getField(name
) {
89 var dotSplit
= name
.split('.'),
90 joinEntity
= dotSplit
.length
> 1 ? dotSplit
[0] : null,
91 fieldName
= _
.last(dotSplit
).split(':')[0],
92 entityName
= searchEntity
;
93 // Custom fields contain a dot in their fieldname
94 // If 3 segments, the first is the joinEntity and the last 2 are the custom field
95 if (dotSplit
.length
=== 3) {
96 fieldName
= dotSplit
[1] + '.' + fieldName
;
98 // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first.
99 if (dotSplit
.length
=== 2) {
100 var field
= _
.find(getEntity(true).fields
, {name
: dotSplit
[0] + '.' + fieldName
});
106 entityName
= _
.find(CRM
.vars
.search
.links
[entityName
], {alias
: joinEntity
}).entity
;
108 return _
.find(getEntity(entityName
).fields
, {name
: fieldName
});
111 getEntity
: getEntity
,
113 parseExpr: function(expr
) {
114 var result
= {fn
: null, modifier
: ''},
116 bracketPos
= expr
.indexOf('(');
117 if (bracketPos
>= 0) {
118 var parsed
= expr
.substr(bracketPos
).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/);
119 fieldName
= parsed
[2];
120 result
.fn
= _
.find(CRM
.vars
.search
.functions
, {name
: expr
.substring(0, bracketPos
)});
121 result
.modifier
= _
.trim(parsed
[1]);
123 result
.field
= getField(fieldName
);
124 var split
= fieldName
.split(':'),
125 prefixPos
= split
[0].lastIndexOf(result
.field
.name
);
126 result
.path
= split
[0];
127 result
.prefix
= prefixPos
> 0 ? result
.path
.substring(0, prefixPos
) : '';
128 result
.suffix
= !split
[1] ? '' : ':' + split
[1];
134 // Reformat an array of objects for compatibility with select2
135 // Todo this probably belongs in core
136 .factory('formatForSelect2', function() {
137 return function(input
, key
, label
, extra
) {
138 return _
.transform(input
, function(result
, item
) {
139 var formatted
= {id
: item
[key
], text
: item
[label
]};
141 _
.merge(formatted
, _
.pick(item
, extra
));
143 result
.push(formatted
);
148 })(angular
, CRM
.$, CRM
._
);