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