(function(angular, $, _) {
"use strict";
- angular.module('crmSearchAdmin').component('crmSearchAdmin', {
- bindings: {
- savedSearch: '<'
- },
- templateUrl: '~/crmSearchAdmin/crmSearchAdmin.html',
- controller: function($scope, $element, $location, $timeout, crmApi4, dialogService, searchMeta, crmUiHelp) {
- var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
- ctrl = this,
- afformLoad,
- fieldsForJoinGetters = {};
- $scope.hs = crmUiHelp({file: 'CRM/Search/Help/Compose'});
-
- this.afformEnabled = 'org.civicrm.afform' in CRM.crmSearchAdmin.modules;
- this.afformAdminEnabled = (CRM.checkPerm('administer CiviCRM') || CRM.checkPerm('administer afform')) &&
- 'org.civicrm.afform_admin' in CRM.crmSearchAdmin.modules;
- this.displayTypes = _.indexBy(CRM.crmSearchAdmin.displayTypes, 'id');
- this.searchDisplayPath = CRM.url('civicrm/search');
- this.afformPath = CRM.url('civicrm/admin/afform');
-
- $scope.controls = {tab: 'compose', joinType: 'LEFT'};
- $scope.joinTypes = [
- {k: 'LEFT', v: ts('With (optional)')},
- {k: 'INNER', v: ts('With (required)')},
- {k: 'EXCLUDE', v: ts('Without')},
- ];
- $scope.getEntity = searchMeta.getEntity;
- $scope.getField = searchMeta.getField;
- this.perm = {
- editGroups: CRM.checkPerm('edit groups')
- };
-
- this.$onInit = function() {
- this.entityTitle = searchMeta.getEntity(this.savedSearch.api_entity).title_plural;
-
- this.savedSearch.displays = this.savedSearch.displays || [];
- this.savedSearch.groups = this.savedSearch.groups || [];
- this.savedSearch.tag_id = this.savedSearch.tag_id || [];
- this.groupExists = !!this.savedSearch.groups.length;
-
- if (!this.savedSearch.id) {
- var defaults = {
- version: 4,
- select: getDefaultSelect(),
- orderBy: {},
- where: [],
- };
- _.each(['groupBy', 'join', 'having'], function(param) {
- if (ctrl.paramExists(param)) {
- defaults[param] = [];
- }
- });
- // Default to Individuals
- if (this.savedSearch.api_entity === 'Contact' && CRM.crmSearchAdmin.defaultContactType) {
- defaults.where.push(['contact_type:name', '=', CRM.crmSearchAdmin.defaultContactType]);
+ // Hooks allow code outside this component to modify behaviors.
+ // Register a hook by decorating "crmSearchAdminDirective". Ex:
+ // angular.module('myModule').decorator('crmSearchAdminDirective', function($delegate) {
+ // $delegate[0].controller.hook.postSaveDisplay.push(function(display) {
+ // console.log(display);
+ // });
+ // return $delegate;
+ // });
+ var hook = {
+ preSaveDisplay: [],
+ postSaveDisplay: []
+ };
+
+ // Controller function for main crmSearchAdmin component
+ var ctrl = function($scope, $element, $location, $timeout, crmApi4, dialogService, searchMeta, crmUiHelp) {
+ var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
+ ctrl = this,
+ afformLoad,
+ fieldsForJoinGetters = {};
+ $scope.hs = crmUiHelp({file: 'CRM/Search/Help/Compose'});
+
+ this.afformEnabled = 'org.civicrm.afform' in CRM.crmSearchAdmin.modules;
+ this.afformAdminEnabled = (CRM.checkPerm('administer CiviCRM') || CRM.checkPerm('administer afform')) &&
+ 'org.civicrm.afform_admin' in CRM.crmSearchAdmin.modules;
+ this.displayTypes = _.indexBy(CRM.crmSearchAdmin.displayTypes, 'id');
+ this.searchDisplayPath = CRM.url('civicrm/search');
+ this.afformPath = CRM.url('civicrm/admin/afform');
+
+ $scope.controls = {tab: 'compose', joinType: 'LEFT'};
+ $scope.joinTypes = [
+ {k: 'LEFT', v: ts('With (optional)')},
+ {k: 'INNER', v: ts('With (required)')},
+ {k: 'EXCLUDE', v: ts('Without')},
+ ];
+ $scope.getEntity = searchMeta.getEntity;
+ $scope.getField = searchMeta.getField;
+ this.perm = {
+ editGroups: CRM.checkPerm('edit groups')
+ };
+
+ this.$onInit = function() {
+ this.entityTitle = searchMeta.getEntity(this.savedSearch.api_entity).title_plural;
+
+ this.savedSearch.displays = this.savedSearch.displays || [];
+ this.savedSearch.groups = this.savedSearch.groups || [];
+ this.savedSearch.tag_id = this.savedSearch.tag_id || [];
+ this.groupExists = !!this.savedSearch.groups.length;
+
+ if (!this.savedSearch.id) {
+ var defaults = {
+ version: 4,
+ select: getDefaultSelect(),
+ orderBy: {},
+ where: [],
+ };
+ _.each(['groupBy', 'join', 'having'], function(param) {
+ if (ctrl.paramExists(param)) {
+ defaults[param] = [];
}
+ });
+ // Default to Individuals
+ if (this.savedSearch.api_entity === 'Contact' && CRM.crmSearchAdmin.defaultContactType) {
+ defaults.where.push(['contact_type:name', '=', CRM.crmSearchAdmin.defaultContactType]);
+ }
- $scope.$bindToRoute({
- param: 'params',
- expr: '$ctrl.savedSearch.api_params',
- deep: true,
- default: defaults
- });
+ $scope.$bindToRoute({
+ param: 'params',
+ expr: '$ctrl.savedSearch.api_params',
+ deep: true,
+ default: defaults
+ });
- $scope.$bindToRoute({
- param: 'label',
- expr: '$ctrl.savedSearch.label',
- format: 'raw',
- default: ''
- });
- }
+ $scope.$bindToRoute({
+ param: 'label',
+ expr: '$ctrl.savedSearch.label',
+ format: 'raw',
+ default: ''
+ });
+ }
- $scope.mainEntitySelect = searchMeta.getPrimaryAndSecondaryEntitySelect();
+ $scope.mainEntitySelect = searchMeta.getPrimaryAndSecondaryEntitySelect();
- $scope.$watchCollection('$ctrl.savedSearch.api_params.select', onChangeSelect);
+ $scope.$watchCollection('$ctrl.savedSearch.api_params.select', onChangeSelect);
- $scope.$watch('$ctrl.savedSearch', onChangeAnything, true);
+ $scope.$watch('$ctrl.savedSearch', onChangeAnything, true);
- // After watcher runs for the first time and messes up the status, set it correctly
- $timeout(function() {
- $scope.status = ctrl.savedSearch && ctrl.savedSearch.id ? 'saved' : 'unsaved';
- });
+ // After watcher runs for the first time and messes up the status, set it correctly
+ $timeout(function() {
+ $scope.status = ctrl.savedSearch && ctrl.savedSearch.id ? 'saved' : 'unsaved';
+ });
- loadFieldOptions();
- loadAfforms();
- };
+ loadFieldOptions();
+ loadAfforms();
+ };
- function onChangeAnything() {
- $scope.status = 'unsaved';
- }
+ function onChangeAnything() {
+ $scope.status = 'unsaved';
+ }
- this.save = function() {
- if (!validate()) {
- return;
+ this.save = function() {
+ if (!validate()) {
+ return;
+ }
+ $scope.status = 'saving';
+ var params = _.cloneDeep(ctrl.savedSearch),
+ apiCalls = {},
+ chain = {};
+ if (ctrl.groupExists) {
+ chain.groups = ['Group', 'save', {defaults: {saved_search_id: '$id'}, records: params.groups}];
+ delete params.groups;
+ } else if (params.id) {
+ apiCalls.deleteGroup = ['Group', 'delete', {where: [['saved_search_id', '=', params.id]]}];
+ }
+ _.remove(params.displays, {trashed: true});
+ if (params.displays && params.displays.length) {
+ // Call preSaveDisplay hook
+ if (hook.preSaveDisplay.length) {
+ params.displays.forEach(function(display) {
+ hook.preSaveDisplay.forEach(function(callback) {
+ callback(display, apiCalls);
+ });
+ });
}
- $scope.status = 'saving';
- var params = _.cloneDeep(ctrl.savedSearch),
- apiCalls = {},
- chain = {};
- if (ctrl.groupExists) {
- chain.groups = ['Group', 'save', {defaults: {saved_search_id: '$id'}, records: params.groups}];
- delete params.groups;
- } else if (params.id) {
- apiCalls.deleteGroup = ['Group', 'delete', {where: [['saved_search_id', '=', params.id]]}];
+ chain.displays = ['SearchDisplay', 'replace', {where: [['saved_search_id', '=', '$id']], records: params.displays}];
+ } else if (params.id) {
+ apiCalls.deleteDisplays = ['SearchDisplay', 'delete', {where: [['saved_search_id', '=', params.id]]}];
+ }
+ delete params.displays;
+ if (params.tag_id && params.tag_id.length) {
+ chain.tag_id = ['EntityTag', 'replace', {
+ where: [['entity_id', '=', '$id'], ['entity_table', '=', 'civicrm_saved_search']],
+ match: ['entity_id', 'entity_table', 'tag_id'],
+ records: _.transform(params.tag_id, function(records, id) {records.push({tag_id: id});})
+ }];
+ } else if (params.id) {
+ chain.tag_id = ['EntityTag', 'delete', {
+ where: [['entity_id', '=', '$id'], ['entity_table', '=', 'civicrm_saved_search']]
+ }];
+ }
+ delete params.tag_id;
+ apiCalls.saved = ['SavedSearch', 'save', {records: [params], chain: chain}, 0];
+ crmApi4(apiCalls).then(function(results) {
+ // Call postSaveDisplay hook
+ if (chain.displays && hook.postSaveDisplay.length) {
+ results.saved.displays.forEach(function(display) {
+ hook.postSaveDisplay.forEach(function(callback) {
+ callback(display, results);
+ });
+ });
}
- _.remove(params.displays, {trashed: true});
- if (params.displays && params.displays.length) {
- chain.displays = ['SearchDisplay', 'replace', {where: [['saved_search_id', '=', '$id']], records: params.displays}];
- } else if (params.id) {
- apiCalls.deleteDisplays = ['SearchDisplay', 'delete', {where: [['saved_search_id', '=', params.id]]}];
+ // After saving a new search, redirect to the edit url
+ if (!ctrl.savedSearch.id) {
+ $location.url('edit/' + results.saved.id);
}
- delete params.displays;
- if (params.tag_id && params.tag_id.length) {
- chain.tag_id = ['EntityTag', 'replace', {
- where: [['entity_id', '=', '$id'], ['entity_table', '=', 'civicrm_saved_search']],
- match: ['entity_id', 'entity_table', 'tag_id'],
- records: _.transform(params.tag_id, function(records, id) {records.push({tag_id: id});})
- }];
- } else if (params.id) {
- chain.tag_id = ['EntityTag', 'delete', {
- where: [['entity_id', '=', '$id'], ['entity_table', '=', 'civicrm_saved_search']]
- }];
+ // Set new status to saved unless the user changed something in the interim
+ var newStatus = $scope.status === 'unsaved' ? 'unsaved' : 'saved';
+ if (results.saved.groups && results.saved.groups.length) {
+ ctrl.savedSearch.groups[0].id = results.saved.groups[0].id;
}
- delete params.tag_id;
- apiCalls.saved = ['SavedSearch', 'save', {records: [params], chain: chain}, 0];
- crmApi4(apiCalls).then(function(results) {
- // After saving a new search, redirect to the edit url
- if (!ctrl.savedSearch.id) {
- $location.url('edit/' + results.saved.id);
- }
- // Set new status to saved unless the user changed something in the interim
- var newStatus = $scope.status === 'unsaved' ? 'unsaved' : 'saved';
- if (results.saved.groups && results.saved.groups.length) {
- ctrl.savedSearch.groups[0].id = results.saved.groups[0].id;
- }
- ctrl.savedSearch.displays = results.saved.displays || [];
- // Wait until after onChangeAnything to update status
- $timeout(function() {
- $scope.status = newStatus;
- });
- });
- };
-
- this.paramExists = function(param) {
- return _.includes(searchMeta.getEntity(ctrl.savedSearch.api_entity).params, param);
- };
-
- this.hasFunction = function(expr) {
- return expr.indexOf('(') > -1;
- };
-
- this.addDisplay = function(type) {
- var count = _.filter(ctrl.savedSearch.displays, {type: type}).length,
- searchLabel = ctrl.savedSearch.label || searchMeta.getEntity(ctrl.savedSearch.api_entity).title_plural;
- ctrl.savedSearch.displays.push({
- type: type,
- label: searchLabel + ' ' + ctrl.displayTypes[type].label + ' ' + (count + 1),
+ ctrl.savedSearch.displays = results.saved.displays || [];
+ // Wait until after onChangeAnything to update status
+ $timeout(function() {
+ $scope.status = newStatus;
});
- $scope.selectTab('display_' + (ctrl.savedSearch.displays.length - 1));
- };
-
- this.removeDisplay = function(index) {
- var display = ctrl.savedSearch.displays[index];
- if (display.id) {
- display.trashed = !display.trashed;
- if ($scope.controls.tab === ('display_' + index) && display.trashed) {
- $scope.selectTab('compose');
- } else if (!display.trashed) {
- $scope.selectTab('display_' + index);
- }
- if (display.trashed && afformLoad) {
- afformLoad.then(function() {
- var displayForms = _.filter(ctrl.afforms, function(form) {
- return _.includes(form.displays, ctrl.savedSearch.name + '.' + display.name);
- });
- if (displayForms.length) {
- var msg = displayForms.length === 1 ?
- ts('Form "%1" will be deleted if the embedded display "%2" is deleted.', {1: displayForms[0].title, 2: display.label}) :
- ts('%1 forms will be deleted if the embedded display "%2" is deleted.', {1: displayForms.length, 2: display.label});
- CRM.alert(msg, ts('Display embedded'), 'alert');
- }
- });
- }
- } else {
+ });
+ };
+
+ this.paramExists = function(param) {
+ return _.includes(searchMeta.getEntity(ctrl.savedSearch.api_entity).params, param);
+ };
+
+ this.hasFunction = function(expr) {
+ return expr.indexOf('(') > -1;
+ };
+
+ this.addDisplay = function(type) {
+ var count = _.filter(ctrl.savedSearch.displays, {type: type}).length,
+ searchLabel = ctrl.savedSearch.label || searchMeta.getEntity(ctrl.savedSearch.api_entity).title_plural;
+ ctrl.savedSearch.displays.push({
+ type: type,
+ label: searchLabel + ' ' + ctrl.displayTypes[type].label + ' ' + (count + 1),
+ });
+ $scope.selectTab('display_' + (ctrl.savedSearch.displays.length - 1));
+ };
+
+ this.removeDisplay = function(index) {
+ var display = ctrl.savedSearch.displays[index];
+ if (display.id) {
+ display.trashed = !display.trashed;
+ if ($scope.controls.tab === ('display_' + index) && display.trashed) {
$scope.selectTab('compose');
- ctrl.savedSearch.displays.splice(index, 1);
+ } else if (!display.trashed) {
+ $scope.selectTab('display_' + index);
}
- };
-
- this.cloneDisplay = function(display) {
- var newDisplay = angular.copy(display);
- delete newDisplay.name;
- delete newDisplay.id;
- newDisplay.label += ts(' (copy)');
- ctrl.savedSearch.displays.push(newDisplay);
- $scope.selectTab('display_' + (ctrl.savedSearch.displays.length - 1));
- };
-
- this.addGroup = function() {
- ctrl.savedSearch.groups.push({
- title: '',
- description: '',
- visibility: 'User and User Admin Only',
- group_type: []
- });
- ctrl.groupExists = true;
- $scope.selectTab('group');
- };
-
- $scope.selectTab = function(tab) {
- if (tab === 'group') {
- loadFieldOptions('Group');
- $scope.smartGroupColumns = searchMeta.getSmartGroupColumns(ctrl.savedSearch.api_entity, ctrl.savedSearch.api_params);
- var smartGroupColumns = _.map($scope.smartGroupColumns, 'id');
- if (smartGroupColumns.length && !_.includes(smartGroupColumns, ctrl.savedSearch.api_params.select[0])) {
- ctrl.savedSearch.api_params.select.unshift(smartGroupColumns[0]);
- }
- }
- ctrl.savedSearch.api_params.select = _.uniq(ctrl.savedSearch.api_params.select);
- $scope.controls.tab = tab;
- };
-
- this.removeGroup = function() {
- ctrl.groupExists = !ctrl.groupExists;
- $scope.status = 'unsaved';
- if (!ctrl.groupExists && (!ctrl.savedSearch.groups.length || !ctrl.savedSearch.groups[0].id)) {
- ctrl.savedSearch.groups.length = 0;
+ if (display.trashed && afformLoad) {
+ afformLoad.then(function() {
+ var displayForms = _.filter(ctrl.afforms, function(form) {
+ return _.includes(form.displays, ctrl.savedSearch.name + '.' + display.name);
+ });
+ if (displayForms.length) {
+ var msg = displayForms.length === 1 ?
+ ts('Form "%1" will be deleted if the embedded display "%2" is deleted.', {1: displayForms[0].title, 2: display.label}) :
+ ts('%1 forms will be deleted if the embedded display "%2" is deleted.', {1: displayForms.length, 2: display.label});
+ CRM.alert(msg, ts('Display embedded'), 'alert');
+ }
+ });
}
- if ($scope.controls.tab === 'group') {
- $scope.selectTab('compose');
+ } else {
+ $scope.selectTab('compose');
+ ctrl.savedSearch.displays.splice(index, 1);
+ }
+ };
+
+ this.cloneDisplay = function(display) {
+ var newDisplay = angular.copy(display);
+ delete newDisplay.name;
+ delete newDisplay.id;
+ newDisplay.label += ts(' (copy)');
+ ctrl.savedSearch.displays.push(newDisplay);
+ $scope.selectTab('display_' + (ctrl.savedSearch.displays.length - 1));
+ };
+
+ this.addGroup = function() {
+ ctrl.savedSearch.groups.push({
+ title: '',
+ description: '',
+ visibility: 'User and User Admin Only',
+ group_type: []
+ });
+ ctrl.groupExists = true;
+ $scope.selectTab('group');
+ };
+
+ $scope.selectTab = function(tab) {
+ if (tab === 'group') {
+ loadFieldOptions('Group');
+ $scope.smartGroupColumns = searchMeta.getSmartGroupColumns(ctrl.savedSearch.api_entity, ctrl.savedSearch.api_params);
+ var smartGroupColumns = _.map($scope.smartGroupColumns, 'id');
+ if (smartGroupColumns.length && !_.includes(smartGroupColumns, ctrl.savedSearch.api_params.select[0])) {
+ ctrl.savedSearch.api_params.select.unshift(smartGroupColumns[0]);
}
- };
-
- function addNum(name, num) {
- return name + (num < 10 ? '_0' : '_') + num;
}
-
- function getExistingJoins() {
- return _.transform(ctrl.savedSearch.api_params.join || [], function(joins, join) {
- joins[join[0].split(' AS ')[1]] = searchMeta.getJoin(join[0]);
- }, {});
+ ctrl.savedSearch.api_params.select = _.uniq(ctrl.savedSearch.api_params.select);
+ $scope.controls.tab = tab;
+ };
+
+ this.removeGroup = function() {
+ ctrl.groupExists = !ctrl.groupExists;
+ $scope.status = 'unsaved';
+ if (!ctrl.groupExists && (!ctrl.savedSearch.groups.length || !ctrl.savedSearch.groups[0].id)) {
+ ctrl.savedSearch.groups.length = 0;
}
+ if ($scope.controls.tab === 'group') {
+ $scope.selectTab('compose');
+ }
+ };
- $scope.getJoin = searchMeta.getJoin;
-
- $scope.getJoinEntities = function() {
- var existingJoins = getExistingJoins();
+ function addNum(name, num) {
+ return name + (num < 10 ? '_0' : '_') + num;
+ }
- function addEntityJoins(entity, stack, baseEntity) {
- return _.transform(CRM.crmSearchAdmin.joins[entity], function(joinEntities, join) {
- var num = 0;
- if (
- // Exclude joins that singly point back to the original entity
- !(baseEntity === join.entity && !join.multi) &&
- // Exclude joins to bridge tables
- !searchMeta.getEntity(join.entity).bridge
- ) {
- do {
- appendJoin(joinEntities, join, ++num, stack, entity);
- } while (addNum((stack ? stack + '_' : '') + join.alias, num) in existingJoins);
- }
- }, []);
- }
+ function getExistingJoins() {
+ return _.transform(ctrl.savedSearch.api_params.join || [], function(joins, join) {
+ joins[join[0].split(' AS ')[1]] = searchMeta.getJoin(join[0]);
+ }, {});
+ }
- function appendJoin(collection, join, num, stack, baseEntity) {
- var alias = addNum((stack ? stack + '_' : '') + join.alias, num),
- opt = {
- id: join.entity + ' AS ' + alias,
- description: join.description,
- text: join.label + (num > 1 ? ' ' + num : ''),
- icon: searchMeta.getEntity(join.entity).icon,
- disabled: alias in existingJoins
- };
- if (alias in existingJoins) {
- opt.children = addEntityJoins(join.entity, alias, baseEntity);
+ $scope.getJoin = searchMeta.getJoin;
+
+ $scope.getJoinEntities = function() {
+ var existingJoins = getExistingJoins();
+
+ function addEntityJoins(entity, stack, baseEntity) {
+ return _.transform(CRM.crmSearchAdmin.joins[entity], function(joinEntities, join) {
+ var num = 0;
+ if (
+ // Exclude joins that singly point back to the original entity
+ !(baseEntity === join.entity && !join.multi) &&
+ // Exclude joins to bridge tables
+ !searchMeta.getEntity(join.entity).bridge
+ ) {
+ do {
+ appendJoin(joinEntities, join, ++num, stack, entity);
+ } while (addNum((stack ? stack + '_' : '') + join.alias, num) in existingJoins);
}
- collection.push(opt);
- }
-
- return {results: addEntityJoins(ctrl.savedSearch.api_entity)};
- };
+ }, []);
+ }
- this.addJoin = function(value) {
- if (value) {
- ctrl.savedSearch.api_params.join = ctrl.savedSearch.api_params.join || [];
- var join = searchMeta.getJoin(value),
- entity = searchMeta.getEntity(join.entity),
- params = [value, $scope.controls.joinType || 'LEFT'];
- _.each(_.cloneDeep(join.conditions), function(condition) {
- params.push(condition);
- });
- _.each(_.cloneDeep(join.defaults), function(condition) {
- params.push(condition);
- });
- ctrl.savedSearch.api_params.join.push(params);
- if (entity.label_field && $scope.controls.joinType !== 'EXCLUDE') {
- ctrl.savedSearch.api_params.select.push(join.alias + '.' + entity.label_field);
- }
- loadFieldOptions();
+ function appendJoin(collection, join, num, stack, baseEntity) {
+ var alias = addNum((stack ? stack + '_' : '') + join.alias, num),
+ opt = {
+ id: join.entity + ' AS ' + alias,
+ description: join.description,
+ text: join.label + (num > 1 ? ' ' + num : ''),
+ icon: searchMeta.getEntity(join.entity).icon,
+ disabled: alias in existingJoins
+ };
+ if (alias in existingJoins) {
+ opt.children = addEntityJoins(join.entity, alias, baseEntity);
}
- };
-
- // Remove an explicit join + all SELECT, WHERE & other JOINs that use it
- this.removeJoin = function(index) {
- var alias = searchMeta.getJoin(ctrl.savedSearch.api_params.join[index][0]).alias;
- ctrl.clearParam('join', index);
- removeJoinStuff(alias);
- };
+ collection.push(opt);
+ }
- function removeJoinStuff(alias) {
- _.remove(ctrl.savedSearch.api_params.select, function(item) {
- var pattern = new RegExp('\\b' + alias + '\\.');
- return pattern.test(item.split(' AS ')[0]);
- });
- _.remove(ctrl.savedSearch.api_params.where, function(clause) {
- return clauseUsesJoin(clause, alias);
+ return {results: addEntityJoins(ctrl.savedSearch.api_entity)};
+ };
+
+ this.addJoin = function(value) {
+ if (value) {
+ ctrl.savedSearch.api_params.join = ctrl.savedSearch.api_params.join || [];
+ var join = searchMeta.getJoin(value),
+ entity = searchMeta.getEntity(join.entity),
+ params = [value, $scope.controls.joinType || 'LEFT'];
+ _.each(_.cloneDeep(join.conditions), function(condition) {
+ params.push(condition);
});
- _.eachRight(ctrl.savedSearch.api_params.join, function(item, i) {
- var joinAlias = searchMeta.getJoin(item[0]).alias;
- if (joinAlias !== alias && joinAlias.indexOf(alias) === 0) {
- ctrl.removeJoin(i);
- }
+ _.each(_.cloneDeep(join.defaults), function(condition) {
+ params.push(condition);
});
+ ctrl.savedSearch.api_params.join.push(params);
+ if (entity.label_field && $scope.controls.joinType !== 'EXCLUDE') {
+ ctrl.savedSearch.api_params.select.push(join.alias + '.' + entity.label_field);
+ }
+ loadFieldOptions();
}
+ };
+
+ // Remove an explicit join + all SELECT, WHERE & other JOINs that use it
+ this.removeJoin = function(index) {
+ var alias = searchMeta.getJoin(ctrl.savedSearch.api_params.join[index][0]).alias;
+ ctrl.clearParam('join', index);
+ removeJoinStuff(alias);
+ };
+
+ function removeJoinStuff(alias) {
+ _.remove(ctrl.savedSearch.api_params.select, function(item) {
+ var pattern = new RegExp('\\b' + alias + '\\.');
+ return pattern.test(item.split(' AS ')[0]);
+ });
+ _.remove(ctrl.savedSearch.api_params.where, function(clause) {
+ return clauseUsesJoin(clause, alias);
+ });
+ _.eachRight(ctrl.savedSearch.api_params.join, function(item, i) {
+ var joinAlias = searchMeta.getJoin(item[0]).alias;
+ if (joinAlias !== alias && joinAlias.indexOf(alias) === 0) {
+ ctrl.removeJoin(i);
+ }
+ });
+ }
- this.changeJoinType = function(join) {
- if (join[1] === 'EXCLUDE') {
- removeJoinStuff(searchMeta.getJoin(join[0]).alias);
- }
- };
+ this.changeJoinType = function(join) {
+ if (join[1] === 'EXCLUDE') {
+ removeJoinStuff(searchMeta.getJoin(join[0]).alias);
+ }
+ };
- $scope.changeGroupBy = function(idx) {
- // When clearing a selection
- if (!ctrl.savedSearch.api_params.groupBy[idx]) {
- ctrl.clearParam('groupBy', idx);
+ $scope.changeGroupBy = function(idx) {
+ // When clearing a selection
+ if (!ctrl.savedSearch.api_params.groupBy[idx]) {
+ ctrl.clearParam('groupBy', idx);
+ }
+ reconcileAggregateColumns();
+ };
+
+ function reconcileAggregateColumns() {
+ _.each(ctrl.savedSearch.api_params.select, function(col, pos) {
+ var info = searchMeta.parseExpr(col),
+ fieldExpr = (_.findWhere(info.args, {type: 'field'}) || {}).value;
+ if (ctrl.canAggregate(col)) {
+ // Ensure all non-grouped columns are aggregated if using GROUP BY
+ if (!info.fn || info.fn.category !== 'aggregate') {
+ var dflFn = searchMeta.getDefaultAggregateFn(info) || 'GROUP_CONCAT',
+ flagBefore = dflFn === 'GROUP_CONCAT' ? 'DISTINCT ' : '';
+ ctrl.savedSearch.api_params.select[pos] = dflFn + '(' + flagBefore + fieldExpr + ') AS ' + dflFn + '_' + fieldExpr.replace(/[.:]/g, '_');
+ }
+ } else {
+ // Remove aggregate functions when no grouping
+ if (info.fn && info.fn.category === 'aggregate') {
+ ctrl.savedSearch.api_params.select[pos] = fieldExpr;
+ }
}
- reconcileAggregateColumns();
- };
+ });
+ }
- function reconcileAggregateColumns() {
- _.each(ctrl.savedSearch.api_params.select, function(col, pos) {
- var info = searchMeta.parseExpr(col),
- fieldExpr = (_.findWhere(info.args, {type: 'field'}) || {}).value;
- if (ctrl.canAggregate(col)) {
- // Ensure all non-grouped columns are aggregated if using GROUP BY
- if (!info.fn || info.fn.category !== 'aggregate') {
- var dflFn = searchMeta.getDefaultAggregateFn(info) || 'GROUP_CONCAT',
- flagBefore = dflFn === 'GROUP_CONCAT' ? 'DISTINCT ' : '';
- ctrl.savedSearch.api_params.select[pos] = dflFn + '(' + flagBefore + fieldExpr + ') AS ' + dflFn + '_' + fieldExpr.replace(/[.:]/g, '_');
- }
- } else {
- // Remove aggregate functions when no grouping
- if (info.fn && info.fn.category === 'aggregate') {
- ctrl.savedSearch.api_params.select[pos] = fieldExpr;
- }
- }
+ function clauseUsesJoin(clause, alias) {
+ if (clause[0].indexOf(alias + '.') === 0) {
+ return true;
+ }
+ if (_.isArray(clause[1])) {
+ return clause[1].some(function(subClause) {
+ return clauseUsesJoin(subClause, alias);
});
}
+ return false;
+ }
- function clauseUsesJoin(clause, alias) {
- if (clause[0].indexOf(alias + '.') === 0) {
- return true;
- }
- if (_.isArray(clause[1])) {
- return clause[1].some(function(subClause) {
- return clauseUsesJoin(subClause, alias);
- });
- }
+ // Returns true if a clause contains one of the
+ function clauseUsesFields(clause, fields) {
+ if (!fields || !fields.length) {
return false;
}
-
- // Returns true if a clause contains one of the
- function clauseUsesFields(clause, fields) {
- if (!fields || !fields.length) {
- return false;
- }
- if (_.includes(fields, clause[0])) {
- return true;
- }
- if (_.isArray(clause[1])) {
- return clause[1].some(function(subClause) {
- return clauseUsesField(subClause, fields);
- });
- }
- return false;
+ if (_.includes(fields, clause[0])) {
+ return true;
}
-
- function validate() {
- var errors = [],
- errorEl,
- label,
- tab;
- if (!ctrl.savedSearch.label) {
- errorEl = '#crm-saved-search-label';
- label = ts('Search Label');
- errors.push(ts('%1 is a required field.', {1: label}));
- }
- if (ctrl.groupExists && !ctrl.savedSearch.groups[0].title) {
- errorEl = '#crm-search-admin-group-title';
- label = ts('Group Title');
- errors.push(ts('%1 is a required field.', {1: label}));
- tab = 'group';
- }
- _.each(ctrl.savedSearch.displays, function(display, index) {
- if (!display.trashed && !display.label) {
- errorEl = '#crm-search-admin-display-label';
- label = ts('Display Label');
- errors.push(ts('%1 is a required field.', {1: label}));
- tab = 'display_' + index;
- }
+ if (_.isArray(clause[1])) {
+ return clause[1].some(function(subClause) {
+ return clauseUsesField(subClause, fields);
});
- if (errors.length) {
- if (tab) {
- $scope.selectTab(tab);
- }
- $(errorEl).crmError(errors.join('<br>'), ts('Error Saving'), {expires: 5000});
- }
- return !errors.length;
}
+ return false;
+ }
- this.addParam = function(name, value) {
- if (value && !_.contains(ctrl.savedSearch.api_params[name], value)) {
- ctrl.savedSearch.api_params[name].push(value);
- // This needs to be called when adding a field as well as changing groupBy
- reconcileAggregateColumns();
- }
- };
-
- // Deletes an item from an array param
- this.clearParam = function(name, idx) {
- if (name === 'select') {
- // Function selectors use `ng-repeat` with `track by $index` so must be refreshed when splicing the array
- ctrl.hideFuncitons();
- }
- ctrl.savedSearch.api_params[name].splice(idx, 1);
- };
-
- this.hideFuncitons = function() {
- $scope.controls.showFunctions = false;
- };
-
- function onChangeSelect(newSelect, oldSelect) {
- // When removing a column from SELECT, also remove from ORDER BY & HAVING
- _.each(_.difference(oldSelect, newSelect), function(col) {
- col = _.last(col.split(' AS '));
- delete ctrl.savedSearch.api_params.orderBy[col];
- _.remove(ctrl.savedSearch.api_params.having, function(clause) {
- return clauseUsesFields(clause, [col]);
- });
- });
+ function validate() {
+ var errors = [],
+ errorEl,
+ label,
+ tab;
+ if (!ctrl.savedSearch.label) {
+ errorEl = '#crm-saved-search-label';
+ label = ts('Search Label');
+ errors.push(ts('%1 is a required field.', {1: label}));
}
-
- this.getFieldLabel = searchMeta.getDefaultLabel;
-
- // Is a column eligible to use an aggregate function?
- this.canAggregate = function(col) {
- // If the query does not use grouping, never
- if (!ctrl.savedSearch.api_params.groupBy || !ctrl.savedSearch.api_params.groupBy.length) {
- return false;
- }
- var arg = _.findWhere(searchMeta.parseExpr(col).args, {type: 'field'}) || {};
- // If the column is not a database field, no
- if (!arg.field || !arg.field.entity || !_.includes(['Field', 'Custom', 'Extra'], arg.field.type)) {
- return false;
+ if (ctrl.groupExists && !ctrl.savedSearch.groups[0].title) {
+ errorEl = '#crm-search-admin-group-title';
+ label = ts('Group Title');
+ errors.push(ts('%1 is a required field.', {1: label}));
+ tab = 'group';
+ }
+ _.each(ctrl.savedSearch.displays, function(display, index) {
+ if (!display.trashed && !display.label) {
+ errorEl = '#crm-search-admin-display-label';
+ label = ts('Display Label');
+ errors.push(ts('%1 is a required field.', {1: label}));
+ tab = 'display_' + index;
}
- // If the column is used for a groupBy, no
- if (ctrl.savedSearch.api_params.groupBy.indexOf(arg.path) > -1) {
- return false;
+ });
+ if (errors.length) {
+ if (tab) {
+ $scope.selectTab(tab);
}
- // If the entity this column belongs to is being grouped by primary key, then also no
- var idField = searchMeta.getEntity(arg.field.entity).primary_key[0];
- return ctrl.savedSearch.api_params.groupBy.indexOf(arg.prefix + idField) < 0;
- };
+ $(errorEl).crmError(errors.join('<br>'), ts('Error Saving'), {expires: 5000});
+ }
+ return !errors.length;
+ }
- $scope.fieldsForGroupBy = function() {
- return {results: ctrl.getAllFields('', ['Field', 'Custom', 'Extra'], function(key) {
- return _.contains(ctrl.savedSearch.api_params.groupBy, key);
- })
- };
- };
+ this.addParam = function(name, value) {
+ if (value && !_.contains(ctrl.savedSearch.api_params[name], value)) {
+ ctrl.savedSearch.api_params[name].push(value);
+ // This needs to be called when adding a field as well as changing groupBy
+ reconcileAggregateColumns();
+ }
+ };
- function getFieldsForJoin(joinEntity) {
- return {results: ctrl.getAllFields(':name', ['Field', 'Extra'], null, joinEntity)};
+ // Deletes an item from an array param
+ this.clearParam = function(name, idx) {
+ if (name === 'select') {
+ // Function selectors use `ng-repeat` with `track by $index` so must be refreshed when splicing the array
+ ctrl.hideFuncitons();
}
+ ctrl.savedSearch.api_params[name].splice(idx, 1);
+ };
+
+ this.hideFuncitons = function() {
+ $scope.controls.showFunctions = false;
+ };
+
+ function onChangeSelect(newSelect, oldSelect) {
+ // When removing a column from SELECT, also remove from ORDER BY & HAVING
+ _.each(_.difference(oldSelect, newSelect), function(col) {
+ col = _.last(col.split(' AS '));
+ delete ctrl.savedSearch.api_params.orderBy[col];
+ _.remove(ctrl.savedSearch.api_params.having, function(clause) {
+ return clauseUsesFields(clause, [col]);
+ });
+ });
+ }
- // @return {function}
- $scope.fieldsForJoin = function(joinEntity) {
- if (!fieldsForJoinGetters[joinEntity]) {
- fieldsForJoinGetters[joinEntity] = _.wrap(joinEntity, getFieldsForJoin);
- }
- return fieldsForJoinGetters[joinEntity];
- };
+ this.getFieldLabel = searchMeta.getDefaultLabel;
- $scope.fieldsForWhere = function() {
- return {results: ctrl.getAllFields(':name')};
+ // Is a column eligible to use an aggregate function?
+ this.canAggregate = function(col) {
+ // If the query does not use grouping, never
+ if (!ctrl.savedSearch.api_params.groupBy || !ctrl.savedSearch.api_params.groupBy.length) {
+ return false;
+ }
+ var arg = _.findWhere(searchMeta.parseExpr(col).args, {type: 'field'}) || {};
+ // If the column is not a database field, no
+ if (!arg.field || !arg.field.entity || !_.includes(['Field', 'Custom', 'Extra'], arg.field.type)) {
+ return false;
+ }
+ // If the column is used for a groupBy, no
+ if (ctrl.savedSearch.api_params.groupBy.indexOf(arg.path) > -1) {
+ return false;
+ }
+ // If the entity this column belongs to is being grouped by primary key, then also no
+ var idField = searchMeta.getEntity(arg.field.entity).primary_key[0];
+ return ctrl.savedSearch.api_params.groupBy.indexOf(arg.prefix + idField) < 0;
+ };
+
+ $scope.fieldsForGroupBy = function() {
+ return {results: ctrl.getAllFields('', ['Field', 'Custom', 'Extra'], function(key) {
+ return _.contains(ctrl.savedSearch.api_params.groupBy, key);
+ })
};
+ };
- $scope.fieldsForHaving = function() {
- return {results: ctrl.getSelectFields()};
- };
+ function getFieldsForJoin(joinEntity) {
+ return {results: ctrl.getAllFields(':name', ['Field', 'Extra'], null, joinEntity)};
+ }
- // Sets the default select clause based on commonly-named fields
- function getDefaultSelect() {
- var entity = searchMeta.getEntity(ctrl.savedSearch.api_entity);
- return _.transform(entity.fields, function(defaultSelect, field) {
- if (field.name === 'id' || field.name === entity.label_field) {
- defaultSelect.push(field.name);
- }
- });
+ // @return {function}
+ $scope.fieldsForJoin = function(joinEntity) {
+ if (!fieldsForJoinGetters[joinEntity]) {
+ fieldsForJoinGetters[joinEntity] = _.wrap(joinEntity, getFieldsForJoin);
}
+ return fieldsForJoinGetters[joinEntity];
+ };
- this.getAllFields = function(suffix, allowedTypes, disabledIf, topJoin) {
- disabledIf = disabledIf || _.noop;
- allowedTypes = allowedTypes || ['Field', 'Custom', 'Extra', 'Filter'];
+ $scope.fieldsForWhere = function() {
+ return {results: ctrl.getAllFields(':name')};
+ };
- function formatEntityFields(entityName, join) {
- var prefix = join ? join.alias + '.' : '',
- result = [];
+ $scope.fieldsForHaving = function() {
+ return {results: ctrl.getSelectFields()};
+ };
- // Add extra searchable fields from bridge entity
- if (join && join.bridge) {
- formatFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) {
- return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && field.fk_entity !== entityName);
- }), result, prefix);
- }
-
- formatFields(searchMeta.getEntity(entityName).fields, result, prefix);
- return result;
+ // Sets the default select clause based on commonly-named fields
+ function getDefaultSelect() {
+ var entity = searchMeta.getEntity(ctrl.savedSearch.api_entity);
+ return _.transform(entity.fields, function(defaultSelect, field) {
+ if (field.name === 'id' || field.name === entity.label_field) {
+ defaultSelect.push(field.name);
}
+ });
+ }
- function formatFields(fields, result, prefix) {
- prefix = typeof prefix === 'undefined' ? '' : prefix;
- _.each(fields, function(field) {
- var item = {
- // Use options suffix if available.
- id: prefix + field.name + (_.includes(field.suffixes || [], suffix.replace(':', '')) ? suffix : ''),
- text: field.label,
- description: field.description
- };
- if (disabledIf(item.id)) {
- item.disabled = true;
- }
- if (_.includes(allowedTypes, field.type)) {
- result.push(item);
- }
- });
- return result;
- }
+ this.getAllFields = function(suffix, allowedTypes, disabledIf, topJoin) {
+ disabledIf = disabledIf || _.noop;
+ allowedTypes = allowedTypes || ['Field', 'Custom', 'Extra', 'Filter'];
- var mainEntity = searchMeta.getEntity(ctrl.savedSearch.api_entity),
- joinEntities = _.map(ctrl.savedSearch.api_params.join, 0),
+ function formatEntityFields(entityName, join) {
+ var prefix = join ? join.alias + '.' : '',
result = [];
- function addJoin(join) {
- var joinInfo = searchMeta.getJoin(join),
- joinEntity = searchMeta.getEntity(joinInfo.entity);
- result.push({
- text: joinInfo.label,
- description: joinInfo.description,
- icon: joinEntity.icon,
- children: formatEntityFields(joinEntity.name, joinInfo)
- });
- }
-
- // Place specified join at top of list
- if (topJoin) {
- addJoin(topJoin);
- _.pull(joinEntities, topJoin);
+ // Add extra searchable fields from bridge entity
+ if (join && join.bridge) {
+ formatFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) {
+ return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && field.fk_entity !== entityName);
+ }), result, prefix);
}
- result.push({
- text: mainEntity.title_plural,
- icon: mainEntity.icon,
- children: formatEntityFields(ctrl.savedSearch.api_entity)
- });
-
- // Include SearchKit's pseudo-fields if specifically requested
- if (_.includes(allowedTypes, 'Pseudo')) {
- result.push({
- text: ts('Extra'),
- icon: 'fa-gear',
- children: formatFields(CRM.crmSearchAdmin.pseudoFields, [])
- });
- }
-
- _.each(joinEntities, addJoin);
+ formatFields(searchMeta.getEntity(entityName).fields, result, prefix);
return result;
- };
+ }
- this.getSelectFields = function(disabledIf) {
- disabledIf = disabledIf || _.noop;
- return _.transform(ctrl.savedSearch.api_params.select, function(fields, name) {
- var info = searchMeta.parseExpr(name);
+ function formatFields(fields, result, prefix) {
+ prefix = typeof prefix === 'undefined' ? '' : prefix;
+ _.each(fields, function(field) {
var item = {
- id: info.alias,
- text: ctrl.getFieldLabel(name),
- description: info.fn ? info.fn.description : info.args[0].field && info.args[0].field.description
+ // Use options suffix if available.
+ id: prefix + field.name + (_.includes(field.suffixes || [], suffix.replace(':', '')) ? suffix : ''),
+ text: field.label,
+ description: field.description
};
if (disabledIf(item.id)) {
item.disabled = true;
}
- fields.push(item);
+ if (_.includes(allowedTypes, field.type)) {
+ result.push(item);
+ }
});
- };
+ return result;
+ }
- this.isPseudoField = function(name) {
- return _.findIndex(CRM.crmSearchAdmin.pseudoFields, {name: name}) >= 0;
- };
+ var mainEntity = searchMeta.getEntity(ctrl.savedSearch.api_entity),
+ joinEntities = _.map(ctrl.savedSearch.api_params.join, 0),
+ result = [];
- // Ensure options are loaded for main entity + joined entities
- // And an optional additional entity
- function loadFieldOptions(entity) {
- // Main entity
- var entitiesToLoad = [ctrl.savedSearch.api_entity];
-
- // Join entities + bridge entities
- _.each(ctrl.savedSearch.api_params.join, function(join) {
- var joinInfo = searchMeta.getJoin(join[0]);
- entitiesToLoad.push(joinInfo.entity);
- if (joinInfo.bridge) {
- entitiesToLoad.push(joinInfo.bridge);
- }
+ function addJoin(join) {
+ var joinInfo = searchMeta.getJoin(join),
+ joinEntity = searchMeta.getEntity(joinInfo.entity);
+ result.push({
+ text: joinInfo.label,
+ description: joinInfo.description,
+ icon: joinEntity.icon,
+ children: formatEntityFields(joinEntity.name, joinInfo)
});
+ }
- // Optional additional entity
- if (entity) {
- entitiesToLoad.push(entity);
- }
+ // Place specified join at top of list
+ if (topJoin) {
+ addJoin(topJoin);
+ _.pull(joinEntities, topJoin);
+ }
- searchMeta.loadFieldOptions(entitiesToLoad);
+ result.push({
+ text: mainEntity.title_plural,
+ icon: mainEntity.icon,
+ children: formatEntityFields(ctrl.savedSearch.api_entity)
+ });
+
+ // Include SearchKit's pseudo-fields if specifically requested
+ if (_.includes(allowedTypes, 'Pseudo')) {
+ result.push({
+ text: ts('Extra'),
+ icon: 'fa-gear',
+ children: formatFields(CRM.crmSearchAdmin.pseudoFields, [])
+ });
}
- // Build a list of all possible links to main entity & join entities
- // @return {Array}
- this.buildLinks = function() {
- function addTitle(link, entityName) {
- link.text = link.text.replace('%1', entityName);
- }
+ _.each(joinEntities, addJoin);
+ return result;
+ };
+
+ this.getSelectFields = function(disabledIf) {
+ disabledIf = disabledIf || _.noop;
+ return _.transform(ctrl.savedSearch.api_params.select, function(fields, name) {
+ var info = searchMeta.parseExpr(name);
+ var item = {
+ id: info.alias,
+ text: ctrl.getFieldLabel(name),
+ description: info.fn ? info.fn.description : info.args[0].field && info.args[0].field.description
+ };
+ if (disabledIf(item.id)) {
+ item.disabled = true;
+ }
+ fields.push(item);
+ });
+ };
+
+ this.isPseudoField = function(name) {
+ return _.findIndex(CRM.crmSearchAdmin.pseudoFields, {name: name}) >= 0;
+ };
+
+ // Ensure options are loaded for main entity + joined entities
+ // And an optional additional entity
+ function loadFieldOptions(entity) {
+ // Main entity
+ var entitiesToLoad = [ctrl.savedSearch.api_entity];
+
+ // Join entities + bridge entities
+ _.each(ctrl.savedSearch.api_params.join, function(join) {
+ var joinInfo = searchMeta.getJoin(join[0]);
+ entitiesToLoad.push(joinInfo.entity);
+ if (joinInfo.bridge) {
+ entitiesToLoad.push(joinInfo.bridge);
+ }
+ });
+
+ // Optional additional entity
+ if (entity) {
+ entitiesToLoad.push(entity);
+ }
+
+ searchMeta.loadFieldOptions(entitiesToLoad);
+ }
+
+ // Build a list of all possible links to main entity & join entities
+ // @return {Array}
+ this.buildLinks = function() {
+ function addTitle(link, entityName) {
+ link.text = link.text.replace('%1', entityName);
+ }
- // Links to main entity
- var mainEntity = searchMeta.getEntity(ctrl.savedSearch.api_entity),
- links = _.cloneDeep(mainEntity.links || []);
- _.each(links, function(link) {
- link.join = '';
- addTitle(link, mainEntity.title);
+ // Links to main entity
+ var mainEntity = searchMeta.getEntity(ctrl.savedSearch.api_entity),
+ links = _.cloneDeep(mainEntity.links || []);
+ _.each(links, function(link) {
+ link.join = '';
+ addTitle(link, mainEntity.title);
+ });
+ // Links to explicitly joined entities
+ _.each(ctrl.savedSearch.api_params.join, function(joinClause) {
+ var join = searchMeta.getJoin(joinClause[0]),
+ joinEntity = searchMeta.getEntity(join.entity),
+ bridgeEntity = _.isString(joinClause[2]) ? searchMeta.getEntity(joinClause[2]) : null;
+ _.each(_.cloneDeep(joinEntity.links), function(link) {
+ link.join = join.alias;
+ addTitle(link, join.label);
+ links.push(link);
});
- // Links to explicitly joined entities
- _.each(ctrl.savedSearch.api_params.join, function(joinClause) {
- var join = searchMeta.getJoin(joinClause[0]),
- joinEntity = searchMeta.getEntity(join.entity),
- bridgeEntity = _.isString(joinClause[2]) ? searchMeta.getEntity(joinClause[2]) : null;
- _.each(_.cloneDeep(joinEntity.links), function(link) {
- link.join = join.alias;
- addTitle(link, join.label);
- links.push(link);
- });
- _.each(_.cloneDeep(bridgeEntity && bridgeEntity.links), function(link) {
- link.join = join.alias;
- addTitle(link, join.label + (bridgeEntity.bridge_title ? ' ' + bridgeEntity.bridge_title : ''));
- links.push(link);
- });
+ _.each(_.cloneDeep(bridgeEntity && bridgeEntity.links), function(link) {
+ link.join = join.alias;
+ addTitle(link, join.label + (bridgeEntity.bridge_title ? ' ' + bridgeEntity.bridge_title : ''));
+ links.push(link);
});
- // Links to implicit joins
- _.each(ctrl.savedSearch.api_params.select, function(fieldName) {
- if (!_.includes(fieldName, ' AS ')) {
- var info = searchMeta.parseExpr(fieldName).args[0];
- if (info.field && !info.suffix && !info.fn && info.field.type === 'Field' && (info.field.fk_entity || info.field.name !== info.field.fieldName)) {
- var idFieldName = info.field.fk_entity ? fieldName : fieldName.substr(0, fieldName.lastIndexOf('.')),
- idField = searchMeta.parseExpr(idFieldName).args[0].field;
- if (!ctrl.canAggregate(idFieldName)) {
- var joinEntity = searchMeta.getEntity(idField.fk_entity),
- label = (idField.join ? idField.join.label + ': ' : '') + (idField.input_attrs && idField.input_attrs.label || idField.label);
- _.each(_.cloneDeep(joinEntity && joinEntity.links), function(link) {
- link.join = idFieldName;
- addTitle(link, label);
- links.push(link);
- });
- }
+ });
+ // Links to implicit joins
+ _.each(ctrl.savedSearch.api_params.select, function(fieldName) {
+ if (!_.includes(fieldName, ' AS ')) {
+ var info = searchMeta.parseExpr(fieldName).args[0];
+ if (info.field && !info.suffix && !info.fn && info.field.type === 'Field' && (info.field.fk_entity || info.field.name !== info.field.fieldName)) {
+ var idFieldName = info.field.fk_entity ? fieldName : fieldName.substr(0, fieldName.lastIndexOf('.')),
+ idField = searchMeta.parseExpr(idFieldName).args[0].field;
+ if (!ctrl.canAggregate(idFieldName)) {
+ var joinEntity = searchMeta.getEntity(idField.fk_entity),
+ label = (idField.join ? idField.join.label + ': ' : '') + (idField.input_attrs && idField.input_attrs.label || idField.label);
+ _.each(_.cloneDeep(joinEntity && joinEntity.links), function(link) {
+ link.join = idFieldName;
+ addTitle(link, label);
+ links.push(link);
+ });
}
}
- });
- return links;
- };
+ }
+ });
+ return links;
+ };
- function loadAfforms() {
- ctrl.afforms = null;
- if (ctrl.afformEnabled && ctrl.savedSearch.id) {
- var findDisplays = _.transform(ctrl.savedSearch.displays, function(findDisplays, display) {
- if (display.id && display.name) {
- findDisplays.push(['search_displays', 'CONTAINS', ctrl.savedSearch.name + '.' + display.name]);
- }
- }, [['search_displays', 'CONTAINS', ctrl.savedSearch.name]]);
- afformLoad = crmApi4('Afform', 'get', {
- select: ['name', 'title', 'search_displays'],
- where: [['OR', findDisplays]]
- }).then(function(afforms) {
- ctrl.afforms = [];
- _.each(afforms, function(afform) {
- ctrl.afforms.push({
- title: afform.title,
- displays: afform.search_displays,
- link: ctrl.afformAdminEnabled ? CRM.url('civicrm/admin/afform#/edit/' + afform.name) : '',
- });
+ function loadAfforms() {
+ ctrl.afforms = null;
+ if (ctrl.afformEnabled && ctrl.savedSearch.id) {
+ var findDisplays = _.transform(ctrl.savedSearch.displays, function(findDisplays, display) {
+ if (display.id && display.name) {
+ findDisplays.push(['search_displays', 'CONTAINS', ctrl.savedSearch.name + '.' + display.name]);
+ }
+ }, [['search_displays', 'CONTAINS', ctrl.savedSearch.name]]);
+ afformLoad = crmApi4('Afform', 'get', {
+ select: ['name', 'title', 'search_displays'],
+ where: [['OR', findDisplays]]
+ }).then(function(afforms) {
+ ctrl.afforms = [];
+ _.each(afforms, function(afform) {
+ ctrl.afforms.push({
+ title: afform.title,
+ displays: afform.search_displays,
+ link: ctrl.afformAdminEnabled ? CRM.url('civicrm/admin/afform#/edit/' + afform.name) : '',
});
- ctrl.afformCount = ctrl.afforms.length;
});
- }
+ ctrl.afformCount = ctrl.afforms.length;
+ });
}
+ }
- // Creating an Afform opens a new tab, so when switching back after > 10 sec, re-check for Afforms
- $(window).on('focus', _.debounce(function() {
- $scope.$apply(loadAfforms);
- }, 10000, {leading: true, trailing: false}));
+ // Creating an Afform opens a new tab, so when switching back after > 10 sec, re-check for Afforms
+ $(window).on('focus', _.debounce(function() {
+ $scope.$apply(loadAfforms);
+ }, 10000, {leading: true, trailing: false}));
- }
+ };
+
+ ctrl.hook = hook;
+
+ angular.module('crmSearchAdmin').component('crmSearchAdmin', {
+ bindings: {
+ savedSearch: '<'
+ },
+ templateUrl: '~/crmSearchAdmin/crmSearchAdmin.html',
+ controller: ctrl
});
})(angular, CRM.$, CRM._);