Merge pull request #3066 from relldoesphp/CRM-14466
[civicrm-core.git] / templates / CRM / Admin / Page / APIExplorer.js
index 444453fb4a26d4123d60f7ee576b7bb828aa1e82..36fe3ab2a9848b16f1065e27e5e0c448a521390e 100644 (file)
@@ -2,34 +2,75 @@
   var
     entity,
     action,
+    actions = ['get'],
     fields = [],
     options = {},
     params = {},
+    smartyStub,
     fieldTpl = _.template($('#api-param-tpl').html()),
+    optionsTpl = _.template($('#api-options-tpl').html()),
+    returnTpl = _.template($('#api-return-tpl').html()),
     chainTpl = _.template($('#api-chain-tpl').html());
 
+  /**
+   * Call prettyPrint function if it successfully loaded from the cdn
+   */
   function prettyPrint() {
     if (window.prettyPrint) {
       window.prettyPrint();
     }
   }
 
+  /**
+   * Add a "fields" row
+   * @param name
+   */
   function addField(name) {
     $('#api-params').append($(fieldTpl({name: name || ''})));
     var $row = $('tr:last-child', '#api-params');
-    $('.api-param-name', $row).crmSelect2({data: fields}).change();
+    $('.api-param-name', $row).crmSelect2({
+      data: fields.concat({id: '-', text: ts('Other') + '...'})
+    }).change();
   }
 
+  /**
+   * Add a new "options" row
+   * @param name
+   */
+  function addOptionField(name) {
+    if ($('.api-options-row', '#api-params').length) {
+      $('.api-options-row:last', '#api-params').after($(optionsTpl({})));
+    } else {
+      $('#api-params').append($(optionsTpl({})));
+    }
+    var $row = $('.api-options-row:last', '#api-params');
+    $('.api-option-name', $row).crmSelect2({data: [
+      {id: 'limit', text: 'limit'},
+      {id: 'offset', text: 'offset'},
+      {id: 'sort', text: 'sort'},
+      {id: 'metadata', text: 'metadata'},
+      {id: '-', text: ts('Other') + '...'}
+    ]});
+  }
+
+  /**
+   * Add an "api chain" row
+   */
   function addChainField() {
     $('#api-params').append($(chainTpl({})));
     var $row = $('tr:last-child', '#api-params');
     $('.api-chain-entity', $row).crmSelect2({
       formatSelection: function(item) {
         return '<span class="icon ui-icon-link"></span> API ' + item.text;
-      }
+      },
+      placeholder: '<span class="icon ui-icon-link"></span> ' + ts('Entity'),
+      escapeMarkup: function(m) {return m}
     });
   }
 
+  /**
+   * Fetch fields for entity+action
+   */
   function getFields() {
     var required = [];
     fields = [];
         }
       });
       showFields(required);
+      if (action === 'get' || action === 'getsingle') {
+        showReturn();
+      }
     });
   }
 
-  function showFields(required) {
-    fields.push({
-      id: '-',
-      text: ts('Other') + '...'
+  /**
+   * For "get" actions show the "return" options
+   */
+  function showReturn() {
+    $('#api-params').prepend($(returnTpl({})));
+    console.log($(returnTpl({})));
+    $('#api-return-value').crmSelect2({data: fields, multiple: true});
+  }
+
+  /**
+   * Fetch actions for entity
+   */
+  function getActions() {
+    if (entity) {
+      CRM.api3(entity, 'getactions').done(function(data) {
+        // Ensure 'get' is always an action
+        actions = _.union(['get'], data.values);
+        populateActions();
+      });
+    } else {
+      actions = ['get'];
+      populateActions();
+    }
+  }
+
+  /**
+   * Called after getActions to populate action list
+   * @param el
+   */
+  function populateActions(el) {
+    $('#api-action').select2({
+      data: _.transform(actions, function(ret, item) {ret.push({text: item, id: item})})
     });
+  }
+
+  /**
+   * Called after getfields to show buttons and required fields
+   * @param required
+   */
+  function showFields(required) {
     $('#api-params').empty();
     $('#api-param-buttons').show();
     if (required.length) {
     }
   }
 
+  /**
+   * Add/remove option list for selected field's pseudoconstant
+   */
   function toggleOptions() {
     var name = $(this).val(),
       $valField = $(this).closest('tr').find('.api-param-value');
     else if ($valField.data('select2')) {
       $valField.select2('destroy');
     }
-    if (name === '-') {
-      $(this).select2('destroy');
-      $(this).val('').focus();
-    }
   }
 
   /**
     var ret = '';
     if ($.isPlainObject(val)) {
       $.each(val, function(k, v) {
-        ret += (ret ? ',' : '') + "'" + k + "' => " + phpFormat(v);
+        ret += (ret ? ', ' : '') + "'" + k + "' => " + phpFormat(v);
       });
       return 'array(' + ret + ')';
     }
    */
   function smartyFormat(js, key) {
     if (js.indexOf('[') > -1 || js.indexOf('{') > -1) {
+      smartyStub = true;
       return '$' + key.replace(/[. -]/g, '_');
     }
     return js;
   }
 
+  /**
+   * Create the params array from user input
+   * @param e
+   */
   function buildParams(e) {
     params = {};
     $('.api-param-checkbox:checked').each(function() {
       params[this.name] = 1;
     });
-    $('input.api-param-value').each(function() {
+    $('input.api-param-value, input.api-option-value').each(function() {
       var $row = $(this).closest('tr'),
         val = evaluate($(this).val(), $(this).is('.select2-offscreen')),
         name = $('input.api-param-name', $row).val(),
         op = $('select.api-param-op', $row).val() || '=';
+
+      // Ignore blank values for the return field
+      if ($(this).is('#api-return-value') && !val) {
+        return;
+      }
       // Special syntax for api chaining
       if (!name && $('select.api-chain-entity', $row).val()) {
         name = 'api.' + $('select.api-chain-entity', $row).val() + '.' + $('select.api-chain-action', $row).val();
       }
+      // Special handling for options
+      if ($(this).is('.api-option-value')) {
+        op = $('input.api-option-name', $row).val();
+        if (op) {
+          name = 'options';
+        }
+      }
       if (name && val !== undefined) {
-        params[name] = op === '=' ? val : {};
+        params[name] = op === '=' ? val : (params[name] || {});
         if (op !== '=') {
           params[name][op] = val;
         }
     if (!$(el).hasClass('crm-error')) {
       $(el)
         .addClass('crm-error')
+        .css('width', '82%')
         .attr('title', ts('Syntax error'))
         .before('<div class="icon red-icon ui-icon-alert"/>');
     }
     $(el)
       .removeClass('crm-error')
       .attr('title', '')
+      .css('width', '85%')
       .siblings('.ui-icon-alert').remove();
   }
 
       json: "CRM.api3('" + entity + "', '" + action + "'",
       rest: CRM.config.resourceBase + "extern/rest.php?entity=" + entity + "&action=" + action + "&json=" + JSON.stringify(params) + "&api_key=yoursitekey&key=yourkey"
     };
+    smartyStub = false;
     $.each(params, function(key, value) {
       var js = JSON.stringify(value);
       if (!i++) {
       }
       q.php += "  '" + key + "' => " + phpFormat(value) + ",\n";
       q.json += "  \"" + key + '": ' + js;
-      // FIXME: How to deal with complex values in smarty?
       q.smarty += ' ' + key + '=' + smartyFormat(js, key);
     });
     if (i) {
     q.smarty += "}\n{foreach from=$result.values item=" + entity.toLowerCase() + "}\n  {$" + entity.toLowerCase() + ".some_field}\n{/foreach}";
     if (action.indexOf('get') < 0) {
       q.smarty = '{* Smarty API only works with get actions *}';
+    } else if (smartyStub) {
+      q.smarty = "{* Smarty does not have a syntax for array literals; assign complex variables on the server *}\n" + q.smarty;
     }
     $.each(q, function(type, val) {
       $('#api-' + type).removeClass('prettyprinted').text(val);
   function submit(e) {
     e.preventDefault();
     if (!entity || !action) {
-      alert(ts('Select an entity & action.'));
+      alert(ts('Select an entity.'));
       return;
     }
     if (action.indexOf('get') < 0) {
       type: action.indexOf('get') < 0 ? 'POST' : 'GET',
       dataType: 'text'
     }).done(function(text) {
-      $('#api-result').addClass('prettyprint linenums').removeClass('prettyprinted').text(text);
+      $('#api-result').addClass('prettyprint').removeClass('prettyprinted').text(text);
       prettyPrint();
     });
   }
     $('form#api-explorer')
       .on('change', '#api-entity, #api-action', function() {
         entity = $('#api-entity').val();
+        if ($(this).is('#api-entity')) {
+          $('#api-action').select2('val', 'get');
+          getActions();
+        }
         action = $('#api-action').val();
         if (entity && action) {
           $('#api-params').html('<tr><td colspan="4" class="crm-loading-element"></td></tr>');
           $('#api-param-buttons, #api-params-table thead').hide();
         }
       })
-      .on('change keyup', 'input.api-param-checkbox, input.api-param-value, input.api-param-name, #api-params select', buildParams)
+      .on('change keyup', 'input.api-input, #api-params select', buildParams)
       .on('submit', submit);
     $('#api-params')
       .on('change', '.api-param-name', toggleOptions)
+      .on('change', '.api-param-name, .api-option-name', function() {
+        if ($(this).val() === '-') {
+          $(this).select2('destroy');
+          $(this).val('').focus();
+        }
+      })
       .on('click', '.api-param-remove', function(e) {
         e.preventDefault();
         $(this).closest('tr').remove();
       e.preventDefault();
       addField();
     });
+    $('#api-option-add').on('click', function(e) {
+      e.preventDefault();
+      addOptionField();
+    });
     $('#api-chain-add').on('click', function(e) {
       e.preventDefault();
       addChainField();