class CRM_Api4_Page_Api4Explorer extends CRM_Core_Page {
public function run() {
+ $apiDoc = new ReflectionFunction('civicrm_api4');
$vars = [
'operators' => \CRM_Core_DAO::acceptedSQLOperators(),
'basePath' => Civi::resources()->getUrl('civicrm'),
'schema' => (array) \Civi\Api4\Entity::get()->setChain(['fields' => ['$name', 'getFields']])->execute(),
'links' => (array) \Civi\Api4\Entity::getLinks()->execute(),
+ 'docs' => \Civi\Api4\Utils\ReflectionUtils::parseDocBlock($apiDoc->getDocComment()),
];
Civi::resources()
->addVars('api4', $vars)
*/
public static function parseDocBlock($comment) {
$info = [];
+ $param = NULL;
foreach (preg_split("/((\r?\n)|(\r\n?))/", $comment) as $num => $line) {
if (!$num || strpos($line, '*/') !== FALSE) {
continue;
}
- $line = ltrim(trim($line), '* ');
+ $line = preg_replace('/[ ]+/', ' ', ltrim(trim($line), '* '));
if (strpos($line, '@') === 0) {
$words = explode(' ', $line);
- $key = substr($words[0], 1);
+ $key = substr(array_shift($words), 1);
+ $param = NULL;
if ($key == 'var') {
- $info['type'] = explode('|', $words[1]);
+ $info['type'] = explode('|', $words[0]);
}
elseif ($key == 'options') {
- $val = str_replace(', ', ',', implode(' ', array_slice($words, 1)));
+ $val = str_replace(', ', ',', implode(' ', $words));
$info['options'] = explode(',', $val);
}
+ elseif ($key == 'throws') {
+ $info[$key][] = implode(' ', $words);
+ }
+ elseif ($key == 'param' && $words) {
+ $type = $words[0][0] !== '$' ? explode('|', array_shift($words)) : NULL;
+ $param = rtrim(array_shift($words), '-:()/');
+ $info['params'][$param] = [
+ 'type' => $type,
+ 'description' => $words ? ltrim(implode(' ', $words), '-: ') : '',
+ 'comment' => '',
+ ];
+ }
else {
// Unrecognized annotation, but we'll duly add it to the info array
- $val = implode(' ', array_slice($words, 1));
+ $val = implode(' ', $words);
$info[$key] = strlen($val) ? $val : TRUE;
}
}
+ elseif ($param) {
+ $info['params'][$param]['comment'] .= $line . "\n";
+ }
elseif ($num == 1) {
$info['description'] = $line;
}
<div crm-ui-debug="availableParams"></div>
<h1 crm-page-title>
- {{ ts('CiviCRM API v4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }}
+ {{ ts('CiviCRM APIv4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }}
</h1>
<!--This warning will show if bootstrap is unavailable. Normally it will be hidden by the bootstrap .collapse class.-->
<form name="api4-explorer" class="panel panel-default explorer-params-panel">
<div class="panel-heading">
<div class="form-inline">
- <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="{placeholder: ts('Entity'), data: entities}" />
- <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="{placeholder: ts('Action'), data: actions}" />
- <input class="form-control api4-index" type="search" ng-model="index" ng-mouseenter="help('index', indexHelp)" ng-mouseleave="help()" placeholder="{{ ts('Index') }}" />
+ <span ng-mouseenter="help('entity', paramDoc('$entity'))" ng-mouseleave="help()">
+ <input class="collapsible-optgroups form-control" ng-model="entity" ng-disabled="!entities.length" ng-class="{loading: !entities.length}" crm-ui-select="{placeholder: ts('Entity'), data: entities}" />
+ </span>
+ <span ng-mouseenter="help('action', paramDoc('$action'))" ng-mouseleave="help()">
+ <input class="collapsible-optgroups form-control" ng-model="action" ng-disabled="!entity || !actions.length" ng-class="{loading: entity && !actions.length}" crm-ui-select="{placeholder: ts('Action'), data: actions}" />
+ </span>
+ <input class="form-control api4-index" type="search" ng-model="index" ng-mouseenter="help('index', paramDoc('$index'))" ng-mouseleave="help()" placeholder="{{ ts('Index') }}" />
<button class="btn btn-success pull-right" crm-icon="fa-bolt" ng-disabled="!entity || !action || loading" ng-click="execute()">{{ ts('Execute') }}</button>
</div>
</div>
<div class="panel-body">
<h4>{{ helpContent.description }}</h4>
<div ng-if="helpContent.comment">
- <p ng-repeat='text in helpContent.comment.split("\n\n")'>{{ text }}</p>
+ <div ng-repeat='text in helpContent.comment.split("\n\n")'>
+ <p ng-if="text[0] !== '-' && text[0] !== '*'">{{ text }}</p>
+ <ul ng-if="text[0] === '-' || text[0] === '*'">
+ <li ng-repeat='item in text.split("\n")'>{{ item.substr(1) }}</li>
+ </ul>
+ </div>
</div>
<p ng-repeat="(key, item) in helpContent" ng-if="key !== 'description' && key !== 'comment'">
<strong>{{ key }}:</strong> {{ item }}
$scope.index = '';
var getMetaParams = {},
objectParams = {orderBy: 'ASC', values: '', chain: ['Entity', '', '{}']},
+ docs = CRM.vars.api4.docs,
helpTitle = '',
helpContent = {};
$scope.helpTitle = '';
}
if (!$scope.entity) {
- $scope.helpTitle = helpTitle = ts('Help');
- $scope.helpContent = helpContent = {description: ts('Welcome to the api explorer.'), comment: ts('Select an entity to begin.')};
+ $scope.helpTitle = helpTitle = ts('APIv4 Explorer');
+ $scope.helpContent = helpContent = {description: docs.description, comment: docs.comment};
} else if (!actions.length && !getEntity().actions) {
getMetaParams.actions = [$scope.entity, 'getActions', {chain: {fields: [$scope.entity, 'getFields', {action: '$name'}]}}];
fetchMeta();
}
});
- $scope.indexHelp = {
- description: ts('(string|int) Index results or select by index.'),
- comment: ts('Pass a string to index the results by a field value. E.g. index: "name" will return an associative array with names as keys.') + '\n\n' +
- ts('Pass an integer to return a single result; e.g. index: 0 will return the first result, 1 will return the second, and -1 will return the last.')
+ $scope.paramDoc = function(name) {
+ return docs.params[name];
};
$scope.$watch('params', writeCode, true);
}
/**
- * Procedural wrapper for the OO api version 4.
+ * Calls the CiviCRM APIv4 with supplied parameters and returns a Result object.
*
- * @param string $entity
- * @param string $action
- * @param array $params
- * @param string|int|array $index
- * If $index is a string, the results array will be indexed by that key.
- * If $index is an integer, only the result at that index will be returned.
- * $index can also be a single-item array representing key|value pairs to be returned ex ['id' => 'title'].
+ * This API (Application Programming Interface) is used to access and manage data in CiviCRM.
+ *
+ * APIv4 is the latest stable version.
+ *
+ * @see http://example.com/civicrm/api4/explorer
+ *
+ * @param string $entity Name of the CiviCRM entity to access.
+ * All entity names are capitalized CamelCase, e.g. "ContributionPage".
+ * Most entities correspond to a database table (e.g. "Contact" is the table "civicrm_contact").
+ * For a complete list of available entities, call civicrm_api4('Entity', 'get');
+ *
+ * @param string $action The "verb" of the api call.
+ * For a complete list of actions for a given entity (e.g. Contact), call civicrm_api4('Contact', 'getActions');
+ *
+ * @param array $params An array of API input keyed by parameter name.
+ * The easiest way to discover all available parameters is to visit the API Explorer on your CiviCRM site.
+ * The API Explorer is listed in the CiviCRM menu under Support -> Developer.
+ *
+ * @param string|int|array $index Controls the Result array format.
+ * By default the api Result contains a non-associative array of data. Passing an $index tells the api to
+ * automatically reformat the array, depending on the variable type passed:
+ *
+ * - Integer: return a single result array; e.g. index = 0 will return the first result, 1 will return the second, and -1 will return the last.
+ * - String: index the results by a field value; e.g. index = "name" will return an associative array with the field 'name' as keys.
+ * - Non-associative array: return a single value from each result; e.g. index = ['title'] will return a non-associative array of strings - the 'title' field from each result.
+ * - Associative array: a combination of the previous two modes; e.g. index = ['name' => 'title'] will return an array of strings - the 'title' field keyed by the 'name' field.
*
* @return \Civi\Api4\Generic\Result
* @throws \API_Exception
#bootstrap-theme.api4-explorer-page > div > .panel {
flex: 1;
margin: 10px;
- min-height: 400px;
+ min-height: 500px;
}
#bootstrap-theme.api4-explorer-page > div > form.panel {
flex: 2;
word-break: break-word;
}
+/* because each help p is in a div, this undoes bootstrap removing margin from last-child */
+#bootstrap-theme.api4-explorer-page .explorer-help-panel .panel-body p {
+ margin-bottom: 10px;
+}
+
#bootstrap-theme.api4-explorer-page form label {
text-transform: capitalize;
}