});
});
- angular.module('api4Explorer').controller('Api4Explorer', function($scope, $routeParams, $location, $timeout, $http, crmUiHelp, crmApi4) {
+ angular.module('api4Explorer').controller('Api4Explorer', function($scope, $routeParams, $location, $timeout, $http, crmUiHelp, crmApi4, dialogService) {
var ts = $scope.ts = CRM.ts();
$scope.entities = entities;
$scope.actions = actions;
$scope.fields = [];
+ $scope.havingOptions = [];
$scope.fieldsAndJoins = [];
+ $scope.fieldsAndJoinsAndFunctions = [];
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards = [];
$scope.availableParams = {};
$scope.params = {};
$scope.index = '';
+ $scope.selectedTab = {result: 'result', code: 'php'};
+ $scope.perm = {
+ accessDebugOutput: CRM.checkPerm('access debug output'),
+ editGroups: CRM.checkPerm('edit groups')
+ };
+ marked.setOptions({highlight: prettyPrintOne});
var getMetaParams = {},
- objectParams = {orderBy: 'ASC', values: '', chain: ['Entity', '', '{}']},
+ objectParams = {orderBy: 'ASC', values: '', defaults: '', chain: ['Entity', '', '{}']},
docs = CRM.vars.api4.docs,
helpTitle = '',
helpContent = {};
$scope.helpContent = {};
$scope.entity = $routeParams.api4entity;
$scope.result = [];
+ $scope.debug = null;
$scope.status = 'default';
$scope.loading = false;
$scope.controls = {};
- $scope.codeLabel = {
- oop: ts('PHP (oop style)'),
- php: ts('PHP (traditional)'),
- js: ts('Javascript'),
- cli: ts('Command Line')
+ $scope.langs = ['php', 'js', 'ang', 'cli'];
+ $scope.code = {
+ php: [
+ {name: 'oop', label: ts('OOP Style'), code: ''},
+ {name: 'php', label: ts('Traditional'), code: ''}
+ ],
+ js: [
+ {name: 'js', label: ts('Single Call'), code: ''},
+ {name: 'js2', label: ts('Batch Calls'), code: ''}
+ ],
+ ang: [
+ {name: 'ang', label: ts('Single Call'), code: ''},
+ {name: 'ang2', label: ts('Batch Calls'), code: ''}
+ ],
+ cli: [
+ {name: 'cv', label: ts('CV'), code: ''}
+ ]
};
- $scope.code = codeDefaults();
-
- function codeDefaults() {
- return _.mapValues($scope.codeLabel, function(val, key) {
- return key === 'oop' ? ts('Select an entity and action') : '';
- });
- }
if (!entities.length) {
formatForSelect2(schema, entities, 'name', ['description']);
}
}
- // Turn a flat array into a select2 array
- function arrayToSelect2(array) {
- var out = [];
- _.each(array, function(item) {
- out.push({id: item, text: item});
- });
- return out;
- }
-
// Reformat an existing array of objects for compatibility with select2
function formatForSelect2(input, container, key, extra, prefix) {
_.each(input, function(item) {
return fields;
}
- function addJoins(fieldList) {
+ function addJoins(fieldList, addWildcard) {
var fields = _.cloneDeep(fieldList),
fks = _.findWhere(links, {entity: $scope.entity}) || {};
_.each(fks.links, function(link) {
- var linkFields = entityFields(link.entity);
+ var linkFields = _.cloneDeep(entityFields(link.entity)),
+ wildCard = addWildcard ? [{id: link.alias + '.*', text: link.alias + '.*', 'description': 'All core ' + link.entity + ' fields'}] : [];
if (linkFields) {
fields.push({
text: link.alias,
description: 'Join to ' + link.entity,
- children: formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.')
+ children: wildCard.concat(formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.'))
});
}
});
return fields;
}
- $scope.help = function(title, param) {
- if (!param) {
+ $scope.help = function(title, content) {
+ if (!content) {
$scope.helpTitle = helpTitle;
$scope.helpContent = helpContent;
} else {
$scope.helpTitle = title;
- $scope.helpContent = param;
+ $scope.helpContent = formatHelp(content);
}
};
+ // Sets the static help text (which gets overridden by mousing over other elements)
+ function setHelp(title, content) {
+ $scope.helpTitle = helpTitle = title;
+ $scope.helpContent = helpContent = formatHelp(content);
+ }
+
+ // Convert plain-text help to markdown; replace variables and format links
+ function formatHelp(rawContent) {
+ function formatRefs(see) {
+ _.each(see, function(ref, idx) {
+ var match = ref.match(/^\\Civi\\Api4\\([a-zA-Z]+)$/);
+ if (match) {
+ ref = '#/explorer/' + match[1];
+ }
+ if (ref[0] === '\\') {
+ ref = 'https://github.com/civicrm/civicrm-core/blob/master' + ref.replace(/\\/i, '/') + '.php';
+ }
+ see[idx] = '<a target="' + (ref[0] === '#' ? '_self' : '_blank') + '" href="' + ref + '">' + see[idx] + '</a>';
+ });
+ }
+ var formatted = _.cloneDeep(rawContent);
+ if (formatted.description) {
+ formatted.description = marked(formatted.description);
+ }
+ if (formatted.comment) {
+ formatted.comment = marked(formatted.comment);
+ }
+ formatRefs(formatted.see);
+ return formatted;
+ }
+
$scope.fieldHelp = function(fieldName) {
var field = getField(fieldName, $scope.entity, $scope.action);
if (!field) {
return info;
};
- $scope.valuesFields = function() {
- var fields = _.cloneDeep($scope.action === 'getFields' ? getFieldList($scope.params.action || 'get') : $scope.fields);
- // Disable fields that are already in use
- _.each($scope.params.values || [], function(val) {
- (_.findWhere(fields, {id: val[0]}) || {}).disabled = true;
- });
- return {results: fields};
+ $scope.fieldList = function(param) {
+ return function() {
+ var fields = _.cloneDeep($scope.action === 'getFields' ? getFieldList($scope.params.action || 'get') : $scope.fields);
+ // Disable fields that are already in use
+ _.each($scope.params[param] || [], function(val) {
+ (_.findWhere(fields, {id: val[0]}) || {}).disabled = true;
+ });
+ return {results: fields};
+ };
};
$scope.formatSelect2Item = function(row) {
(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) {
- var specialParams = ['select', 'fields', 'action', 'where', 'values', 'orderBy', 'chain'];
+ var specialParams = ['select', 'fields', 'action', 'where', 'values', 'defaults', 'orderBy', 'chain', 'groupBy', 'having'];
+ if ($scope.availableParams.limit && $scope.availableParams.offset) {
+ specialParams.push('limit', 'offset');
+ }
return _.contains(specialParams, name);
};
};
$scope.isSelectRowCount = function() {
- return $scope.params && $scope.params.select && $scope.params.select.length === 1 && $scope.params.select[0] === 'row_count';
+ return isSelectRowCount($scope.params);
+ };
+
+ $scope.selectLang = function(lang) {
+ $scope.selectedTab.code = lang;
+ writeCode();
};
+ function isSelectRowCount(params) {
+ return params && params.select && params.select.length === 1 && params.select[0] === 'row_count';
+ }
+
function getEntity(entityName) {
return _.findWhere(schema, {name: entityName || $scope.entity});
}
function selectAction() {
$scope.action = $routeParams.api4action;
- $scope.fieldsAndJoins = [];
+ $scope.fieldsAndJoins.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);
+ 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.fieldsAndJoinsAndFunctions = $scope.fields;
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards = _.cloneDeep($scope.fields);
}
+ $scope.fieldsAndJoinsAndFunctionsAndWildcards.unshift({id: '*', text: '*', 'description': 'All core ' + $scope.entity + ' fields'});
_.each(actionInfo.params, function (param, name) {
var format,
defaultVal = _.cloneDeep(param.default);
default:
format = 'raw';
}
- if (name == 'limit') {
+ if (name === 'limit') {
defaultVal = 25;
}
+ if (name === 'debug') {
+ defaultVal = true;
+ }
if (name === 'values') {
defaultVal = defaultValues(defaultVal);
}
deep: format === 'json'
});
}
- if (typeof objectParams[name] !== 'undefined') {
- $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 (name === 'select' && actionInfo.params.having) {
+ $scope.$watchCollection('params.select', function(values) {
+ $scope.havingOptions.length = 0;
+ _.each(values, function(item) {
+ var pieces = item.split(' AS '),
+ alias = _.trim(pieces[pieces.length - 1]);
+ $scope.havingOptions.push({id: alias, text: alias});
+ });
+ });
+ }
+ if (typeof objectParams[name] !== 'undefined' || name === 'groupBy' || name === 'select') {
$scope.$watch('controls.' + name, function(value) {
var field = value;
$timeout(function() {
if (field) {
- var defaultOp = _.cloneDeep(objectParams[name]);
- if (name === 'chain') {
- var num = $scope.params.chain.length;
- defaultOp[0] = field;
- field = 'name_me_' + num;
+ if (typeof objectParams[name] === 'undefined') {
+ $scope.params[name].push(field);
+ } else {
+ var defaultOp = _.cloneDeep(objectParams[name]);
+ if (name === 'chain') {
+ var num = $scope.params.chain.length;
+ defaultOp[0] = field;
+ field = 'name_me_' + num;
+ }
+ $scope.params[name].push([field, defaultOp]);
}
- $scope.params[name].push([field, defaultOp]);
$scope.controls[name] = null;
}
});
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) {
}
function writeCode() {
- var code = codeDefaults(),
+ var code = {},
entity = $scope.entity,
action = $scope.action,
params = getParams(),
index = isInt($scope.index) ? +$scope.index : parseYaml($scope.index),
result = 'result';
if ($scope.entity && $scope.action) {
+ delete params.debug;
if (action.slice(0, 3) === 'get') {
result = entity.substr(0, 7) === 'Custom_' ? _.camelCase(entity.substr(7)) : entity;
result = lcfirst(action.replace(/s$/, '').slice(3) || result);
}
var results = lcfirst(_.isNumber(index) ? result : pluralize(result)),
paramCount = _.size(params),
- isSelectRowCount = params.select && params.select.length === 1 && params.select[0] === 'row_count',
i = 0;
- if (isSelectRowCount) {
+ if (isSelectRowCount(params)) {
results = result + 'Count';
}
- // Write javascript
- code.js = "CRM.api4('" + entity + "', '" + action + "', {";
- _.each(params, function(param, key) {
- code.js += "\n " + key + ': ' + stringify(param) +
- (++i < paramCount ? ',' : '');
- if (key === 'checkPermissions') {
- code.js += ' // IGNORED: permissions are always enforced from client-side requests';
- }
- });
- code.js += "\n}";
- if (index || index === 0) {
- code.js += ', ' + JSON.stringify(index);
- }
- code.js += ").then(function(" + results + ") {\n // do something with " + results + " array\n}, function(failure) {\n // handle failure\n});";
-
- // Write php code
- code.php = '$' + results + " = civicrm_api4('" + entity + "', '" + action + "', [";
- _.each(params, function(param, key) {
- code.php += "\n '" + key + "' => " + phpFormat(param, 4) + ',';
- });
- code.php += "\n]";
- if (index || index === 0) {
- code.php += ', ' + phpFormat(index);
- }
- code.php += ");";
-
- // Write oop code
- if (entity.substr(0, 7) !== 'Custom_') {
- code.oop = '$' + results + " = \\Civi\\Api4\\" + entity + '::' + action + '()';
- } else {
- code.oop = '$' + results + " = \\Civi\\Api4\\CustomValue::" + action + "('" + entity.substr(7) + "')";
- }
- _.each(params, function(param, key) {
- var val = '';
- if (typeof objectParams[key] !== 'undefined' && key !== 'chain') {
- _.each(param, function(item, index) {
- val = phpFormat(index) + ', ' + phpFormat(item, 4);
- code.oop += "\n ->add" + ucfirst(key).replace(/s$/, '') + '(' + val + ')';
- });
- } else if (key === 'where') {
- _.each(param, function (clause) {
- if (clause[0] === 'AND' || clause[0] === 'OR' || clause[0] === 'NOT') {
- code.oop += "\n ->addClause(" + phpFormat(clause[0]) + ", " + phpFormat(clause[1]).slice(1, -1) + ')';
- } else {
- code.oop += "\n ->addWhere(" + phpFormat(clause).slice(1, -1) + ")";
+ switch ($scope.selectedTab.code) {
+ case 'js':
+ case 'ang':
+ // Write javascript
+ var js = "'" + entity + "', '" + action + "', {";
+ _.each(params, function(param, key) {
+ js += "\n " + key + ': ' + stringify(param) +
+ (++i < paramCount ? ',' : '');
+ if (key === 'checkPermissions') {
+ js += ' // IGNORED: permissions are always enforced from client-side requests';
}
});
- } else if (key === 'select' && isSelectRowCount) {
- code.oop += "\n ->selectRowCount()";
- } else {
- code.oop += "\n ->set" + ucfirst(key) + '(' + phpFormat(param, 4) + ')';
- }
- });
- code.oop += "\n ->execute()";
- if (isSelectRowCount) {
- code.oop += "\n ->count()";
- } else if (_.isNumber(index)) {
- code.oop += !index ? '\n ->first()' : (index === -1 ? '\n ->last()' : '\n ->itemAt(' + index + ')');
- } else if (index) {
- if (_.isString(index) || (_.isPlainObject(index) && !index[0] && !index['0'])) {
- code.oop += "\n ->indexBy('" + (_.isPlainObject(index) ? _.keys(index)[0] : index) + "')";
- }
- if (_.isArray(index) || _.isPlainObject(index)) {
- code.oop += "\n ->column('" + (_.isArray(index) ? index[0] : _.values(index)[0]) + "')";
- }
- }
- code.oop += ";\n";
- if (!_.isNumber(index) && !isSelectRowCount) {
- code.oop += "foreach ($" + results + ' as $' + ((_.isString(index) && index) ? index + ' => $' : '') + result + ') {\n // do something\n}';
+ js += "\n}";
+ if (index || index === 0) {
+ js += ', ' + JSON.stringify(index);
+ }
+ code.js = "CRM.api4(" + js + ").then(function(" + results + ") {\n // do something with " + results + " array\n}, function(failure) {\n // handle failure\n});";
+ code.js2 = "CRM.api4({" + results + ': [' + js + "]}).then(function(batch) {\n // do something with batch." + results + " array\n}, function(failure) {\n // handle failure\n});";
+ code.ang = "crmApi4(" + js + ").then(function(" + results + ") {\n // do something with " + results + " array\n}, function(failure) {\n // handle failure\n});";
+ code.ang2 = "crmApi4({" + results + ': [' + js + "]}).then(function(batch) {\n // do something with batch." + results + " array\n}, function(failure) {\n // handle failure\n});";
+ break;
+
+ case 'php':
+ // Write php code
+ code.php = '$' + results + " = civicrm_api4('" + entity + "', '" + action + "', [";
+ _.each(params, function(param, key) {
+ code.php += "\n '" + key + "' => " + phpFormat(param, 4) + ',';
+ });
+ code.php += "\n]";
+ if (index || index === 0) {
+ code.php += ', ' + phpFormat(index);
+ }
+ code.php += ");";
+
+ // Write oop code
+ code.oop = '$' + results + " = " + formatOOP(entity, action, params, 2) + "\n ->execute()";
+ if (isSelectRowCount(params)) {
+ code.oop += "\n ->count()";
+ } else if (_.isNumber(index)) {
+ code.oop += !index ? '\n ->first()' : (index === -1 ? '\n ->last()' : '\n ->itemAt(' + index + ')');
+ } else if (index) {
+ if (_.isString(index) || (_.isPlainObject(index) && !index[0] && !index['0'])) {
+ code.oop += "\n ->indexBy('" + (_.isPlainObject(index) ? _.keys(index)[0] : index) + "')";
+ }
+ if (_.isArray(index) || _.isPlainObject(index)) {
+ code.oop += "\n ->column('" + (_.isArray(index) ? index[0] : _.values(index)[0]) + "')";
+ }
+ }
+ code.oop += ";\n";
+ if (!_.isNumber(index) && !isSelectRowCount(params)) {
+ code.oop += "foreach ($" + results + ' as $' + ((_.isString(index) && index) ? index + ' => $' : '') + result + ') {\n // do something\n}';
+ }
+ break;
+
+ case 'cli':
+ // Write cli code
+ code.cv = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'";
}
+ }
+ _.each($scope.code, function(vals) {
+ _.each(vals, function(style) {
+ style.code = code[style.name] ? prettyPrintOne(code[style.name]) : '';
+ });
+ });
+ }
- // Write cli code
- code.cli = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'";
+ // Format oop params
+ function formatOOP(entity, action, params, indent) {
+ var code = '',
+ newLine = "\n" + _.repeat(' ', indent);
+ if (entity.substr(0, 7) !== 'Custom_') {
+ code = "\\Civi\\Api4\\" + entity + '::' + action + '()';
+ } else {
+ code = "\\Civi\\Api4\\CustomValue::" + action + "('" + entity.substr(7) + "')";
}
- _.each(code, function(val, type) {
- $scope.code[type] = prettyPrintOne(_.escape(val));
+ _.each(params, function(param, key) {
+ var val = '';
+ if (typeof objectParams[key] !== 'undefined' && key !== 'chain') {
+ _.each(param, function(item, index) {
+ val = phpFormat(index) + ', ' + phpFormat(item, 2 + indent);
+ code += newLine + "->add" + ucfirst(key).replace(/s$/, '') + '(' + val + ')';
+ });
+ } else if (key === 'where') {
+ _.each(param, function (clause) {
+ if (clause[0] === 'AND' || clause[0] === 'OR' || clause[0] === 'NOT') {
+ code += newLine + "->addClause(" + phpFormat(clause[0]) + ", " + phpFormat(clause[1]).slice(1, -1) + ')';
+ } else {
+ code += newLine + "->addWhere(" + phpFormat(clause).slice(1, -1) + ")";
+ }
+ });
+ } else if (key === 'select') {
+ code += newLine;
+ // addSelect() is a variadic function & can take multiple arguments; selectRowCount() is a shortcut for addSelect('row_count')
+ code += isSelectRowCount(params) ? '->selectRowCount()' : '->addSelect(' + phpFormat(param).slice(1, -1) + ')';
+ } else if (key === 'chain') {
+ _.each(param, function(chain, name) {
+ code += newLine + "->addChain('" + name + "', " + formatOOP(chain[0], chain[1], chain[2], 2 + indent);
+ code += (chain.length > 3 ? ',' : '') + (!_.isEmpty(chain[2]) ? newLine : ' ') + (chain.length > 3 ? phpFormat(chain[3]) : '') + ')';
+ });
+ }
+ else {
+ code += newLine + "->set" + ucfirst(key) + '(' + phpFormat(param, 2 + indent) + ')';
+ }
});
+ return code;
}
function isInt(value) {
}).then(function(resp) {
$scope.loading = false;
$scope.status = 'success';
+ $scope.debug = debugFormat(resp.data);
$scope.result = [formatMeta(resp.data), prettyPrintOne(_.escape(JSON.stringify(resp.data.values, null, 2)), 'js', 1)];
}, function(resp) {
$scope.loading = false;
$scope.status = 'danger';
+ $scope.debug = debugFormat(resp.data);
$scope.result = [formatMeta(resp), prettyPrintOne(_.escape(JSON.stringify(resp.data, null, 2)))];
});
};
+ function debugFormat(data) {
+ var debug = data.debug ? prettyPrintOne(_.escape(JSON.stringify(data.debug, null, 2)).replace(/\\n/g, "\n")) : null;
+ delete data.debug;
+ return debug;
+ }
+
/**
* Format value to look like php code
*/
// Help for an entity with no action selected
function showEntityHelp(entityName) {
var entityInfo = getEntity(entityName);
- $scope.helpTitle = helpTitle = $scope.entity;
- $scope.helpContent = helpContent = {
+ setHelp($scope.entity, {
description: entityInfo.description,
- comment: entityInfo.comment
- };
+ comment: entityInfo.comment,
+ see: entityInfo.see
+ });
}
if (!$scope.entity) {
- $scope.helpTitle = helpTitle = ts('APIv4 Explorer');
- $scope.helpContent = helpContent = {description: docs.description, comment: docs.comment};
+ setHelp(ts('APIv4 Explorer'), {description: docs.description, comment: docs.comment, see: docs.see});
} else if (!actions.length && !getEntity().actions) {
getMetaParams.actions = [$scope.entity, 'getActions', {chain: {fields: [$scope.entity, 'getFields', {action: '$name'}]}}];
fetchMeta();
if ($scope.entity && $routeParams.api4action !== newVal && !_.isUndefined(newVal)) {
$location.url('/explorer/' + $scope.entity + '/' + newVal);
} else if (newVal) {
- $scope.helpTitle = helpTitle = $scope.entity + '::' + newVal;
- $scope.helpContent = helpContent = _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment']);
+ setHelp($scope.entity + '::' + newVal, _.pick(_.findWhere(getEntity().actions, {name: newVal}), ['description', 'comment', 'see']));
}
});
return docs.params[name];
};
+ $scope.executeDoc = function() {
+ var doc = {
+ description: ts('Runs API call on the CiviCRM database.'),
+ comment: ts('Results and debugging info will be displayed below.')
+ };
+ if ($scope.action === 'delete') {
+ doc.WARNING = ts('This API call will be executed on the real database. Deleting data cannot be undone.');
+ }
+ else if ($scope.action && $scope.action.slice(0, 3) !== 'get') {
+ doc.WARNING = ts('This API call will be executed on the real database. It cannot be undone.');
+ }
+ return doc;
+ };
+
+ $scope.saveDoc = function() {
+ return {
+ description: ts('Save API call as a smart group.'),
+ comment: ts('Allows you to create a SavedSearch containing the WHERE clause of this API call.'),
+ };
+ };
+
$scope.$watch('params', writeCode, true);
$scope.$watch('index', writeCode);
writeCode();
+ $scope.save = function() {
+ var model = {
+ title: '',
+ description: '',
+ visibility: 'User and User Admin Only',
+ group_type: [],
+ id: null,
+ entity: $scope.entity,
+ params: JSON.parse(angular.toJson($scope.params))
+ };
+ model.params.version = 4;
+ delete model.params.select;
+ delete model.params.chain;
+ delete model.params.debug;
+ delete model.params.limit;
+ delete model.params.checkPermissions;
+ var options = CRM.utils.adjustDialogDefaults({
+ width: '500px',
+ autoOpen: false,
+ title: ts('Save smart group')
+ });
+ dialogService.open('saveSearchDialog', '~/api4Explorer/SaveSearch.html', model, options);
+ };
+ });
+
+ angular.module('api4Explorer').controller('SaveSearchCtrl', function($scope, crmApi4, dialogService) {
+ var ts = $scope.ts = CRM.ts(),
+ model = $scope.model;
+ $scope.groupEntityRefParams = {
+ entity: 'Group',
+ api: {
+ params: {is_hidden: 0, is_active: 1, 'saved_search_id.api_entity': model.entity},
+ extra: ['saved_search_id', 'description', 'visibility', 'group_type']
+ },
+ select: {
+ allowClear: true,
+ minimumInputLength: 0,
+ placeholder: ts('Select existing group')
+ }
+ };
+ if (!CRM.checkPerm('administer reserved groups')) {
+ $scope.groupEntityRefParams.api.params.is_reserved = 0;
+ }
+ $scope.perm = {
+ administerReservedGroups: CRM.checkPerm('administer reserved groups')
+ };
+ $scope.options = CRM.vars.api4.groupOptions;
+ $scope.$watch('model.id', function(id) {
+ if (id) {
+ _.assign(model, $('#api-save-search-select-group').select2('data').extra);
+ }
+ });
+ $scope.cancel = function() {
+ dialogService.cancel('saveSearchDialog');
+ };
+ $scope.save = function() {
+ $('.ui-dialog:visible').block();
+ var group = model.id ? {id: model.id} : {title: model.title};
+ group.description = model.description;
+ group.visibility = model.visibility;
+ group.group_type = model.group_type;
+ group.saved_search_id = '$id';
+ var savedSearch = {
+ api_entity: model.entity,
+ api_params: model.params
+ };
+ if (group.id) {
+ savedSearch.id = model.saved_search_id;
+ }
+ crmApi4('SavedSearch', 'save', {records: [savedSearch], chain: {group: ['Group', 'save', {'records': [group]}]}})
+ .then(function(result) {
+ dialogService.close('saveSearchDialog', result[0]);
+ });
+ };
});
- angular.module('api4Explorer').directive('crmApi4WhereClause', function($timeout) {
+ angular.module('api4Explorer').directive('crmApi4Clause', function($timeout) {
return {
scope: {
- data: '=crmApi4WhereClause'
+ data: '=crmApi4Clause'
},
- templateUrl: '~/api4Explorer/WhereClause.html',
+ templateUrl: '~/api4Explorer/Clause.html',
link: function (scope, element, attrs) {
var ts = scope.ts = CRM.ts();
scope.newClause = '';
scope.operators = CRM.vars.api4.operators;
scope.addGroup = function(op) {
- scope.data.where.push([op, []]);
+ scope.data.clauses.push([op, []]);
};
scope.removeGroup = function() {
};
scope.onSort = function(event, ui) {
- $('.api4-where-fieldset').toggleClass('api4-sorting', event.type === 'sortstart');
+ $(element).closest('.api4-clause-fieldset').toggleClass('api4-sorting', event.type === 'sortstart');
$('.api4-input.form-inline').css('margin-left', '');
};
var field = value;
$timeout(function() {
if (field) {
- scope.data.where.push([field, '=', '']);
+ scope.data.clauses.push([field, '=', '']);
scope.newClause = null;
}
});
});
- scope.$watch('data.where', function(values) {
+ scope.$watch('data.clauses', function(values) {
// Remove empty values
_.each(values, function(clause, index) {
if (typeof clause !== 'undefined' && !clause[0]) {
});
} else if (dataType === 'Boolean') {
$el.attr('placeholder', ts('- select -')).crmSelect2({allowClear: false, multiple: multi, placeholder: ts('- select -'), data: [
- {id: '1', text: ts('Yes')},
- {id: '0', text: ts('No')}
+ {id: 'true', text: ts('Yes')},
+ {id: 'false', text: ts('No')}
]});
}
} else if (dataType === 'Integer' && !multi) {