1 (function(angular
, $, _
) {
4 // Shared between router and searchMeta service
8 // Declare module and route/controller/services
9 angular
.module('searchAdmin', CRM
.angRequires('searchAdmin'))
11 .config(function($routeProvider
) {
12 $routeProvider
.when('/list', {
13 controller
: 'searchList',
14 templateUrl
: '~/searchAdmin/searchList.html',
16 // Load data for lists
17 savedSearches: function(crmApi4
) {
18 return crmApi4('SavedSearch', 'get', {
19 select
: ['id', 'label', 'api_entity', 'form_values', 'COUNT(search_display.id) AS displays', 'GROUP_CONCAT(group.title) AS groups'],
20 join
: [['SearchDisplay AS search_display'], ['Group AS group']],
21 where
: [['api_entity', 'IS NOT NULL']],
27 $routeProvider
.when('/create/:entity', {
28 controller
: 'searchCreate',
29 template
: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>',
31 $routeProvider
.when('/edit/:id', {
32 controller
: 'searchEdit',
33 template
: '<crm-search-admin saved-search="$ctrl.savedSearch"></crm-search-admin>',
36 savedSearch: function($route
, crmApi4
) {
37 var params
= $route
.current
.params
;
38 return crmApi4('SavedSearch', 'get', {
39 where
: [['id', '=', params
.id
]],
41 groups
: ['Group', 'get', {where
: [['saved_search_id', '=', '$id']]}],
42 displays
: ['SearchDisplay', 'get', {where
: [['saved_search_id', '=', '$id']]}]
50 // Controller for creating a new search
51 .controller('searchCreate', function($scope
, $routeParams
, $location
) {
52 searchEntity
= $routeParams
.entity
;
55 api_entity
: searchEntity
,
57 // Changing entity will refresh the angular page
58 $scope
.$watch('$ctrl.savedSearch.api_entity', function(newEntity
, oldEntity
) {
59 if (newEntity
&& oldEntity
&& newEntity
!== oldEntity
) {
60 $location
.url('/create/' + newEntity
);
65 // Controller for editing a SavedSearch
66 .controller('searchEdit', function($scope
, savedSearch
) {
67 searchEntity
= savedSearch
.api_entity
;
68 this.savedSearch
= savedSearch
;
72 .factory('searchMeta', function() {
73 function getEntity(entityName
) {
75 return _
.find(CRM
.vars
.search
.schema
, {name
: entityName
});
78 function getField(fieldName
, entityName
) {
79 var dotSplit
= fieldName
.split('.'),
80 joinEntity
= dotSplit
.length
> 1 ? dotSplit
[0] : null,
81 name
= _
.last(dotSplit
).split(':')[0];
82 // Custom fields contain a dot in their fieldname
83 // If 3 segments, the first is the joinEntity and the last 2 are the custom field
84 if (dotSplit
.length
=== 3) {
85 name
= dotSplit
[1] + '.' + name
;
87 // If 2 segments, it's ambiguous whether this is a custom field or joined field. Search the main entity first.
88 if (dotSplit
.length
=== 2) {
89 var field
= _
.find(getEntity(entityName
).fields
, {name
: dotSplit
[0] + '.' + name
});
95 entityName
= _
.find(CRM
.vars
.search
.links
[entityName
], {alias
: joinEntity
}).entity
;
97 return _
.find(getEntity(entityName
).fields
, {name
: name
});
100 getEntity
: getEntity
,
102 parseExpr: function(expr
) {
103 var result
= {fn
: null, modifier
: ''},
105 bracketPos
= expr
.indexOf('(');
106 if (bracketPos
>= 0) {
107 var parsed
= expr
.substr(bracketPos
).match(/[ ]?([A-Z]+[ ]+)?([\w.:]+)/);
108 fieldName
= parsed
[2];
109 result
.fn
= _
.find(CRM
.searchAdmin
.functions
, {name
: expr
.substring(0, bracketPos
)});
110 result
.modifier
= _
.trim(parsed
[1]);
112 result
.field
= expr
? getField(fieldName
, searchEntity
) : undefined;
114 var split
= fieldName
.split(':'),
115 prefixPos
= split
[0].lastIndexOf(result
.field
.name
);
116 result
.path
= split
[0];
117 result
.prefix
= prefixPos
> 0 ? result
.path
.substring(0, prefixPos
) : '';
118 result
.suffix
= !split
[1] ? '' : ':' + split
[1];
122 // Find all possible search columns that could serve as contact_id for a smart group
123 getSmartGroupColumns: function(api_entity
, api_params
) {
124 var joins
= _
.pluck((api_params
.join
|| []), 0),
126 return _
.transform([api_entity
].concat(joins
), function(columns
, joinExpr
) {
127 var joinName
= joinExpr
.split(' AS '),
128 entityName
= joinName
[0],
129 entity
= getEntity(entityName
),
130 prefix
= joinName
[1] ? joinName
[1] + '.' : '';
131 _
.each(entity
.fields
, function(field
) {
132 if ((entityName
=== 'Contact' && field
.name
=== 'id') || field
.fk_entity
=== 'Contact') {
134 id
: prefix
+ field
.name
,
135 text
: entity
.titlePlural
+ (entityCount
[entityName
] ? ' ' + entityCount
[entityName
] : '') + ': ' + field
.label
,
140 entityCount
[entityName
] = 1 + (entityCount
[entityName
] || 1);
146 // Reformat an array of objects for compatibility with select2
147 // Todo this probably belongs in core
148 .factory('formatForSelect2', function() {
149 return function(input
, key
, label
, extra
) {
150 return _
.transform(input
, function(result
, item
) {
151 var formatted
= {id
: item
[key
], text
: item
[label
]};
153 _
.merge(formatted
, _
.pick(item
, extra
));
155 result
.push(formatted
);
160 })(angular
, CRM
.$, CRM
._
);