<fieldset class="api4-input form-inline" ng-mouseenter="help('select', availableParams.select)" ng-mouseleave="help()" ng-if="availableParams.select && !isSelectRowCount()">
<legend>select<span class="crm-marker" ng-if="availableParams.select.required"> *</span></legend>
<div ng-model="params.select" ui-sortable="{axis: 'y'}">
- <div class="api4-input form-inline" ng-repeat="item in params.select">
+ <div class="api4-input form-inline" ng-repeat="item in params.select track by $index">
<i class="crm-i fa-arrows"></i>
- <input class="collapsible-optgroups form-control" ng-model="item" crm-ui-select="{data: selectFieldsAndJoins, allowClear: true, placeholder: 'Field'}" />
+ <input class="form-control huge" type="text" ng-model="params.select[$index]" />
+ <a href class="crm-hover-button" title="Clear" ng-click="clearParam('select', $index)"><i class="crm-i fa-times"></i></a>
</div>
</div>
<div class="api4-input form-inline">
- <input class="collapsible-optgroups form-control" ng-model="controls.select" crm-ui-select="{data: selectFieldsAndJoins}" placeholder="Add select" />
+ <input class="collapsible-optgroups form-control huge" ng-model="controls.select" crm-ui-select="{data: fieldsAndJoinsAndFunctionsAndWildcards}" placeholder="Add select" />
</div>
</fieldset>
<div class="api4-input form-inline" ng-mouseenter="help('fields', availableParams.fields)" ng-mouseleave="help()"ng-if="availableParams.fields">
<fieldset ng-if="availableParams.groupBy" ng-mouseenter="help('groupBy', availableParams.groupBy)" ng-mouseleave="help()">
<legend>groupBy<span class="crm-marker" ng-if="availableParams.groupBy.required"> *</span></legend>
<div ng-model="params.groupBy" ui-sortable="{axis: 'y'}">
- <div class="api4-input form-inline" ng-repeat="(pos, field) in params.groupBy">
+ <div class="api4-input form-inline" ng-repeat="item in params.groupBy track by $index">
<i class="crm-i fa-arrows"></i>
- <input class="collapsible-optgroups form-control" ng-model="params.groupBy[pos]" crm-ui-select="{data: fieldsAndJoins, allowClear: true, placeholder: 'Field'}" />
+ <input class="form-control huge" type="text" ng-model="params.groupBy[$index]" />
+ <a href class="crm-hover-button" title="Clear" ng-click="clearParam('groupBy', $index)"><i class="crm-i fa-times"></i></a>
</div>
</div>
<div class="api4-input form-inline">
- <input class="collapsible-optgroups form-control" ng-model="controls.groupBy" crm-ui-select="{data: fieldsAndJoins}" placeholder="Add groupBy" />
+ <input class="collapsible-optgroups form-control huge" ng-model="controls.groupBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add groupBy" />
</div>
</fieldset>
<fieldset ng-if="availableParams.orderBy" ng-mouseenter="help('orderBy', availableParams.orderBy)" ng-mouseleave="help()">
<div ng-model="params.orderBy" ui-sortable="{axis: 'y'}">
<div class="api4-input form-inline" ng-repeat="clause in params.orderBy">
<i class="crm-i fa-arrows"></i>
- <input class="collapsible-optgroups form-control" ng-model="clause[0]" crm-ui-select="{data: fieldsAndJoins, allowClear: true, placeholder: 'Field'}" />
+ <input class="form-control huge" type="text" ng-model="clause[0]" />
<select class="form-control" ng-model="clause[1]">
<option value="ASC">ASC</option>
<option value="DESC">DESC</option>
</select>
+ <a href class="crm-hover-button" title="Clear" ng-click="clearParam('orderBy', $index)"><i class="crm-i fa-times"></i></a>
</div>
</div>
<div class="api4-input form-inline">
- <input class="collapsible-optgroups form-control" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoins}" placeholder="Add orderBy" />
+ <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add orderBy" />
</div>
</fieldset>
<fieldset ng-if="availableParams.limit && availableParams.offset">
$scope.actions = actions;
$scope.fields = [];
$scope.fieldsAndJoins = [];
- $scope.selectFieldsAndJoins = [];
+ $scope.fieldsAndJoinsAndFunctions = [];
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards = [];
$scope.availableParams = {};
$scope.params = {};
$scope.index = '';
(row.description ? '<div class="crm-select2-row-description"><p>' + _.escape(row.description) + '</p></div>' : '');
};
- $scope.clearParam = function(name) {
- $scope.params[name] = $scope.availableParams[name].default;
+ $scope.clearParam = function(name, idx) {
+ if (typeof idx === 'undefined') {
+ $scope.params[name] = $scope.availableParams[name].default;
+ } else {
+ $scope.params[name].splice(idx, 1);
+ }
};
$scope.isSpecial = function(name) {
function selectAction() {
$scope.action = $routeParams.api4action;
$scope.fieldsAndJoins.length = 0;
- $scope.selectFieldsAndJoins.length = 0;
+ $scope.fieldsAndJoinsAndFunctions.length = 0;
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards.length = 0;
if (!actions.length) {
formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
}
$scope.fields = getFieldList($scope.action);
if (_.contains(['get', 'update', 'delete', 'replace'], $scope.action)) {
$scope.fieldsAndJoins = addJoins($scope.fields);
- $scope.selectFieldsAndJoins = addJoins($scope.fields, true);
+ var fieldsAndFunctions = _.cloneDeep($scope.fields);
+ // SQL functions are supported if HAVING is
+ if (actionInfo.params.having) {
+ fieldsAndFunctions.push({
+ text: ts('FUNCTION'),
+ description: ts('Calculate result of a SQL function'),
+ children: _.transform(CRM.vars.api4.functions, function(result, fn) {
+ result.push({
+ id: fn.name + '() AS ' + fn.name.toLowerCase(),
+ text: fn.name + '()',
+ description: fn.name + '(' + describeSqlFn(fn.params) + ')'
+ });
+ })
+ });
+ }
+ $scope.fieldsAndJoinsAndFunctions = addJoins(fieldsAndFunctions, true);
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards = addJoins(fieldsAndFunctions, true);
} else {
$scope.fieldsAndJoins = $scope.fields;
- $scope.selectFieldsAndJoins = _.cloneDeep($scope.fields);
+ $scope.fieldsAndJoinsAndFunctions = $scope.fields;
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards = _.cloneDeep($scope.fields);
}
- $scope.selectFieldsAndJoins.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
_.each(actionInfo.params, function (param, name) {
var format,
defaultVal = _.cloneDeep(param.default);
deep: format === 'json'
});
}
- if (typeof objectParams[name] !== 'undefined' || name === 'groupBy' || name === 'select') {
- $scope.$watch('params.' + name, function(values) {
+ if (typeof objectParams[name] !== 'undefined' && name !== 'orderBy') {
+ $scope.$watch('params.' + name, function (values) {
// Remove empty values
- _.each(values, function(clause, index) {
+ _.each(values, function (clause, index) {
if (!clause || !clause[0]) {
- $scope.params[name].splice(index, 1);
+ $scope.clearParam(name, index);
}
});
}, true);
+ }
+ if (typeof objectParams[name] !== 'undefined' || name === 'groupBy' || name === 'select') {
$scope.$watch('controls.' + name, function(value) {
var field = value;
$timeout(function() {
if (field) {
- if (name === 'groupBy' || name === 'select') {
+ if (typeof objectParams[name] === 'undefined') {
$scope.params[name].push(field);
} else {
var defaultOp = _.cloneDeep(objectParams[name]);
writeCode();
}
+ function describeSqlFn(params) {
+ var desc = ' ';
+ _.each(params, function(param) {
+ desc += ' ';
+ if (param.prefix) {
+ desc += _.filter(param.prefix).join('|') + ' ';
+ }
+ if (param.expr === 1) {
+ desc += 'expr ';
+ } else if (param.expr > 1) {
+ desc += 'expr, ... ';
+ }
+ if (param.suffix) {
+ desc += ' ' + _.filter(param.suffix).join('|') + ' ';
+ }
+ });
+ return desc.replace(/[ ]+/g, ' ');
+ }
+
function defaultValues(defaultVal) {
_.each($scope.fields, function(field) {
if (field.required) {