Merge pull request #3113 from deepak-srivastava/CRM-14431
[civicrm-core.git] / templates / CRM / Admin / Page / APIExplorer.js
CommitLineData
e4176358
CW
1(function($, _, undefined) {
2 var
3 entity,
4 action,
2b6e1174 5 actions = ['get'],
e4176358
CW
6 fields = [],
7 options = {},
8 params = {},
2b6e1174 9 smartyStub,
8ffa1387 10 fieldTpl = _.template($('#api-param-tpl').html()),
c275764e 11 optionsTpl = _.template($('#api-options-tpl').html()),
b07af612 12 returnTpl = _.template($('#api-return-tpl').html()),
8ffa1387 13 chainTpl = _.template($('#api-chain-tpl').html());
0c3a6e64 14
2b6e1174
CW
15 /**
16 * Call prettyPrint function if it successfully loaded from the cdn
17 */
cd77ffa6
CW
18 function prettyPrint() {
19 if (window.prettyPrint) {
20 window.prettyPrint();
21 }
22 }
23
b07af612
CW
24 /**
25 * Add a "fields" row
26 * @param name
27 */
e4176358
CW
28 function addField(name) {
29 $('#api-params').append($(fieldTpl({name: name || ''})));
30 var $row = $('tr:last-child', '#api-params');
b07af612
CW
31 $('.api-param-name', $row).crmSelect2({
32 data: fields.concat({id: '-', text: ts('Other') + '...'})
33 }).change();
8ffa1387
CW
34 }
35
c275764e
CW
36 /**
37 * Add a new "options" row
38 * @param name
39 */
40 function addOptionField(name) {
41 if ($('.api-options-row', '#api-params').length) {
42 $('.api-options-row:last', '#api-params').after($(optionsTpl({})));
43 } else {
44 $('#api-params').append($(optionsTpl({})));
45 }
46 var $row = $('.api-options-row:last', '#api-params');
47 $('.api-option-name', $row).crmSelect2({data: [
48 {id: 'limit', text: 'limit'},
49 {id: 'offset', text: 'offset'},
50 {id: 'sort', text: 'sort'},
51 {id: 'metadata', text: 'metadata'},
52 {id: '-', text: ts('Other') + '...'}
53 ]});
54 }
55
b07af612
CW
56 /**
57 * Add an "api chain" row
58 */
8ffa1387
CW
59 function addChainField() {
60 $('#api-params').append($(chainTpl({})));
61 var $row = $('tr:last-child', '#api-params');
62 $('.api-chain-entity', $row).crmSelect2({
63 formatSelection: function(item) {
64 return '<span class="icon ui-icon-link"></span> API ' + item.text;
2b6e1174
CW
65 },
66 placeholder: '<span class="icon ui-icon-link"></span> ' + ts('Entity'),
67 escapeMarkup: function(m) {return m}
8ffa1387 68 });
0c3a6e64
CW
69 }
70
b07af612
CW
71 /**
72 * Fetch fields for entity+action
73 */
e4176358
CW
74 function getFields() {
75 var required = [];
76 fields = [];
77 options = {};
78 // Special case for getfields
79 if (action === 'getfields') {
80 fields.push({
81 id: 'api_action',
82 text: 'Action'
83 });
84 options.api_action = [];
85 $('option', '#api-action').each(function() {
86 if (this.value) {
87 options.api_action.push({key: this.value, value: $(this).text()});
88 }
89 });
90 showFields(['api_action']);
0c3a6e64
CW
91 return;
92 }
e4176358
CW
93 CRM.api3(entity, 'getFields', {'api_action': action, sequential: 1, options: {get_options: 'all'}}).done(function(data) {
94 _.each(data.values, function(field) {
95 if (field.name) {
96 fields.push({
97 id: field.name,
98 text: field.title || field.name,
99 required: field['api.required'] || false
100 });
101 if (field['api.required']) {
102 required.push(field.name);
103 }
104 if (field.options) {
105 options[field.name] = field.options;
106 }
107 }
108 });
109 showFields(required);
b07af612
CW
110 if (action === 'get' || action === 'getsingle') {
111 showReturn();
112 }
0c3a6e64
CW
113 });
114 }
115
b07af612
CW
116 /**
117 * For "get" actions show the "return" options
118 */
119 function showReturn() {
120 $('#api-params').prepend($(returnTpl({})));
121 console.log($(returnTpl({})));
122 $('#api-return-value').crmSelect2({data: fields, multiple: true});
123 }
124
125 /**
126 * Fetch actions for entity
127 */
2b6e1174
CW
128 function getActions() {
129 if (entity) {
130 CRM.api3(entity, 'getactions').done(function(data) {
131 // Ensure 'get' is always an action
132 actions = _.union(['get'], data.values);
133 populateActions();
134 });
135 } else {
136 actions = ['get'];
137 populateActions();
138 }
139 }
140
b07af612
CW
141 /**
142 * Called after getActions to populate action list
143 * @param el
144 */
2b6e1174
CW
145 function populateActions(el) {
146 $('#api-action').select2({
147 data: _.transform(actions, function(ret, item) {ret.push({text: item, id: item})})
148 });
149 }
150
b07af612
CW
151 /**
152 * Called after getfields to show buttons and required fields
153 * @param required
154 */
e4176358 155 function showFields(required) {
e4176358 156 $('#api-params').empty();
8ffa1387 157 $('#api-param-buttons').show();
e4176358
CW
158 if (required.length) {
159 _.each(required, addField);
160 } else {
161 addField();
0c3a6e64
CW
162 }
163 }
164
c275764e
CW
165 /**
166 * Add/remove option list for selected field's pseudoconstant
167 */
e4176358
CW
168 function toggleOptions() {
169 var name = $(this).val(),
170 $valField = $(this).closest('tr').find('.api-param-value');
171 if (options[name]) {
172 $valField.val('').select2({
173 multiple: true,
174 data: _.transform(options[name], function(result, option) {
175 result.push({id: option.key, text: option.value});
176 })
177 });
178 }
179 else if ($valField.data('select2')) {
180 $valField.select2('destroy');
181 }
e4176358 182 }
0c3a6e64 183
e4176358
CW
184 /**
185 * Attempt to parse a string into a value of the intended type
186 * @param val
187 */
188 function evaluate(val, makeArray) {
189 try {
190 if (!val.length) {
191 return val;
192 }
193 var first = val.charAt(0),
194 last = val.slice(-1);
195 // Simple types
196 if (val === 'true' || val === 'false' || val === 'null' || !isNaN(val)) {
197 return eval(val);
198 }
199 // Quoted strings
200 if ((first === '"' || first === "'") && last === first) {
201 return val.slice(1, -1);
0c3a6e64 202 }
e4176358
CW
203 // Parse json
204 if ((first === '[' && last === ']') || (first === '{' && last === '}')) {
205 return eval('(' + val + ')');
206 }
207 // Transform csv to array
208 if (makeArray && val.indexOf(',') > 0) {
209 return val.split(',');
210 }
211 // Ok ok it's really a string
212 return val;
213 } catch(e) {
214 // If eval crashed return undefined
215 return undefined;
0c3a6e64 216 }
e4176358 217 }
0c3a6e64 218
e4176358
CW
219 /**
220 * Format value to look like php code
221 * @param val
222 */
223 function phpFormat(val) {
224 var ret = '';
225 if ($.isPlainObject(val)) {
226 $.each(val, function(k, v) {
c275764e 227 ret += (ret ? ', ' : '') + "'" + k + "' => " + phpFormat(v);
e4176358
CW
228 });
229 return 'array(' + ret + ')';
0c3a6e64 230 }
e4176358
CW
231 if ($.isArray(val)) {
232 $.each(val, function(k, v) {
233 ret += (ret ? ', ' : '') + phpFormat(v);
234 });
235 return 'array(' + ret + ')';
236 }
237 return JSON.stringify(val);
238 }
239
240 /**
241 * Smarty doesn't support array literals so we provide a stub
242 * @param js string
243 */
244 function smartyFormat(js, key) {
245 if (js.indexOf('[') > -1 || js.indexOf('{') > -1) {
2b6e1174 246 smartyStub = true;
e4176358
CW
247 return '$' + key.replace(/[. -]/g, '_');
248 }
249 return js;
250 }
251
c275764e
CW
252 /**
253 * Create the params array from user input
254 * @param e
255 */
e4176358
CW
256 function buildParams(e) {
257 params = {};
258 $('.api-param-checkbox:checked').each(function() {
259 params[this.name] = 1;
260 });
c275764e 261 $('input.api-param-value, input.api-option-value').each(function() {
e4176358
CW
262 var $row = $(this).closest('tr'),
263 val = evaluate($(this).val(), $(this).is('.select2-offscreen')),
264 name = $('input.api-param-name', $row).val(),
8ffa1387 265 op = $('select.api-param-op', $row).val() || '=';
b07af612
CW
266
267 // Ignore blank values for the return field
268 if ($(this).is('#api-return-value') && !val) {
269 return;
270 }
8ffa1387
CW
271 // Special syntax for api chaining
272 if (!name && $('select.api-chain-entity', $row).val()) {
273 name = 'api.' + $('select.api-chain-entity', $row).val() + '.' + $('select.api-chain-action', $row).val();
274 }
c275764e
CW
275 // Special handling for options
276 if ($(this).is('.api-option-value')) {
277 op = $('input.api-option-name', $row).val();
278 if (op) {
279 name = 'options';
280 }
281 }
e4176358 282 if (name && val !== undefined) {
c275764e 283 params[name] = op === '=' ? val : (params[name] || {});
e4176358
CW
284 if (op !== '=') {
285 params[name][op] = val;
286 }
287 clearError(this);
288 }
289 else if (name && (!e || e.type !== 'keyup')) {
290 setError(this);
291 }
292 });
293 if (entity && action) {
294 formatQuery();
0c3a6e64 295 }
e4176358 296 }
0c3a6e64 297
e4176358
CW
298 function setError(el) {
299 if (!$(el).hasClass('crm-error')) {
300 $(el)
301 .addClass('crm-error')
2b6e1174 302 .css('width', '82%')
e4176358
CW
303 .attr('title', ts('Syntax error'))
304 .before('<div class="icon red-icon ui-icon-alert"/>');
305 }
306 }
0c3a6e64 307
e4176358
CW
308 function clearError(el) {
309 $(el)
310 .removeClass('crm-error')
311 .attr('title', '')
2b6e1174 312 .css('width', '85%')
e4176358
CW
313 .siblings('.ui-icon-alert').remove();
314 }
0c3a6e64 315
e4176358
CW
316 function formatQuery() {
317 var i = 0, q = {
318 smarty: "{crmAPI var='result' entity='" + entity + "' action='" + action + "'",
319 php: "$result = civicrm_api3('" + entity + "', '" + action + "'",
320 json: "CRM.api3('" + entity + "', '" + action + "'",
321 rest: CRM.config.resourceBase + "extern/rest.php?entity=" + entity + "&action=" + action + "&json=" + JSON.stringify(params) + "&api_key=yoursitekey&key=yourkey"
322 };
2b6e1174 323 smartyStub = false;
e4176358
CW
324 $.each(params, function(key, value) {
325 var js = JSON.stringify(value);
326 if (!i++) {
327 q.php += ", array(\n";
328 q.json += ", {\n";
329 } else {
330 q.json += ",\n";
331 }
332 q.php += " '" + key + "' => " + phpFormat(value) + ",\n";
333 q.json += " \"" + key + '": ' + js;
e4176358
CW
334 q.smarty += ' ' + key + '=' + smartyFormat(js, key);
335 });
336 if (i) {
337 q.php += ")";
338 q.json += "\n}";
339 }
340 q.php += ");";
341 q.json += ").done(function(result) {\n // do something\n});";
342 q.smarty += "}\n{foreach from=$result.values item=" + entity.toLowerCase() + "}\n {$" + entity.toLowerCase() + ".some_field}\n{/foreach}";
343 if (action.indexOf('get') < 0) {
344 q.smarty = '{* Smarty API only works with get actions *}';
2b6e1174
CW
345 } else if (smartyStub) {
346 q.smarty = "{* Smarty does not have a syntax for array literals; assign complex variables on the server *}\n" + q.smarty;
e4176358
CW
347 }
348 $.each(q, function(type, val) {
cd77ffa6 349 $('#api-' + type).removeClass('prettyprinted').text(val);
e4176358 350 });
cd77ffa6 351 prettyPrint();
e4176358 352 }
0c3a6e64 353
e4176358
CW
354 function submit(e) {
355 e.preventDefault();
356 if (!entity || !action) {
2b6e1174 357 alert(ts('Select an entity.'));
e4176358
CW
358 return;
359 }
360 if (action.indexOf('get') < 0) {
361 var msg = action === 'delete' ? ts('This will delete data from CiviCRM. Are you sure?') : ts('This will write to the database. Continue?');
362 CRM.confirm({title: ts('Confirm %1', {1: action}), message: msg}).on('crmConfirm:yes', execute);
0c3a6e64 363 } else {
e4176358 364 execute();
0c3a6e64 365 }
0c3a6e64
CW
366 }
367
e4176358
CW
368 function execute() {
369 $('#api-result').html('<div class="crm-loading-element"></div>');
370 $.ajax({
371 url: CRM.url('civicrm/ajax/rest'),
372 data: {
373 entity: entity,
374 action: action,
375 prettyprint: 1,
376 json: JSON.stringify(params)
377 },
378 type: action.indexOf('get') < 0 ? 'POST' : 'GET',
379 dataType: 'text'
380 }).done(function(text) {
2b6e1174 381 $('#api-result').addClass('prettyprint').removeClass('prettyprinted').text(text);
cd77ffa6 382 prettyPrint();
e4176358 383 });
0c3a6e64 384 }
e4176358
CW
385
386 $(document).ready(function() {
387 $('form#api-explorer')
388 .on('change', '#api-entity, #api-action', function() {
389 entity = $('#api-entity').val();
2b6e1174
CW
390 if ($(this).is('#api-entity')) {
391 $('#api-action').select2('val', 'get');
392 getActions();
393 }
e4176358
CW
394 action = $('#api-action').val();
395 if (entity && action) {
396 $('#api-params').html('<tr><td colspan="4" class="crm-loading-element"></td></tr>');
397 $('#api-params-table thead').show();
398 getFields();
399 buildParams();
400 } else {
401 $('#api-params, #api-generated pre').empty();
8ffa1387 402 $('#api-param-buttons, #api-params-table thead').hide();
e4176358
CW
403 }
404 })
2b6e1174 405 .on('change keyup', 'input.api-input, #api-params select', buildParams)
e4176358
CW
406 .on('submit', submit);
407 $('#api-params')
408 .on('change', '.api-param-name', toggleOptions)
c275764e
CW
409 .on('change', '.api-param-name, .api-option-name', function() {
410 if ($(this).val() === '-') {
411 $(this).select2('destroy');
412 $(this).val('').focus();
413 }
414 })
e4176358
CW
415 .on('click', '.api-param-remove', function(e) {
416 e.preventDefault();
417 $(this).closest('tr').remove();
418 buildParams();
419 });
420 $('#api-params-add').on('click', function(e) {
8ffa1387 421 e.preventDefault();
e4176358 422 addField();
8ffa1387 423 });
c275764e
CW
424 $('#api-option-add').on('click', function(e) {
425 e.preventDefault();
426 addOptionField();
427 });
8ffa1387 428 $('#api-chain-add').on('click', function(e) {
e4176358 429 e.preventDefault();
8ffa1387 430 addChainField();
e4176358
CW
431 });
432 $('#api-entity').change();
0c3a6e64 433 });
e4176358 434}(CRM.$, CRM._));