summaryrefslogtreecommitdiff
path: root/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js')
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js789
1 files changed, 789 insertions, 0 deletions
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js
new file mode 100644
index 00000000..10391793
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ext/api4/ang/api4Explorer/Explorer.js
@@ -0,0 +1,789 @@
+(function(angular, $, _, undefined) {
+
+ // Cache schema metadata
+ var schema = [];
+ // Cache fk schema data
+ var links = [];
+ // Cache list of entities
+ var entities = [];
+ // Cache list of actions
+ var actions = [];
+ // Field options
+ var fieldOptions = {};
+
+
+ angular.module('api4Explorer').config(function($routeProvider) {
+ $routeProvider.when('/explorer/:api4entity?/:api4action?', {
+ controller: 'Api4Explorer',
+ templateUrl: '~/api4Explorer/Explorer.html',
+ reloadOnSearch: false
+ });
+ });
+
+ angular.module('api4Explorer').controller('Api4Explorer', function($scope, $routeParams, $location, $timeout, crmUiHelp, crmApi4) {
+ var ts = $scope.ts = CRM.ts('api4');
+ $scope.entities = entities;
+ $scope.actions = actions;
+ $scope.fields = [];
+ $scope.fieldsAndJoins = [];
+ $scope.availableParams = {};
+ $scope.params = {};
+ $scope.index = '';
+ var getMetaParams = schema.length ? {} : {
+ schema: ['Entity', 'get', {chain: {fields: ['$name', 'getFields']}}],
+ links: ['Entity', 'getLinks']
+ },
+ objectParams = {orderBy: 'ASC', values: '', chain: ['Entity', '', '{}']},
+ helpTitle = '',
+ helpContent = {};
+ $scope.helpTitle = '';
+ $scope.helpContent = {};
+ $scope.entity = $routeParams.api4entity;
+ $scope.result = [];
+ $scope.status = 'default';
+ $scope.loading = false;
+ $scope.controls = {};
+ $scope.code = {
+ php: '',
+ javascript: '',
+ cli: ''
+ };
+
+ $scope.$bindToRoute({
+ expr: 'index',
+ param: 'index',
+ default: ''
+ });
+
+ function ucfirst(str) {
+ return str[0].toUpperCase() + str.slice(1);
+ }
+
+ function lcfirst(str) {
+ return str[0].toLowerCase() + str.slice(1);
+ }
+
+ function pluralize(str) {
+ switch (str[str.length-1]) {
+ case 's':
+ return str + 'es';
+ case 'y':
+ return str.slice(0, -1) + 'ies';
+ default:
+ return str + 's';
+ }
+ }
+
+ // 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) {
+ var id = (prefix || '') + item[key];
+ var formatted = {id: id, text: id};
+ if (extra) {
+ _.merge(formatted, _.pick(item, extra));
+ }
+ container.push(formatted);
+ });
+ return container;
+ }
+
+ function getFieldList(source) {
+ var fields = [],
+ fieldInfo = _.findWhere(getEntity().actions, {name: $scope.action}).fields;
+ formatForSelect2(fieldInfo, fields, 'name', ['description', 'required', 'default_value']);
+ return fields;
+ }
+
+ function addJoins(fieldList) {
+ var fields = _.cloneDeep(fieldList),
+ fks = _.findWhere(links, {entity: $scope.entity}) || {};
+ _.each(fks.links, function(link) {
+ var linkFields = entityFields(link.entity);
+ if (linkFields) {
+ fields.push({
+ text: link.alias,
+ description: 'Join to ' + link.entity,
+ children: formatForSelect2(linkFields, [], 'name', ['description'], link.alias + '.')
+ });
+ }
+ });
+ return fields;
+ }
+
+ $scope.help = function(title, param) {
+ if (!param) {
+ $scope.helpTitle = helpTitle;
+ $scope.helpContent = helpContent;
+ } else {
+ $scope.helpTitle = title;
+ $scope.helpContent = param;
+ }
+ };
+
+ $scope.valuesFields = function() {
+ var fields = _.cloneDeep($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.formatSelect2Item = function(row) {
+ return _.escape(row.text) +
+ (row.required ? '<span class="crm-marker"> *</span>' : '') +
+ (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.isSpecial = function(name) {
+ var specialParams = ['select', 'fields', 'action', 'where', 'values', 'orderBy', 'chain'];
+ return _.contains(specialParams, name);
+ };
+
+ function getEntity(entityName) {
+ return _.findWhere(schema, {name: entityName || $scope.entity});
+ }
+
+ // Get all params that have been set
+ function getParams() {
+ var params = {};
+ _.each($scope.params, function(param, key) {
+ if (param != $scope.availableParams[key].default && !(typeof param === 'object' && _.isEmpty(param))) {
+ if (_.contains($scope.availableParams[key].type, 'array') && (typeof objectParams[key] === 'undefined')) {
+ params[key] = parseYaml(_.cloneDeep(param));
+ } else {
+ params[key] = param;
+ }
+ }
+ });
+ _.each(objectParams, function(defaultVal, key) {
+ if (params[key]) {
+ var newParam = {};
+ _.each(params[key], function(item) {
+ newParam[item[0]] = parseYaml(_.cloneDeep(item[1]));
+ });
+ params[key] = newParam;
+ }
+ });
+ if (params.where) {
+ formatWhereClause(params.where);
+ }
+ return params;
+ }
+
+ // Coerce value to an array when the operator is IN or NOT IN
+ // Note this has already been passed through parseYaml once
+ function formatWhereClause(where) {
+ _.each(where, function(clause) {
+ if (_.isArray(clause)) {
+ if (clause.length === 3) {
+ if (_.contains(['IN', 'NOT IN'], clause[1]) && (_.isNumber(clause[2]) || (_.isString(clause[2]) && clause[2].length))) {
+ clause[2] = parseYaml('[' + clause[2] + ']');
+ }
+ } else {
+ formatWhereClause(clause);
+ }
+ }
+ });
+ }
+
+ function parseYaml(input) {
+ if (typeof input === 'undefined') {
+ return undefined;
+ }
+ if (_.isObject(input) || _.isArray(input)) {
+ _.each(input, function(item, index) {
+ input[index] = parseYaml(item);
+ });
+ return input;
+ }
+ try {
+ return input === '>' ? '>' : jsyaml.safeLoad(input);
+ } catch (e) {
+ return input;
+ }
+ }
+
+ function selectAction() {
+ $scope.action = $routeParams.api4action;
+ $scope.fieldsAndJoins = [];
+ formatForSelect2(getEntity().actions, actions, 'name', ['description', 'params']);
+ if ($scope.action) {
+ var actionInfo = _.findWhere(actions, {id: $scope.action});
+ $scope.fields = getFieldList();
+ if (_.contains(['get', 'update', 'delete', 'replace'], $scope.action)) {
+ $scope.fieldsAndJoins = addJoins($scope.fields);
+ } else {
+ $scope.fieldsAndJoins = $scope.fields;
+ }
+ _.each(actionInfo.params, function (param, name) {
+ var format,
+ defaultVal = _.cloneDeep(param.default);
+ if (param.type) {
+ switch (param.type[0]) {
+ case 'int':
+ case 'bool':
+ format = param.type[0];
+ break;
+
+ case 'array':
+ case 'object':
+ format = 'json';
+ break;
+
+ default:
+ format = 'raw';
+ }
+ if (name == 'limit') {
+ defaultVal = 25;
+ }
+ if (name === 'values') {
+ defaultVal = defaultValues(defaultVal);
+ }
+ $scope.$bindToRoute({
+ expr: 'params["' + name + '"]',
+ param: name,
+ format: format,
+ default: defaultVal,
+ deep: format === 'json'
+ });
+ }
+ if (typeof objectParams[name] !== 'undefined') {
+ $scope.$watch('params.' + name, function(values) {
+ // Remove empty values
+ _.each(values, function(clause, index) {
+ if (!clause || !clause[0]) {
+ $scope.params[name].splice(index, 1);
+ }
+ });
+ }, true);
+ $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;
+ }
+ $scope.params[name].push([field, defaultOp]);
+ $scope.controls[name] = null;
+ }
+ });
+ });
+ }
+ });
+ $scope.availableParams = actionInfo.params;
+ }
+ writeCode();
+ }
+
+ function defaultValues(defaultVal) {
+ _.each($scope.fields, function(field) {
+ if (field.required) {
+ defaultVal.push([field.id, '']);
+ }
+ });
+ return defaultVal;
+ }
+
+ function stringify(value, trim) {
+ if (typeof value === 'undefined') {
+ return '';
+ }
+ var str = JSON.stringify(value).replace(/,/g, ', ');
+ if (trim) {
+ str = str.slice(1, -1);
+ }
+ return str.trim();
+ }
+
+ function writeCode() {
+ var code = {
+ php: ts('Select an entity and action'),
+ javascript: '',
+ cli: ''
+ },
+ entity = $scope.entity,
+ action = $scope.action,
+ params = getParams(),
+ index = isInt($scope.index) ? +$scope.index : $scope.index,
+ result = 'result';
+ if ($scope.entity && $scope.action) {
+ 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),
+ i = 0;
+
+ // Write javascript
+ code.javascript = "CRM.api4('" + entity + "', '" + action + "', {";
+ _.each(params, function(param, key) {
+ code.javascript += "\n " + key + ': ' + stringify(param) +
+ (++i < paramCount ? ',' : '');
+ if (key === 'checkPermissions') {
+ code.javascript += ' // IGNORED: permissions are always enforced from client-side requests';
+ }
+ });
+ code.javascript += "\n}";
+ if (index || index === 0) {
+ code.javascript += ', ' + JSON.stringify(index);
+ }
+ code.javascript += ").then(function(" + results + ") {\n // do something with " + results + " array\n}, function(failure) {\n // handle failure\n});";
+
+ // Write php code
+ if (entity.substr(0, 7) !== 'Custom_') {
+ code.php = '$' + results + " = \\Civi\\Api4\\" + entity + '::' + action + '()';
+ } else {
+ code.php = '$' + 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.php += "\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.php += "\n ->addClause(" + phpFormat(clause[0]) + ", " + phpFormat(clause[1]).slice(1, -1) + ')';
+ } else {
+ code.php += "\n ->addWhere(" + phpFormat(clause).slice(1, -1) + ")";
+ }
+ });
+ } else {
+ code.php += "\n ->set" + ucfirst(key) + '(' + phpFormat(param, 4) + ')';
+ }
+ });
+ code.php += "\n ->execute()";
+ if (_.isNumber(index)) {
+ code.php += !index ? '\n ->first()' : (index === -1 ? '\n ->last()' : '\n ->itemAt(' + index + ')');
+ } else if (index) {
+ code.php += "\n ->indexBy('" + index + "')";
+ }
+ code.php += ";\n";
+ if (!_.isNumber(index)) {
+ code.php += "foreach ($" + results + ' as $' + ((_.isString(index) && index) ? index + ' => $' : '') + result + ') {\n // do something\n}';
+ }
+
+ // Write cli code
+ code.cli = 'cv api4 ' + entity + '.' + action + " '" + stringify(params) + "'";
+ }
+ $scope.code = code;
+ }
+
+ function isInt(value) {
+ if (_.isNumber(value)) {
+ return true;
+ }
+ if (!_.isString(value)) {
+ return false;
+ }
+ return /^-{0,1}\d+$/.test(value);
+ }
+
+ $scope.execute = function() {
+ $scope.status = 'warning';
+ $scope.loading = true;
+ crmApi4($scope.entity, $scope.action, getParams(), $scope.index)
+ .then(function(data) {
+ var meta = {length: _.size(data)},
+ result = JSON.stringify(data, null, 2);
+ if (_.isArray(data)) {
+ data.length = 0;
+ _.assign(meta, data);
+ }
+ $scope.loading = false;
+ $scope.status = 'success';
+ $scope.result = [JSON.stringify(meta).replace('{', '').replace(/}$/, ''), result];
+ }, function(data) {
+ $scope.loading = false;
+ $scope.status = 'danger';
+ $scope.result = [JSON.stringify(data, null, 2)];
+ });
+ };
+
+ /**
+ * Format value to look like php code
+ */
+ function phpFormat(val, indent) {
+ if (typeof val === 'undefined') {
+ return '';
+ }
+ indent = (typeof indent === 'number') ? _.repeat(' ', indent) : (indent || '');
+ var ret = '',
+ baseLine = indent ? indent.slice(0, -2) : '',
+ newLine = indent ? '\n' : '';
+ if ($.isPlainObject(val)) {
+ $.each(val, function(k, v) {
+ ret += (ret ? ', ' : '') + newLine + indent + "'" + k + "' => " + phpFormat(v);
+ });
+ return '[' + ret + newLine + baseLine + ']';
+ }
+ if ($.isArray(val)) {
+ $.each(val, function(k, v) {
+ ret += (ret ? ', ' : '') + newLine + indent + phpFormat(v);
+ });
+ return '[' + ret + newLine + baseLine + ']';
+ }
+ if (_.isString(val) && !_.contains(val, "'")) {
+ return "'" + val + "'";
+ }
+ return JSON.stringify(val).replace(/\$/g, '\\$');
+ }
+
+ function fetchMeta() {
+ crmApi4(getMetaParams)
+ .then(function(data) {
+ if (data.schema) {
+ schema = data.schema;
+ entities.length = 0;
+ formatForSelect2(schema, entities, 'name', ['description']);
+ if ($scope.entity && !$scope.action) {
+ showEntityHelp($scope.entity);
+ }
+ }
+ if (data.links) {
+ links = data.links;
+ }
+ if (data.actions) {
+ getEntity().actions = data.actions;
+ selectAction();
+ }
+ });
+ }
+
+ // Help for an entity with no action selected
+ function showEntityHelp(entityName) {
+ var entityInfo = getEntity(entityName);
+ $scope.helpTitle = helpTitle = $scope.entity;
+ $scope.helpContent = helpContent = {
+ description: entityInfo.description,
+ comment: entityInfo.comment
+ };
+ }
+
+ if (!$scope.entity) {
+ $scope.helpTitle = helpTitle = ts('Help');
+ $scope.helpContent = helpContent = {description: ts('Welcome to the api explorer.'), comment: ts('Select an entity to begin.')};
+ if (getMetaParams.schema) {
+ fetchMeta();
+ }
+ } else if (!actions.length && (!schema.length || !getEntity().actions)) {
+ if (getMetaParams.schema) {
+ entities.push({id: $scope.entity, text: $scope.entity});
+ }
+ getMetaParams.actions = [$scope.entity, 'getActions', {chain: {fields: [$scope.entity, 'getFields', {action: '$name'}]}}];
+ fetchMeta();
+ } else {
+ selectAction();
+ }
+
+ if ($scope.entity && schema.length) {
+ showEntityHelp($scope.entity);
+ }
+
+ // Update route when changing entity
+ $scope.$watch('entity', function(newVal, oldVal) {
+ if (oldVal !== newVal) {
+ // Flush actions cache to re-fetch for new entity
+ actions = [];
+ $location.url('/explorer/' + newVal);
+ }
+ });
+
+ // Update route when changing actions
+ $scope.$watch('action', function(newVal, oldVal) {
+ 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']);
+ }
+ });
+
+ $scope.indexHelp = {
+ description: ts('(string|int) Index results or select by index.'),
+ comment: ts('Pass a string to index the results by a field value. E.g. index: "name" will return an associative array with names as keys.') + '\n\n' +
+ ts('Pass an integer to return a single result; e.g. index: 0 will return the first result, 1 will return the second, and -1 will return the last.')
+ };
+
+ $scope.$watch('params', writeCode, true);
+ $scope.$watch('index', writeCode);
+ writeCode();
+
+ });
+
+ angular.module('api4Explorer').directive('crmApi4WhereClause', function($timeout) {
+ return {
+ scope: {
+ data: '=crmApi4WhereClause'
+ },
+ templateUrl: '~/api4Explorer/WhereClause.html',
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4');
+ scope.newClause = '';
+ scope.conjunctions = ['AND', 'OR', 'NOT'];
+ scope.operators = CRM.vars.api4.operators;
+
+ scope.addGroup = function(op) {
+ scope.data.where.push([op, []]);
+ };
+
+ scope.removeGroup = function() {
+ scope.data.groupParent.splice(scope.data.groupIndex, 1);
+ };
+
+ scope.onSort = function(event, ui) {
+ $('.api4-where-fieldset').toggleClass('api4-sorting', event.type === 'sortstart');
+ $('.api4-input.form-inline').css('margin-left', '');
+ };
+
+ // Indent clause while dragging between nested groups
+ scope.onSortOver = function(event, ui) {
+ var offset = 0;
+ if (ui.sender) {
+ offset = $(ui.placeholder).offset().left - $(ui.sender).offset().left;
+ }
+ $('.api4-input.form-inline.ui-sortable-helper').css('margin-left', '' + offset + 'px');
+ };
+
+ scope.$watch('newClause', function(value) {
+ var field = value;
+ $timeout(function() {
+ if (field) {
+ scope.data.where.push([field, '=', '']);
+ scope.newClause = null;
+ }
+ });
+ });
+ scope.$watch('data.where', function(values) {
+ // Remove empty values
+ _.each(values, function(clause, index) {
+ if (typeof clause !== 'undefined' && !clause[0]) {
+ values.splice(index, 1);
+ }
+ });
+ }, true);
+ }
+ };
+ });
+
+ angular.module('api4Explorer').directive('api4ExpValue', function($routeParams, crmApi4) {
+ return {
+ scope: {
+ data: '=api4ExpValue'
+ },
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4'),
+ entity = $routeParams.api4entity;
+
+ function getField(fieldName) {
+ var fieldNames = fieldName.split('.');
+ return get(entity, fieldNames);
+
+ function get(entity, fieldNames) {
+ if (fieldNames.length === 1) {
+ return _.findWhere(entityFields(entity), {name: fieldNames[0]});
+ }
+ var comboName = _.findWhere(entityFields(entity), {name: fieldNames[0] + '.' + fieldNames[1]});
+ if (comboName) {
+ return comboName;
+ }
+ var linkName = fieldNames.shift(),
+ entityLinks = _.findWhere(links, {entity: entity}).links,
+ newEntity = _.findWhere(entityLinks, {alias: linkName}).entity;
+ return get(newEntity, fieldNames);
+ }
+ }
+
+ function destroyWidget() {
+ var $el = $(element);
+ if ($el.is('.crm-form-date-wrapper .crm-hidden-date')) {
+ $el.crmDatepicker('destroy');
+ }
+ if ($el.is('.select2-container + input')) {
+ $el.crmEntityRef('destroy');
+ }
+ $(element).removeData().removeAttr('type').removeAttr('placeholder').show();
+ }
+
+ function makeWidget(field, op) {
+ var $el = $(element),
+ dataType = field.data_type,
+ multi = _.includes(['IN', 'NOT IN'], op);
+ if (op === 'IS NULL' || op === 'IS NOT NULL') {
+ $el.hide();
+ return;
+ }
+ if (dataType === 'Timestamp' || dataType === 'Date') {
+ if (_.includes(['=', '!=', '<>', '<', '>=', '<', '<='], op)) {
+ $el.crmDatepicker({time: dataType === 'Timestamp'});
+ }
+ } else if (_.includes(['=', '!=', '<>', 'IN', 'NOT IN'], op)) {
+ if (field.fk_entity) {
+ $el.crmEntityRef({entity: field.fk_entity, select:{multiple: multi}});
+ } else if (field.options) {
+ $el.addClass('loading').attr('placeholder', ts('- select -')).crmSelect2({multiple: multi, data: [{id: '', text: ''}]});
+ loadFieldOptions(field.entity).then(function(data) {
+ var options = [];
+ _.each(_.findWhere(data, {name: field.name}).options, function(val, key) {
+ options.push({id: key, text: val});
+ });
+ $el.removeClass('loading').select2({data: options, multiple: multi});
+ });
+ } 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')}
+ ]});
+ }
+ }
+ }
+
+ function loadFieldOptions(entity) {
+ var action = $routeParams.api4action;
+ if (!fieldOptions[entity + action]) {
+ fieldOptions[entity + action] = crmApi4(entity, 'getFields', {
+ loadOptions: true,
+ action: action,
+ where: [["options", "!=", false]],
+ select: ["name", "options"]
+ });
+ }
+ return fieldOptions[entity + action];
+ }
+
+ scope.$watchCollection('data', function(data) {
+ destroyWidget();
+ var field = getField(data.field);
+ if (field) {
+ makeWidget(field, data.op || '=');
+ }
+ });
+ }
+ };
+ });
+
+
+ angular.module('api4Explorer').directive('api4ExpChain', function(crmApi4) {
+ return {
+ scope: {
+ chain: '=api4ExpChain',
+ mainEntity: '=',
+ entities: '='
+ },
+ templateUrl: '~/api4Explorer/Chain.html',
+ link: function (scope, element, attrs) {
+ var ts = scope.ts = CRM.ts('api4');
+
+ function changeEntity(newEntity, oldEntity) {
+ // When clearing entity remove this chain
+ if (!newEntity) {
+ scope.chain[0] = '';
+ return;
+ }
+ // Reset action && index
+ if (newEntity !== oldEntity) {
+ scope.chain[1][1] = scope.chain[1][2] = '';
+ }
+ if (getEntity(newEntity).actions) {
+ setActions();
+ } else {
+ crmApi4(newEntity, 'getActions', {chain: {fields: [newEntity, 'getFields', {action: '$name'}]}})
+ .then(function(data) {
+ getEntity(data.entity).actions = data;
+ if (data.entity === scope.chain[1][0]) {
+ setActions();
+ }
+ });
+ }
+ }
+
+ function setActions() {
+ scope.actions = [''].concat(_.pluck(getEntity(scope.chain[1][0]).actions, 'name'));
+ }
+
+ // Set default params when choosing action
+ function changeAction(newAction, oldAction) {
+ var link;
+ // Prepopulate links
+ if (newAction && newAction !== oldAction) {
+ // Clear index
+ scope.chain[1][3] = '';
+ // Look for links back to main entity
+ _.each(entityFields(scope.chain[1][0]), function(field) {
+ if (field.fk_entity === scope.mainEntity) {
+ link = [field.name, '$id'];
+ }
+ });
+ // Look for links from main entity
+ if (!link && newAction !== 'create') {
+ _.each(entityFields(scope.mainEntity), function(field) {
+ if (field.fk_entity === scope.chain[1][0]) {
+ link = ['id', '$' + field.name];
+ // Since we're specifying the id, set index to getsingle
+ scope.chain[1][3] = '0';
+ }
+ });
+ }
+ if (link && _.contains(['get', 'update', 'replace', 'delete'], newAction)) {
+ scope.chain[1][2] = '{where: [[' + link[0] + ', =, ' + link[1] + ']]}';
+ }
+ else if (link && _.contains(['create'], newAction)) {
+ scope.chain[1][2] = '{values: {' + link[0] + ': ' + link[1] + '}}';
+ } else {
+ scope.chain[1][2] = '{}';
+ }
+ }
+ }
+
+ scope.$watch("chain[1][0]", changeEntity);
+ scope.$watch("chain[1][1]", changeAction);
+ }
+ };
+ });
+
+ function getEntity(entityName) {
+ return _.findWhere(schema, {name: entityName});
+ }
+
+ function entityFields(entityName) {
+ return _.result(getEntity(entityName), 'fields');
+ }
+
+ // Collapsible optgroups for select2
+ $(function() {
+ $('body')
+ .on('select2-open', function(e) {
+ if ($(e.target).hasClass('collapsible-optgroups')) {
+ $('#select2-drop')
+ .off('.collapseOptionGroup')
+ .addClass('collapsible-optgroups-enabled')
+ .on('click.collapseOptionGroup', '.select2-result-with-children > .select2-result-label', function() {
+ $(this).parent().toggleClass('optgroup-expanded');
+ });
+ }
+ })
+ .on('select2-close', function() {
+ $('#select2-drop').off('.collapseOptionGroup').removeClass('collapsible-optgroups-enabled');
+ });
+ });
+})(angular, CRM.$, CRM._);