savedSearch: '<'
},
templateUrl: '~/crmSearchAdmin/crmSearchAdmin.html',
- controller: function($scope, $element, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) {
+ controller: function($scope, $element, $location, $timeout, crmApi4, dialogService, searchMeta, formatForSelect2) {
var ts = $scope.ts = CRM.ts(),
ctrl = this;
$scope.controls = {tab: 'compose'};
$scope.joinTypes = [{k: false, v: ts('Optional')}, {k: true, v: ts('Required')}];
$scope.groupOptions = CRM.crmSearchActions.groupOptions;
- $scope.entities = formatForSelect2(CRM.vars.search.schema, 'name', 'title_plural', ['description', 'icon']);
+ // Try to create a sensible list of entities one might want to search for,
+ // excluding those whos primary purpose is to provide joins or option lists to other entities
+ var primaryEntities = _.filter(CRM.crmSearchAdmin.schema, function(entity) {
+ return !_.includes(entity.type, 'EntityBridge') && !_.includes(entity.type, 'OptionList');
+ });
+ $scope.entities = formatForSelect2(primaryEntities, 'name', 'title_plural', ['description', 'icon']);
this.perm = {
editGroups: CRM.checkPerm('edit groups')
};
this.savedSearch.groups = this.savedSearch.groups || [];
this.groupExists = !!this.savedSearch.groups.length;
- if (!this.savedSearch.api_params) {
- this.savedSearch.api_params = {
- version: 4,
- select: getDefaultSelect(),
- orderBy: {},
- where: [],
- };
+ if (!this.savedSearch.id) {
+ $scope.$bindToRoute({
+ param: 'params',
+ expr: '$ctrl.savedSearch.api_params',
+ deep: true,
+ default: {
+ version: 4,
+ select: getDefaultSelect(),
+ orderBy: {},
+ where: [],
+ }
+ });
}
$scope.$watchCollection('$ctrl.savedSearch.api_params.select', onChangeSelect);
delete params.displays;
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';
- ctrl.savedSearch.id = results.saved.id;
if (results.saved.groups && results.saved.groups.length) {
ctrl.savedSearch.groups[0].id = results.saved.groups[0].id;
}
}
};
+ 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]);
+ }, {});
+ }
+
+ $scope.getJoin = searchMeta.getJoin;
+
$scope.getJoinEntities = function() {
- var joinEntities = _.transform(CRM.vars.search.links[ctrl.savedSearch.api_entity], function(joinEntities, link) {
- var entity = searchMeta.getEntity(link.entity);
- if (entity) {
- joinEntities.push({
- id: link.entity + ' AS ' + link.alias,
- text: entity.title_plural,
- description: '(' + link.alias + ')',
- icon: entity.icon
- });
+ var existingJoins = getExistingJoins();
+
+ function addEntityJoins(entity, stack, baseEntity) {
+ return _.transform(CRM.crmSearchAdmin.joins[entity], function(joinEntities, join) {
+ var num = 0;
+ // Add all joins that don't just point directly back to the original entity
+ if (!(baseEntity === join.entity && !join.multi)) {
+ do {
+ appendJoin(joinEntities, join, ++num, stack, entity);
+ } while (addNum((stack ? stack + '_' : '') + join.alias, num) in existingJoins);
+ }
+ }, []);
+ }
+
+ 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, (stack ? stack + '_' : '') + alias, baseEntity);
}
- }, []);
- return {results: joinEntities};
+ collection.push(opt);
+ }
+
+ return {results: addEntityJoins(ctrl.savedSearch.api_entity)};
};
$scope.addJoin = function() {
$timeout(function() {
if ($scope.controls.join) {
ctrl.savedSearch.api_params.join = ctrl.savedSearch.api_params.join || [];
- ctrl.savedSearch.api_params.join.push([$scope.controls.join, false]);
+ var join = searchMeta.getJoin($scope.controls.join),
+ params = [$scope.controls.join, false];
+ _.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);
loadFieldOptions();
}
$scope.controls.join = '';
});
};
- $scope.changeJoin = function(idx) {
- if (ctrl.savedSearch.api_params.join[idx][0]) {
- ctrl.savedSearch.api_params.join[idx].length = 2;
- loadFieldOptions();
- } else {
- ctrl.clearParam('join', idx);
- }
- };
-
$scope.changeGroupBy = function(idx) {
if (!ctrl.savedSearch.api_params.groupBy[idx]) {
ctrl.clearParam('groupBy', idx);
*/
$scope.setOrderBy = function(col, $event) {
var dir = $scope.getOrderBy(col) === 'fa-sort-asc' ? 'DESC' : 'ASC';
- if (!$event.shiftKey) {
+ if (!$event.shiftKey || !ctrl.savedSearch.api_params.orderBy) {
ctrl.savedSearch.api_params.orderBy = {};
}
ctrl.savedSearch.api_params.orderBy[col] = dir;
if (ctrl.savedSearch.api_params.groupBy.length) {
_.each(ctrl.savedSearch.api_params.select, function(col, pos) {
if (!_.contains(col, '(') && ctrl.canAggregate(col)) {
- ctrl.savedSearch.api_params.select[pos] = ctrl.DEFAULT_AGGREGATE_FN + '(' + col + ')';
+ ctrl.savedSearch.api_params.select[pos] = ctrl.DEFAULT_AGGREGATE_FN + '(DISTINCT ' + col + ')';
}
});
}
return ctrl.allRowsSelected || _.includes(ctrl.selectedRows, row.id);
};
- this.getFieldLabel = function(col) {
- var info = searchMeta.parseExpr(col),
- label = info.field.label;
- if (info.fn) {
- label = '(' + info.fn.title + ') ' + label;
- }
- return label;
- };
+ this.getFieldLabel = searchMeta.getDefaultLabel;
// Is a column eligible to use an aggregate function?
this.canAggregate = function(col) {
$scope.formatResult = function(row, col) {
var info = searchMeta.parseExpr(col),
- key = info.fn ? (info.fn.name + ':' + info.path + info.suffix) : col,
- value = row[key];
+ value = row[info.alias];
if (info.fn && info.fn.name === 'COUNT') {
return value;
}
}
function getAllFields(suffix, disabledIf) {
- function formatFields(entityName, prefix) {
- return _.transform(searchMeta.getEntity(entityName).fields, function(result, field) {
- var item = {
- id: prefix + field.name + (field.options ? suffix : ''),
- text: field.label,
- description: field.description
- };
- if (disabledIf(item.id)) {
- item.disabled = true;
- }
- result.push(item);
- }, []);
+ function formatFields(entityName, join) {
+ var prefix = join ? join.alias + '.' : '',
+ result = [];
+
+ function addFields(fields) {
+ _.each(fields, function(field) {
+ var item = {
+ id: prefix + field.name + (field.options ? suffix : ''),
+ text: field.label,
+ description: field.description
+ };
+ if (disabledIf(item.id)) {
+ item.disabled = true;
+ }
+ result.push(item);
+ });
+ }
+
+ // Add extra searchable fields from bridge entity
+ if (join && join.bridge) {
+ addFields(_.filter(searchMeta.getEntity(join.bridge).fields, function(field) {
+ return (field.name !== 'id' && field.name !== 'entity_id' && field.name !== 'entity_table' && !field.fk_entity);
+ }));
+ }
+
+ addFields(searchMeta.getEntity(entityName).fields);
+ return result;
}
var mainEntity = searchMeta.getEntity(ctrl.savedSearch.api_entity),
result = [{
text: mainEntity.title_plural,
icon: mainEntity.icon,
- children: formatFields(ctrl.savedSearch.api_entity, '')
+ children: formatFields(ctrl.savedSearch.api_entity)
}];
_.each(ctrl.savedSearch.api_params.join, function(join) {
- var joinName = join[0].split(' AS '),
- joinEntity = searchMeta.getEntity(joinName[0]);
+ var joinInfo = searchMeta.getJoin(join[0]),
+ joinEntity = searchMeta.getEntity(joinInfo.entity);
result.push({
- text: joinEntity.title_plural + ' (' + joinName[1] + ')',
+ text: joinInfo.label,
+ description: joinInfo.description,
icon: joinEntity.icon,
- children: formatFields(joinEntity.name, joinName[1] + '.')
+ children: formatFields(joinEntity.name, joinInfo)
});
});
return result;
enqueue(mainEntity);
}
_.each(ctrl.savedSearch.api_params.join, function(join) {
- var joinName = join[0].split(' AS '),
- joinEntity = searchMeta.getEntity(joinName[0]);
+ var joinInfo = searchMeta.getJoin(join[0]),
+ joinEntity = searchMeta.getEntity(joinInfo.entity),
+ bridgeEntity = joinInfo.bridge ? searchMeta.getEntity(joinInfo.bridge) : null;
if (typeof joinEntity.optionsLoaded === 'undefined') {
enqueue(joinEntity);
}
+ if (bridgeEntity && typeof bridgeEntity.optionsLoaded === 'undefined') {
+ enqueue(bridgeEntity);
+ }
});
if (!_.isEmpty(entities)) {
crmApi4(entities).then(function(results) {