1 (function($, _
, undefined) {
10 fieldTpl
= _
.template($('#api-param-tpl').html()),
11 optionsTpl
= _
.template($('#api-options-tpl').html()),
12 returnTpl
= _
.template($('#api-return-tpl').html()),
13 chainTpl
= _
.template($('#api-chain-tpl').html()),
15 // Operators with special properties
16 BOOL
= ['IS NULL', 'IS NOT NULL'],
17 TEXT
= ['LIKE', 'NOT LIKE'],
18 MULTI
= ['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'];
21 * Call prettyPrint function if it successfully loaded from the cdn
23 function prettyPrint() {
24 if (window
.prettyPrint
) {
33 function addField(name
) {
34 $('#api-params').append($(fieldTpl({name
: name
|| ''})));
35 var $row
= $('tr:last-child', '#api-params');
36 $('input.api-param-name', $row
).crmSelect2({
37 data
: fields
.concat({id
: '-', text
: ts('Other') + '...'})
42 * Add a new "options" row
45 function addOptionField(name
) {
46 if ($('.api-options-row', '#api-params').length
) {
47 $('.api-options-row:last', '#api-params').after($(optionsTpl({})));
49 $('#api-params').append($(optionsTpl({})));
51 var $row
= $('.api-options-row:last', '#api-params');
52 $('.api-option-name', $row
).crmSelect2({data
: [
53 {id
: 'limit', text
: 'limit'},
54 {id
: 'offset', text
: 'offset'},
55 {id
: 'sort', text
: 'sort'},
56 {id
: 'metadata', text
: 'metadata'},
57 {id
: '-', text
: ts('Other') + '...'}
62 * Add an "api chain" row
64 function addChainField() {
65 $('#api-params').append($(chainTpl({})));
66 var $row
= $('tr:last-child', '#api-params');
67 $('.api-chain-entity', $row
).crmSelect2({
68 formatSelection: function(item
) {
69 return '<span class="icon ui-icon-link"></span> API ' + item
.text
;
71 placeholder
: '<span class="icon ui-icon-link"></span> ' + ts('Entity'),
72 escapeMarkup: function(m
) {return m
}
77 * Fetch fields for entity+action
79 function getFields() {
83 // Special case for getfields
84 if (action
=== 'getfields') {
89 options
.api_action
= [];
90 $('option', '#api-action').each(function() {
92 options
.api_action
.push({key
: this.value
, value
: $(this).text()});
95 showFields(['api_action']);
98 CRM
.api3(entity
, 'getFields', {'api_action': action
, sequential
: 1, options
: {get_options
: 'all'}}).done(function(data
) {
99 _
.each(data
.values
, function(field
) {
103 text
: field
.title
|| field
.name
,
104 required
: field
['api.required'] || false
106 if (field
['api.required']) {
107 required
.push(field
.name
);
110 options
[field
.name
] = field
.options
;
114 showFields(required
);
115 if (action
=== 'get' || action
=== 'getsingle') {
122 * For "get" actions show the "return" options
124 function showReturn() {
125 $('#api-params').prepend($(returnTpl({})));
126 $('#api-return-value').crmSelect2({data
: fields
, multiple
: true});
130 * Fetch actions for entity
132 function getActions() {
134 $('#api-action').addClass('loading');
135 CRM
.api3(entity
, 'getactions').done(function(data
) {
136 actions
= data
.values
|| ['get'];
146 * Called after getActions to populate action list
149 function populateActions(el
) {
150 var val
= $('#api-action').val();
151 $('#api-action').removeClass('loading').select2({
152 data
: _
.transform(actions
, function(ret
, item
) {ret
.push({text
: item
, id
: item
})})
154 // If previously selected action is not available, set it to 'get' if possible
155 if (_
.indexOf(actions
, val
) < 0) {
156 $('#api-action').select2('val', _
.indexOf(actions
, 'get') < 0 ? actions
[0] : 'get', true);
161 * Called after getfields to show buttons and required fields
164 function showFields(required
) {
165 $('#api-params').empty();
166 $('#api-param-buttons').show();
167 if (required
.length
) {
168 _
.each(required
, addField
);
175 * Render value input as a textfield, option list, or hidden,
176 * Depending on selected param name and operator
178 function renderValueField() {
179 var $row
= $(this).closest('tr'),
180 name
= $('input.api-param-name', $row
).val(),
181 operator
= $('.api-param-op', $row
).val(),
182 operatorType
= $.inArray(operator
, MULTI
) > -1 ? 'multi' : ($.inArray(operator
, BOOL
) > -1 ? 'bool' : 'single'),
183 $valField
= $('input.api-param-value', $row
),
184 currentVal
= $valField
.val();
185 // Boolean fields only have 1 possible value
186 if (operatorType
== 'bool') {
187 if ($valField
.data('select2')) {
188 $valField
.select2('destroy');
190 $valField
.css('visibility', 'hidden').val('1');
193 $valField
.css('visibility', '');
195 if (options
[name
] && $.inArray(operator
, TEXT
) < 0) {
196 // Reset value before switching to a select from something else
197 if ($(this).is('.api-param-name') || !$valField
.data('select2')) {
200 // When switching from multi-select to single select
201 else if (operatorType
== 'single' && currentVal
.indexOf(',') > -1) {
202 $valField
.val(currentVal
.split(',')[0]);
205 multiple
: (operatorType
=== 'multi'),
206 data
: _
.transform(options
[name
], function(result
, option
) {
207 result
.push({id
: option
.key
, text
: option
.value
});
213 if ($valField
.data('select2')) {
214 $valField
.select2('destroy');
219 * Attempt to parse a string into a value of the intended type
221 * @param makeArray bool
223 function evaluate(val
, makeArray
) {
226 return makeArray
? [] : '';
228 var first
= val
.charAt(0),
229 last
= val
.slice(-1);
231 if (val
=== 'true' || val
=== 'false' || val
=== 'null') {
235 if ((first
=== '"' || first
=== "'") && last
=== first
) {
236 return val
.slice(1, -1);
239 if ((first
=== '[' && last
=== ']') || (first
=== '{' && last
=== '}')) {
240 return eval('(' + val
+ ')');
242 // Transform csv to array
245 $.each(val
.split(','), function(k
, v
) {
246 result
.push(evaluate($.trim(v
)) || v
);
250 // Integers - quote any number that starts with 0 to avoid oddities
251 if (!isNaN(val
) && val
.search(/[^\d]/) < 0 && (val
.length
=== 1 || first
!== '0')) {
252 return parseInt(val
, 10);
254 // Ok ok it's really a string
257 // If eval crashed return undefined
263 * Format value to look like php code
266 function phpFormat(val
) {
268 if ($.isPlainObject(val
)) {
269 $.each(val
, function(k
, v
) {
270 ret
+= (ret
? ', ' : '') + "'" + k
+ "' => " + phpFormat(v
);
272 return 'array(' + ret
+ ')';
274 if ($.isArray(val
)) {
275 $.each(val
, function(k
, v
) {
276 ret
+= (ret
? ', ' : '') + phpFormat(v
);
278 return 'array(' + ret
+ ')';
280 return JSON
.stringify(val
).replace(/\$/g, '\\$');
284 * Smarty doesn't support array literals so we provide a stub
287 function smartyFormat(js
, key
) {
288 if (js
.indexOf('[') > -1 || js
.indexOf('{') > -1) {
290 return '$' + key
.replace(/[. -]/g, '_');
296 * Create the params array from user input
299 function buildParams(e
) {
301 $('.api-param-checkbox:checked').each(function() {
302 params
[this.name
] = 1;
304 $('input.api-param-value, input.api-option-value').each(function() {
305 var $row
= $(this).closest('tr'),
306 op
= $('select.api-param-op', $row
).val() || '=',
307 name
= $('input.api-param-name', $row
).val(),
308 val
= evaluate($(this).val(), $.inArray(op
, MULTI
) > -1);
310 // Ignore blank values for the return field
311 if ($(this).is('#api-return-value') && !val
) {
314 // Special syntax for api chaining
315 if (!name
&& $('select.api-chain-entity', $row
).val()) {
316 name
= 'api.' + $('select.api-chain-entity', $row
).val() + '.' + $('select.api-chain-action', $row
).val();
318 // Special handling for options
319 if ($(this).is('.api-option-value')) {
320 op
= $('input.api-option-name', $row
).val();
325 if (name
&& val
!== undefined) {
326 params
[name
] = op
=== '=' ? val
: (params
[name
] || {});
328 params
[name
][op
] = val
;
330 if ($(this).hasClass('crm-error')) {
334 else if (name
&& (!e
|| e
.type
!== 'keyup')) {
338 if (entity
&& action
) {
343 function setError(el
) {
344 if (!$(el
).hasClass('crm-error')) {
345 var msg
= ts('Syntax error: input should be valid JSON or a quoted string.');
347 .addClass('crm-error')
350 .before('<div class="icon red-icon ui-icon-alert" title="'+msg
+'"/>')
355 function clearError(el
) {
357 .removeClass('crm-error')
361 .siblings('.ui-icon-alert').remove();
364 function formatQuery() {
366 smarty
: "{crmAPI var='result' entity='" + entity
+ "' action='" + action
+ "'",
367 php
: "$result = civicrm_api3('" + entity
+ "', '" + action
+ "'",
368 json
: "CRM.api3('" + entity
+ "', '" + action
+ "'",
369 drush
: "drush cvapi " + entity
+ '.' + action
+ ' ',
370 wpcli
: "wp cv api " + entity
+ '.' + action
+ ' ',
371 rest
: CRM
.config
.resourceBase
+ "extern/rest.php?entity=" + entity
+ "&action=" + action
+ "&json=" + JSON
.stringify(params
) + "&api_key=yoursitekey&key=yourkey"
374 $.each(params
, function(key
, value
) {
375 var js
= JSON
.stringify(value
);
377 q
.php
+= ", array(\n";
382 q
.php
+= " '" + key
+ "' => " + phpFormat(value
) + ",\n";
383 q
.json
+= " \"" + key
+ '": ' + js
;
384 q
.smarty
+= ' ' + key
+ '=' + smartyFormat(js
, key
);
385 // FIXME: This is not totally correct cli syntax
386 q
.drush
+= key
+ '=' + js
+ ' ';
387 q
.wpcli
+= key
+ '=' + js
+ ' ';
394 q
.json
+= ").done(function(result) {\n // do something\n});";
395 q
.smarty
+= "}\n{foreach from=$result.values item=" + entity
.toLowerCase() + "}\n {$" + entity
.toLowerCase() + ".some_field}\n{/foreach}";
396 if (action
.indexOf('get') < 0) {
397 q
.smarty
= '{* Smarty API only works with get actions *}';
398 } else if (smartyStub
) {
399 q
.smarty
= "{* Smarty does not have a syntax for array literals; assign complex variables from php *}\n" + q
.smarty
;
401 $.each(q
, function(type
, val
) {
402 $('#api-' + type
).removeClass('prettyprinted').text(val
);
409 if (!entity
|| !action
) {
410 alert(ts('Select an entity.'));
413 if (action
.indexOf('get') < 0 && action
!= 'check') {
414 var msg
= action
=== 'delete' ? ts('This will delete data from CiviCRM. Are you sure?') : ts('This will write to the database. Continue?');
415 CRM
.confirm({title
: ts('Confirm %1', {1: action
}), message
: msg
}).on('crmConfirm:yes', execute
);
422 * Execute api call and display the results
423 * Note: We have to manually execute the ajax in order to add the secret extra "prettyprint" param
426 $('#api-result').html('<div class="crm-loading-element"></div>');
428 url
: CRM
.url('civicrm/ajax/rest'),
433 json
: JSON
.stringify(params
)
435 type
: action
.indexOf('get') < 0 ? 'POST' : 'GET',
437 }).done(function(text
) {
438 $('#api-result').addClass('prettyprint').removeClass('prettyprinted').text(text
);
443 $(document
).ready(function() {
444 $('form#api-explorer')
445 .on('change', '#api-entity, #api-action', function() {
446 entity
= $('#api-entity').val();
447 if ($(this).is('#api-entity')) {
450 action
= $('#api-action').val();
451 if (entity
&& action
) {
452 $('#api-params').html('<tr><td colspan="4" class="crm-loading-element"></td></tr>');
453 $('#api-params-table thead').show();
457 $('#api-params, #api-generated pre').empty();
458 $('#api-param-buttons, #api-params-table thead').hide();
461 .on('change keyup', 'input.api-input, #api-params select', buildParams
)
462 .on('submit', submit
);
464 .on('change', 'input.api-param-name, select.api-param-op', renderValueField
)
465 .on('change', 'input.api-param-name, .api-option-name', function() {
466 if ($(this).val() === '-') {
467 $(this).select2('destroy');
468 $(this).val('').focus();
471 .on('click', '.api-param-remove', function(e
) {
473 $(this).closest('tr').remove();
476 $('#api-params-add').on('click', function(e
) {
480 $('#api-option-add').on('click', function(e
) {
484 $('#api-chain-add').on('click', function(e
) {
488 $('#api-entity').change();