Api Explorer - add support for chaining
[civicrm-core.git] / templates / CRM / Admin / Page / APIExplorer.js
CommitLineData
e4176358
CW
1(function($, _, undefined) {
2 var
3 entity,
4 action,
5 fields = [],
6 options = {},
7 params = {},
8ffa1387
CW
8 fieldTpl = _.template($('#api-param-tpl').html()),
9 chainTpl = _.template($('#api-chain-tpl').html());
0c3a6e64 10
e4176358
CW
11 function addField(name) {
12 $('#api-params').append($(fieldTpl({name: name || ''})));
13 var $row = $('tr:last-child', '#api-params');
8ffa1387
CW
14 $('.api-param-name', $row).crmSelect2({data: fields}).change();
15 }
16
17 function addChainField() {
18 $('#api-params').append($(chainTpl({})));
19 var $row = $('tr:last-child', '#api-params');
20 $('.api-chain-entity', $row).crmSelect2({
21 formatSelection: function(item) {
22 return '<span class="icon ui-icon-link"></span> API ' + item.text;
23 }
24 });
0c3a6e64
CW
25 }
26
e4176358
CW
27 function getFields() {
28 var required = [];
29 fields = [];
30 options = {};
31 // Special case for getfields
32 if (action === 'getfields') {
33 fields.push({
34 id: 'api_action',
35 text: 'Action'
36 });
37 options.api_action = [];
38 $('option', '#api-action').each(function() {
39 if (this.value) {
40 options.api_action.push({key: this.value, value: $(this).text()});
41 }
42 });
43 showFields(['api_action']);
0c3a6e64
CW
44 return;
45 }
e4176358
CW
46 CRM.api3(entity, 'getFields', {'api_action': action, sequential: 1, options: {get_options: 'all'}}).done(function(data) {
47 _.each(data.values, function(field) {
48 if (field.name) {
49 fields.push({
50 id: field.name,
51 text: field.title || field.name,
52 required: field['api.required'] || false
53 });
54 if (field['api.required']) {
55 required.push(field.name);
56 }
57 if (field.options) {
58 options[field.name] = field.options;
59 }
60 }
61 });
62 showFields(required);
0c3a6e64
CW
63 });
64 }
65
e4176358
CW
66 function showFields(required) {
67 fields.push({
68 id: '-',
69 text: ts('Other') + '...'
0c3a6e64 70 });
e4176358 71 $('#api-params').empty();
8ffa1387 72 $('#api-param-buttons').show();
e4176358
CW
73 if (required.length) {
74 _.each(required, addField);
75 } else {
76 addField();
0c3a6e64
CW
77 }
78 }
79
e4176358
CW
80 function toggleOptions() {
81 var name = $(this).val(),
82 $valField = $(this).closest('tr').find('.api-param-value');
83 if (options[name]) {
84 $valField.val('').select2({
85 multiple: true,
86 data: _.transform(options[name], function(result, option) {
87 result.push({id: option.key, text: option.value});
88 })
89 });
90 }
91 else if ($valField.data('select2')) {
92 $valField.select2('destroy');
93 }
94 if (name === '-') {
95 $(this).select2('destroy');
96 $(this).val('').focus();
97 }
98 }
0c3a6e64 99
e4176358
CW
100 /**
101 * Attempt to parse a string into a value of the intended type
102 * @param val
103 */
104 function evaluate(val, makeArray) {
105 try {
106 if (!val.length) {
107 return val;
108 }
109 var first = val.charAt(0),
110 last = val.slice(-1);
111 // Simple types
112 if (val === 'true' || val === 'false' || val === 'null' || !isNaN(val)) {
113 return eval(val);
114 }
115 // Quoted strings
116 if ((first === '"' || first === "'") && last === first) {
117 return val.slice(1, -1);
0c3a6e64 118 }
e4176358
CW
119 // Parse json
120 if ((first === '[' && last === ']') || (first === '{' && last === '}')) {
121 return eval('(' + val + ')');
122 }
123 // Transform csv to array
124 if (makeArray && val.indexOf(',') > 0) {
125 return val.split(',');
126 }
127 // Ok ok it's really a string
128 return val;
129 } catch(e) {
130 // If eval crashed return undefined
131 return undefined;
0c3a6e64 132 }
e4176358 133 }
0c3a6e64 134
e4176358
CW
135 /**
136 * Format value to look like php code
137 * @param val
138 */
139 function phpFormat(val) {
140 var ret = '';
141 if ($.isPlainObject(val)) {
142 $.each(val, function(k, v) {
143 ret += (ret ? ',' : '') + "'" + k + "' => " + phpFormat(v);
144 });
145 return 'array(' + ret + ')';
0c3a6e64 146 }
e4176358
CW
147 if ($.isArray(val)) {
148 $.each(val, function(k, v) {
149 ret += (ret ? ', ' : '') + phpFormat(v);
150 });
151 return 'array(' + ret + ')';
152 }
153 return JSON.stringify(val);
154 }
155
156 /**
157 * Smarty doesn't support array literals so we provide a stub
158 * @param js string
159 */
160 function smartyFormat(js, key) {
161 if (js.indexOf('[') > -1 || js.indexOf('{') > -1) {
162 return '$' + key.replace(/[. -]/g, '_');
163 }
164 return js;
165 }
166
167 function buildParams(e) {
168 params = {};
169 $('.api-param-checkbox:checked').each(function() {
170 params[this.name] = 1;
171 });
172 $('input.api-param-value').each(function() {
173 var $row = $(this).closest('tr'),
174 val = evaluate($(this).val(), $(this).is('.select2-offscreen')),
175 name = $('input.api-param-name', $row).val(),
8ffa1387
CW
176 op = $('select.api-param-op', $row).val() || '=';
177 // Special syntax for api chaining
178 if (!name && $('select.api-chain-entity', $row).val()) {
179 name = 'api.' + $('select.api-chain-entity', $row).val() + '.' + $('select.api-chain-action', $row).val();
180 }
e4176358
CW
181 if (name && val !== undefined) {
182 params[name] = op === '=' ? val : {};
183 if (op !== '=') {
184 params[name][op] = val;
185 }
186 clearError(this);
187 }
188 else if (name && (!e || e.type !== 'keyup')) {
189 setError(this);
190 }
191 });
192 if (entity && action) {
193 formatQuery();
0c3a6e64 194 }
e4176358 195 }
0c3a6e64 196
e4176358
CW
197 function setError(el) {
198 if (!$(el).hasClass('crm-error')) {
199 $(el)
200 .addClass('crm-error')
201 .attr('title', ts('Syntax error'))
202 .before('<div class="icon red-icon ui-icon-alert"/>');
203 }
204 }
0c3a6e64 205
e4176358
CW
206 function clearError(el) {
207 $(el)
208 .removeClass('crm-error')
209 .attr('title', '')
210 .siblings('.ui-icon-alert').remove();
211 }
0c3a6e64 212
e4176358
CW
213 function formatQuery() {
214 var i = 0, q = {
215 smarty: "{crmAPI var='result' entity='" + entity + "' action='" + action + "'",
216 php: "$result = civicrm_api3('" + entity + "', '" + action + "'",
217 json: "CRM.api3('" + entity + "', '" + action + "'",
218 rest: CRM.config.resourceBase + "extern/rest.php?entity=" + entity + "&action=" + action + "&json=" + JSON.stringify(params) + "&api_key=yoursitekey&key=yourkey"
219 };
220 $.each(params, function(key, value) {
221 var js = JSON.stringify(value);
222 if (!i++) {
223 q.php += ", array(\n";
224 q.json += ", {\n";
225 } else {
226 q.json += ",\n";
227 }
228 q.php += " '" + key + "' => " + phpFormat(value) + ",\n";
229 q.json += " \"" + key + '": ' + js;
230 // FIXME: How to deal with complex values in smarty?
231 q.smarty += ' ' + key + '=' + smartyFormat(js, key);
232 });
233 if (i) {
234 q.php += ")";
235 q.json += "\n}";
236 }
237 q.php += ");";
238 q.json += ").done(function(result) {\n // do something\n});";
239 q.smarty += "}\n{foreach from=$result.values item=" + entity.toLowerCase() + "}\n {$" + entity.toLowerCase() + ".some_field}\n{/foreach}";
240 if (action.indexOf('get') < 0) {
241 q.smarty = '{* Smarty API only works with get actions *}';
242 }
243 $.each(q, function(type, val) {
244 $('#api-' + type).text(val);
245 });
246 }
0c3a6e64 247
e4176358
CW
248 function submit(e) {
249 e.preventDefault();
250 if (!entity || !action) {
251 alert(ts('Select an entity & action.'));
252 return;
253 }
254 if (action.indexOf('get') < 0) {
255 var msg = action === 'delete' ? ts('This will delete data from CiviCRM. Are you sure?') : ts('This will write to the database. Continue?');
256 CRM.confirm({title: ts('Confirm %1', {1: action}), message: msg}).on('crmConfirm:yes', execute);
0c3a6e64 257 } else {
e4176358 258 execute();
0c3a6e64 259 }
0c3a6e64
CW
260 }
261
e4176358
CW
262 function execute() {
263 $('#api-result').html('<div class="crm-loading-element"></div>');
264 $.ajax({
265 url: CRM.url('civicrm/ajax/rest'),
266 data: {
267 entity: entity,
268 action: action,
269 prettyprint: 1,
270 json: JSON.stringify(params)
271 },
272 type: action.indexOf('get') < 0 ? 'POST' : 'GET',
273 dataType: 'text'
274 }).done(function(text) {
275 $('#api-result').text(text);
276 });
0c3a6e64 277 }
e4176358
CW
278
279 $(document).ready(function() {
280 $('form#api-explorer')
281 .on('change', '#api-entity, #api-action', function() {
282 entity = $('#api-entity').val();
283 action = $('#api-action').val();
284 if (entity && action) {
285 $('#api-params').html('<tr><td colspan="4" class="crm-loading-element"></td></tr>');
286 $('#api-params-table thead').show();
287 getFields();
288 buildParams();
289 } else {
290 $('#api-params, #api-generated pre').empty();
8ffa1387 291 $('#api-param-buttons, #api-params-table thead').hide();
e4176358
CW
292 }
293 })
8ffa1387 294 .on('change keyup', 'input.api-param-checkbox, input.api-param-value, input.api-param-name, #api-params select', buildParams)
e4176358
CW
295 .on('submit', submit);
296 $('#api-params')
297 .on('change', '.api-param-name', toggleOptions)
298 .on('click', '.api-param-remove', function(e) {
299 e.preventDefault();
300 $(this).closest('tr').remove();
301 buildParams();
302 });
303 $('#api-params-add').on('click', function(e) {
8ffa1387 304 e.preventDefault();
e4176358 305 addField();
8ffa1387
CW
306 });
307 $('#api-chain-add').on('click', function(e) {
e4176358 308 e.preventDefault();
8ffa1387 309 addChainField();
e4176358
CW
310 });
311 $('#api-entity').change();
0c3a6e64 312 });
e4176358 313}(CRM.$, CRM._));