From 9cea36192dc55d10f7e23ef366889a8afbbec942 Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Thu, 9 Jan 2020 11:44:39 -0500 Subject: [PATCH] Improve civicrm_api4 docblock and use it for help display in Explorer --- CRM/Api4/Page/Api4Explorer.php | 2 ++ Civi/Api4/Utils/ReflectionUtils.php | 27 +++++++++++++++++----- ang/api4Explorer/Explorer.html | 19 +++++++++++----- ang/api4Explorer/Explorer.js | 11 +++++---- api/api.php | 35 ++++++++++++++++++++++------- css/api4-explorer.css | 7 +++++- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/CRM/Api4/Page/Api4Explorer.php b/CRM/Api4/Page/Api4Explorer.php index ec5718c657..65af14d147 100644 --- a/CRM/Api4/Page/Api4Explorer.php +++ b/CRM/Api4/Page/Api4Explorer.php @@ -20,11 +20,13 @@ 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) diff --git a/Civi/Api4/Utils/ReflectionUtils.php b/Civi/Api4/Utils/ReflectionUtils.php index aa125662bb..58b3aab951 100644 --- a/Civi/Api4/Utils/ReflectionUtils.php +++ b/Civi/Api4/Utils/ReflectionUtils.php @@ -63,27 +63,44 @@ class ReflectionUtils { */ 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; } diff --git a/ang/api4Explorer/Explorer.html b/ang/api4Explorer/Explorer.html index 24326c6956..d016b467de 100644 --- a/ang/api4Explorer/Explorer.html +++ b/ang/api4Explorer/Explorer.html @@ -2,7 +2,7 @@

- {{ ts('CiviCRM API v4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }} + {{ ts('CiviCRM APIv4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }}

@@ -18,9 +18,13 @@
- - - + + + + + + +
@@ -110,7 +114,12 @@

{{ helpContent.description }}

-

{{ text }}

+
+

{{ text }}

+
    +
  • {{ item.substr(1) }}
  • +
+

{{ key }}: {{ item }} diff --git a/ang/api4Explorer/Explorer.js b/ang/api4Explorer/Explorer.js index a37c12ad60..2bfc82c9e4 100644 --- a/ang/api4Explorer/Explorer.js +++ b/ang/api4Explorer/Explorer.js @@ -31,6 +31,7 @@ $scope.index = ''; var getMetaParams = {}, objectParams = {orderBy: 'ASC', values: '', chain: ['Entity', '', '{}']}, + docs = CRM.vars.api4.docs, helpTitle = '', helpContent = {}; $scope.helpTitle = ''; @@ -550,8 +551,8 @@ } 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(); @@ -582,10 +583,8 @@ } }); - $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); diff --git a/api/api.php b/api/api.php index b1da77ed72..cccad8b017 100644 --- a/api/api.php +++ b/api/api.php @@ -24,15 +24,34 @@ function civicrm_api(string $entity = NULL, string $action, array $params, $extr } /** - * 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 diff --git a/css/api4-explorer.css b/css/api4-explorer.css index 3ce3f37d9a..9451bc26d6 100644 --- a/css/api4-explorer.css +++ b/css/api4-explorer.css @@ -22,7 +22,7 @@ #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; @@ -49,6 +49,11 @@ 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; } -- 2.25.1