APIv4 Explorer - Optionally view results in PHP format
authorColeman Watts <coleman@civicrm.org>
Mon, 8 Nov 2021 19:03:49 +0000 (14:03 -0500)
committerColeman Watts <coleman@civicrm.org>
Mon, 8 Nov 2021 19:28:23 +0000 (14:28 -0500)
ang/api4Explorer/Explorer.html
ang/api4Explorer/Explorer.js
css/api4-explorer.css

index 43308c15bba7b7c65686ecf7789d2324bad2ca9f..a170e2aa847d36f5ef5e3dab6d095692ff61d6e7 100644 (file)
 <div id="bootstrap-theme" class="api4-explorer-page">
-  <div crm-ui-debug="availableParams"></div>
 
   <h1 crm-page-title>
     {{:: ts('CiviCRM APIv4') }}{{ entity ? (' (' + entity + '::' + action + ')') : '' }}
   </h1>
 
   <div class="api4-explorer-row crm-flex-box">
-      <form name="api4-explorer" class="panel panel-default explorer-params-panel crm-flex-2">
-        <div class="panel-heading">
-          <div class="form-inline">
-            <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, formatResultCssClass: formatResultCssClass}" />
-            </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, formatResultCssClass: formatResultCssClass}" />
-            </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()" ng-mouseenter="help(ts('Execute'), executeDoc())" ng-mouseleave="help()">{{:: ts('Execute') }}</button>
-            <button class="btn btn-primary pull-right" crm-icon="fa-save" ng-show="perm.editGroups && action === 'get'" ng-click="save()" ng-mouseenter="help(ts('Save smart group'), saveDoc())" ng-mouseleave="help()">{{:: ts('Save...') }}</button>
-          </div>
+    <form name="api4-explorer" class="panel panel-default explorer-params-panel crm-flex-2">
+      <div class="panel-heading">
+        <div class="form-inline">
+          <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, formatResultCssClass: formatResultCssClass}" />
+          </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, formatResultCssClass: formatResultCssClass}" />
+          </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()" ng-mouseenter="help(ts('Execute'), executeDoc())" ng-mouseleave="help()">{{:: ts('Execute') }}</button>
+          <button class="btn btn-primary pull-right" crm-icon="fa-save" ng-show="perm.editGroups && action === 'get'" ng-click="save()" ng-mouseenter="help(ts('Save smart group'), saveDoc())" ng-mouseleave="help()">{{:: ts('Save...') }}</button>
         </div>
-        <div class="panel-body">
-          <div class="api4-input form-inline">
-            <div class="checkbox-inline form-control" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['bool'], false)">
-              <label>
-                <input type="checkbox" id="api4-param-{{:: name }}" ng-model="params[name]"/>
-                <span>{{:: name }}</span><span class="crm-marker" ng-if="::param.required"> *</span>
-              </label>
-            </div>
-            <div class="checkbox-inline form-control" ng-mouseenter="help('selectRowCount', availableParams.select)" ng-mouseleave="help()" ng-if="::availableParams.select">
-              <label>
-                <input type="checkbox" id="api4-param-selectRowCount" ng-checked="isSelectRowCount()" ng-click="selectRowCount()" />
-                <span>SelectRowCount</span>
-              </label>
-            </div>
-          </div>
-          <div class="api4-input form-inline" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['bool'], true)">
-            <label>{{ name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
-            <label class="radio-inline">
-              <input type="radio" ng-model="params[name]" ng-value="true" />true
+      </div>
+      <div class="panel-body">
+        <div class="api4-input form-inline">
+          <div class="checkbox-inline form-control" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['bool'], false)">
+            <label>
+              <input type="checkbox" id="api4-param-{{:: name }}" ng-model="params[name]"/>
+              <span>{{:: name }}</span><span class="crm-marker" ng-if="::param.required"> *</span>
             </label>
-            <label class="radio-inline">
-              <input type="radio" ng-model="params[name]" ng-value="false" />false
+          </div>
+          <div class="checkbox-inline form-control" ng-mouseenter="help('selectRowCount', availableParams.select)" ng-mouseleave="help()" ng-if="::availableParams.select">
+            <label>
+              <input type="checkbox" id="api4-param-selectRowCount" ng-checked="isSelectRowCount()" ng-click="selectRowCount()" />
+              <span>SelectRowCount</span>
             </label>
-            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="params[name] !== null"><i class="crm-i fa-times" aria-hidden="true"></i></a>
-          </div>
-          <fieldset class="api4-input form-inline" ng-mouseenter="help('select', availableParams.select)" ng-mouseleave="help()" ng-if="availableParams.select">
-            <legend>select<span class="crm-marker" ng-if="::availableParams.select.required"> *</span></legend>
-            <div ng-model="params.select" ui-sortable="{axis: 'y'}">
-              <div class="api4-input form-inline" ng-repeat="item in params.select track by $index" ng-show="item !== 'row_count'">
-                <i class="crm-i fa-arrows" aria-hidden="true"></i>
-                <input class="form-control huge" type="text" ng-model="params.select[$index]" />
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('select', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
-              </div>
-            </div>
-            <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control huge" ng-model="controls.select" crm-ui-select="{data: fieldsAndJoinsAndFunctionsAndWildcards}" placeholder="Add select" />
-            </div>
-          </fieldset>
-          <fieldset id="api4-join-fieldset" class="api4-input form-inline" ng-mouseenter="help('join', availableParams.join)" ng-mouseleave="help()" ng-if="::availableParams.join">
-            <legend>join<span class="crm-marker" ng-if="::availableParams.join.required"> *</span></legend>
-            <div ng-model="params.join" ui-sortable="{axis: 'y', containment: '#api4-join-fieldset'}">
-              <fieldset ng-repeat="item in params.join track by $index">
-                <div class="api4-input form-inline">
-                  <i class="crm-i fa-arrows"></i>
-                  <input class="form-control twenty" type="text" ng-model="params.join[$index][0]" ng-model-options="{updateOn: 'blur'}" ng-change="$ctrl.buildFieldList()"/>
-                  <label>{{:: ts('Required:') }}</label>
-                  <select class="form-control" ng-model="params.join[$index][1]" ng-options="o.k as o.v for o in ::joinTypes" ></select>
-                  <label>{{:: ts('Using:') }}</label>
-                  <select class="form-control" ng-model="params.join[$index][2]" ng-options="e.name as e.name for e in ::bridgeEntities" ng-change="$ctrl.buildFieldList()">
-                    <option value="">{{:: ts('- none -') }}</option>
-                  </select>
-                  <a href class="crm-hover-button" title="Clear" ng-click="clearParam('join', $index)"><i class="crm-i fa-times"></i></a>
-                </div>
-                <fieldset class="api4-clause-fieldset">
-                  <crm-api4-clause clauses="params.join[$index]" format="plain" skip="3" op="AND" label="On" fields="fieldsAndJoins" ></crm-api4-clause>
-                </fieldset>
-              </fieldset>
-            </div>
-            <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control huge" ng-model="controls.join" crm-ui-select="{data: entities}" placeholder="Add join" />
-            </div>
-          </fieldset>
-          <div class="api4-input form-inline" ng-mouseenter="help('fields', availableParams.fields)" ng-mouseleave="help()" ng-if="::availableParams.fields">
-            <label for="api4-param-fields">fields<span class="crm-marker" ng-if="::availableParams.fields.required"> *</span></label>
-            <input class="form-control" ng-list crm-ui-select="::{data: fields, multiple: true}" id="api4-param-fields" ng-model="params.fields" style="width: 85%;"/>
-          </div>
-          <div class="api4-input form-inline" ng-mouseenter="help('action', availableParams.action)" ng-mouseleave="help()"ng-if="::availableParams.action">
-            <label for="api4-param-action">action<span class="crm-marker" ng-if="::availableParams.action.required"> *</span></label>
-            <input class="form-control" crm-ui-select="{data: actions, allowClear: true, placeholder: 'None'}" id="api4-param-action" ng-model="params.action"/>
-          </div>
-          <div class="api4-input form-inline" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['string', 'int'])">
-            <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
-            <input class="form-control" ng-if="::!param.options" type="{{:: param.type[0] === 'int' && param.type.length === 1 ? 'number' : 'text' }}" id="api4-param-{{:: name }}" ng-model="params[name]"/>
-            <select class="form-control" ng-if="::param.options" ng-options="o for o in ::param.options" id="api4-param-{{:: name }}" ng-model="params[name]"></select>
-            <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="!!params[name]"><i class="crm-i fa-times" aria-hidden="true"></i></a>
-          </div>
-          <div class="api4-input" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['array', 'mixed'])">
-            <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
-            <textarea class="form-control" ng-if="::!param.options" id="api4-param-{{:: name }}" ng-model="params[name]">
-            </textarea>
-            <select multiple ng-if="::param.options" crm-ui-select class="form-control" id="api4-param-{{:: name }}" ng-model="params[name]">
-              <option ng-repeat="opt in param.options" value="{{ opt }}">{{ opt }}</option>
-            </select>
-          </div>
-          <fieldset ng-if="::availableParams.where" class="api4-clause-fieldset" ng-mouseenter="help('where', availableParams.where)" ng-mouseleave="help()">
-            <crm-api4-clause clauses="params.where" is-required="availableParams.where.required" op="AND" label="Where" fields="fieldsAndJoins" ></crm-api4-clause>
-          </fieldset>
-          <fieldset ng-repeat="name in ['values', 'defaults']" ng-if="::availableParams[name]" ng-mouseenter="help(name, availableParams[name])" ng-mouseleave="help()">
-            <legend>{{:: name }}<span class="crm-marker" ng-if="::availableParams[name].required"> *</span></legend>
-            <div class="api4-input form-inline" ng-repeat="clause in params[name]" ng-mouseenter="help('value: ' + clause[0], fieldHelp(clause[0]))" ng-mouseleave="help(name, availableParams[name])">
-              <input class="collapsible-optgroups form-control twenty" ng-model="clause[0]" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: fieldList(name), allowClear: true, placeholder: 'Field'}" />
-              <input class="form-control" ng-model="clause[1]" api4-exp-value="{field: clause[0], action: action === 'getFields' ? params.action || 'get' : action}" />
-            </div>
-            <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control twenty" ng-model="controls[name]" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: fieldList(name), placeholder: ts('Add %1', {1: name.slice(0, -1)})}"/>
-            </div>
-          </fieldset>
-          <fieldset ng-if="::availableParams.groupBy" ng-mouseenter="help('groupBy', availableParams.groupBy)" ng-mouseleave="help()">
-            <legend>groupBy<span class="crm-marker" ng-if="::availableParams.groupBy.required"> *</span></legend>
-            <div ng-model="params.groupBy" ui-sortable="{axis: 'y'}">
-              <div class="api4-input form-inline" ng-repeat="item in params.groupBy track by $index">
-                <i class="crm-i fa-arrows" aria-hidden="true"></i>
-                <input class="form-control huge" type="text" ng-model="params.groupBy[$index]" />
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('groupBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
-              </div>
-            </div>
-            <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control huge" ng-model="controls.groupBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add groupBy" />
+          </div>
+        </div>
+        <div class="api4-input form-inline" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['bool'], true)">
+          <label>{{ name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
+          <label class="radio-inline">
+            <input type="radio" ng-model="params[name]" ng-value="true" />true
+          </label>
+          <label class="radio-inline">
+            <input type="radio" ng-model="params[name]" ng-value="false" />false
+          </label>
+          <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="params[name] !== null"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+        </div>
+        <fieldset class="api4-input form-inline" ng-mouseenter="help('select', availableParams.select)" ng-mouseleave="help()" ng-if="availableParams.select">
+          <legend>select<span class="crm-marker" ng-if="::availableParams.select.required"> *</span></legend>
+          <div ng-model="params.select" ui-sortable="{axis: 'y'}">
+            <div class="api4-input form-inline" ng-repeat="item in params.select track by $index" ng-show="item !== 'row_count'">
+              <i class="crm-i fa-arrows" aria-hidden="true"></i>
+              <input class="form-control huge" type="text" ng-model="params.select[$index]" />
+              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('select', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
             </div>
-          </fieldset>
-          <fieldset ng-if="::availableParams.having" class="api4-clause-fieldset" ng-mouseenter="help('having', availableParams.having)" ng-mouseleave="help()">
-            <crm-api4-clause clauses="params.having" is-required="availableParams.having.required" op="AND" label="Having" fields="havingOptions" ></crm-api4-clause>
-          </fieldset>
-          <fieldset ng-if="::availableParams.orderBy" ng-mouseenter="help('orderBy', availableParams.orderBy)" ng-mouseleave="help()">
-            <legend>orderBy<span class="crm-marker" ng-if="::availableParams.orderBy.required"> *</span></legend>
-            <div ng-model="params.orderBy" ui-sortable="{axis: 'y'}">
-              <div class="api4-input form-inline" ng-repeat="clause in params.orderBy">
-                <i class="crm-i fa-arrows" aria-hidden="true"></i>
-                <input class="form-control huge" type="text" ng-model="clause[0]" />
-                <select class="form-control" ng-model="clause[1]">
-                  <option value="ASC">ASC</option>
-                  <option value="DESC">DESC</option>
+          </div>
+          <div class="api4-input form-inline">
+            <input class="collapsible-optgroups form-control huge" ng-model="controls.select" crm-ui-select="{data: fieldsAndJoinsAndFunctionsAndWildcards}" placeholder="Add select" />
+          </div>
+        </fieldset>
+        <fieldset id="api4-join-fieldset" class="api4-input form-inline" ng-mouseenter="help('join', availableParams.join)" ng-mouseleave="help()" ng-if="::availableParams.join">
+          <legend>join<span class="crm-marker" ng-if="::availableParams.join.required"> *</span></legend>
+          <div ng-model="params.join" ui-sortable="{axis: 'y', containment: '#api4-join-fieldset'}">
+            <fieldset ng-repeat="item in params.join track by $index">
+              <div class="api4-input form-inline">
+                <i class="crm-i fa-arrows"></i>
+                <input class="form-control twenty" type="text" ng-model="params.join[$index][0]" ng-model-options="{updateOn: 'blur'}" ng-change="$ctrl.buildFieldList()"/>
+                <label>{{:: ts('Required:') }}</label>
+                <select class="form-control" ng-model="params.join[$index][1]" ng-options="o.k as o.v for o in ::joinTypes" ></select>
+                <label>{{:: ts('Using:') }}</label>
+                <select class="form-control" ng-model="params.join[$index][2]" ng-options="e.name as e.name for e in ::bridgeEntities" ng-change="$ctrl.buildFieldList()">
+                  <option value="">{{:: ts('- none -') }}</option>
                 </select>
-                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('orderBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+                <a href class="crm-hover-button" title="Clear" ng-click="clearParam('join', $index)"><i class="crm-i fa-times"></i></a>
               </div>
-            </div>
-            <div class="api4-input form-inline">
-              <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctionsWithSuffixes}" placeholder="Add orderBy" />
-            </div>
-          </fieldset>
-          <fieldset ng-if="::availableParams.limit && availableParams.offset">
-            <div class="api4-input form-inline">
-              <span ng-mouseenter="help('limit', availableParams.limit)" ng-mouseleave="help()">
-                <label for="api4-param-limit">limit<span class="crm-marker" ng-if="::availableParams.limit.required"> *</span></label>
-                <input class="form-control" type="number" min="0" id="api4-param-limit" ng-model="params.limit"/>
-              </span>
-              <span ng-mouseenter="help('offset', availableParams.offset)" ng-mouseleave="help()">
-                <label for="api4-param-offset">offset<span class="crm-marker" ng-if="::availableParams.offset.required"> *</span></label>
-                <input class="form-control" type="number" min="0" id="api4-param-offset" ng-model="params.offset"/>
-              </span>
-              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('limit');clearParam('offset');" ng-show="!!params.limit || !!params.offset"><i class="crm-i fa-times" aria-hidden="true"></i></a>
-            </div>
-          </fieldset>
-          <fieldset ng-if="::availableParams.chain" ng-mouseenter="help('chain', availableParams.chain)" ng-mouseleave="help()">
-            <legend>chain</legend>
-            <div class="api4-input form-inline" ng-repeat="clause in params.chain" api4-exp-chain="clause" entities="::entities" main-entity="::entity" >
-            </div>
-            <div class="api4-input form-inline">
-              <input class="form-control" ng-model="controls.chain" crm-ui-select="::{data: entities}" placeholder="Add chain" />
-            </div>
-          </fieldset>
+              <fieldset class="api4-clause-fieldset">
+                <crm-api4-clause clauses="params.join[$index]" format="plain" skip="3" op="AND" label="On" fields="fieldsAndJoins" ></crm-api4-clause>
+              </fieldset>
+            </fieldset>
+          </div>
+          <div class="api4-input form-inline">
+            <input class="collapsible-optgroups form-control huge" ng-model="controls.join" crm-ui-select="{data: entities}" placeholder="Add join" />
+          </div>
+        </fieldset>
+        <div class="api4-input form-inline" ng-mouseenter="help('fields', availableParams.fields)" ng-mouseleave="help()" ng-if="::availableParams.fields">
+          <label for="api4-param-fields">fields<span class="crm-marker" ng-if="::availableParams.fields.required"> *</span></label>
+          <input class="form-control" ng-list crm-ui-select="::{data: fields, multiple: true}" id="api4-param-fields" ng-model="params.fields" style="width: 85%;"/>
         </div>
-      </form>
-      <div class="panel panel-info explorer-help-panel">
-        <div class="panel-heading">
-          <h3 class="panel-title" crm-icon="fa-info-circle">{{ helpTitle }}</h3>
+        <div class="api4-input form-inline" ng-mouseenter="help('action', availableParams.action)" ng-mouseleave="help()"ng-if="::availableParams.action">
+          <label for="api4-param-action">action<span class="crm-marker" ng-if="::availableParams.action.required"> *</span></label>
+          <input class="form-control" crm-ui-select="{data: actions, allowClear: true, placeholder: 'None'}" id="api4-param-action" ng-model="params.action"/>
         </div>
-        <div class="panel-body">
-          <h4 ng-bind-html="helpContent.description"></h4>
-          <div ng-bind-html="helpContent.comment"></div>
-          <p ng-repeat="(key, item) in helpContent" ng-if="key !== 'description' && key !== 'comment' && key !== 'see'">
-            <strong>{{ key }}:</strong> {{ item }}
-          </p>
-          <div ng-if="helpContent.see">
-            <strong>See:</strong>
-            <ul>
-              <li ng-repeat="ref in helpContent.see" ng-bind-html="ref"> </li>
-            </ul>
+        <div class="api4-input form-inline" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['string', 'int'])">
+          <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
+          <input class="form-control" ng-if="::!param.options" type="{{:: param.type[0] === 'int' && param.type.length === 1 ? 'number' : 'text' }}" id="api4-param-{{:: name }}" ng-model="params[name]"/>
+          <select class="form-control" ng-if="::param.options" ng-options="o for o in ::param.options" id="api4-param-{{:: name }}" ng-model="params[name]"></select>
+          <a href class="crm-hover-button" title="Clear" ng-click="clearParam(name)" ng-show="!!params[name]"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+        </div>
+        <div class="api4-input" ng-mouseenter="help(name, param)" ng-mouseleave="help()" ng-repeat="(name, param) in ::getGenericParams(['array', 'mixed'])">
+          <label for="api4-param-{{:: name }}">{{:: name }}<span class="crm-marker" ng-if="::param.required"> *</span></label>
+          <textarea class="form-control" ng-if="::!param.options" id="api4-param-{{:: name }}" ng-model="params[name]">
+          </textarea>
+          <select multiple ng-if="::param.options" crm-ui-select class="form-control" id="api4-param-{{:: name }}" ng-model="params[name]">
+            <option ng-repeat="opt in param.options" value="{{ opt }}">{{ opt }}</option>
+          </select>
+        </div>
+        <fieldset ng-if="::availableParams.where" class="api4-clause-fieldset" ng-mouseenter="help('where', availableParams.where)" ng-mouseleave="help()">
+          <crm-api4-clause clauses="params.where" is-required="availableParams.where.required" op="AND" label="Where" fields="fieldsAndJoins" ></crm-api4-clause>
+        </fieldset>
+        <fieldset ng-repeat="name in ['values', 'defaults']" ng-if="::availableParams[name]" ng-mouseenter="help(name, availableParams[name])" ng-mouseleave="help()">
+          <legend>{{:: name }}<span class="crm-marker" ng-if="::availableParams[name].required"> *</span></legend>
+          <div class="api4-input form-inline" ng-repeat="clause in params[name]" ng-mouseenter="help('value: ' + clause[0], fieldHelp(clause[0]))" ng-mouseleave="help(name, availableParams[name])">
+            <input class="collapsible-optgroups form-control twenty" ng-model="clause[0]" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: fieldList(name), allowClear: true, placeholder: 'Field'}" />
+            <input class="form-control" ng-model="clause[1]" api4-exp-value="{field: clause[0], action: action === 'getFields' ? params.action || 'get' : action}" />
+          </div>
+          <div class="api4-input form-inline">
+            <input class="collapsible-optgroups form-control twenty" ng-model="controls[name]" crm-ui-select="{formatResult: formatSelect2Item, formatSelection: formatSelect2Item, data: fieldList(name), placeholder: ts('Add %1', {1: name.slice(0, -1)})}"/>
+          </div>
+        </fieldset>
+        <fieldset ng-if="::availableParams.groupBy" ng-mouseenter="help('groupBy', availableParams.groupBy)" ng-mouseleave="help()">
+          <legend>groupBy<span class="crm-marker" ng-if="::availableParams.groupBy.required"> *</span></legend>
+          <div ng-model="params.groupBy" ui-sortable="{axis: 'y'}">
+            <div class="api4-input form-inline" ng-repeat="item in params.groupBy track by $index">
+              <i class="crm-i fa-arrows" aria-hidden="true"></i>
+              <input class="form-control huge" type="text" ng-model="params.groupBy[$index]" />
+              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('groupBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+            </div>
+          </div>
+          <div class="api4-input form-inline">
+            <input class="collapsible-optgroups form-control huge" ng-model="controls.groupBy" crm-ui-select="{data: fieldsAndJoinsAndFunctions}" placeholder="Add groupBy" />
+          </div>
+        </fieldset>
+        <fieldset ng-if="::availableParams.having" class="api4-clause-fieldset" ng-mouseenter="help('having', availableParams.having)" ng-mouseleave="help()">
+          <crm-api4-clause clauses="params.having" is-required="availableParams.having.required" op="AND" label="Having" fields="havingOptions" ></crm-api4-clause>
+        </fieldset>
+        <fieldset ng-if="::availableParams.orderBy" ng-mouseenter="help('orderBy', availableParams.orderBy)" ng-mouseleave="help()">
+          <legend>orderBy<span class="crm-marker" ng-if="::availableParams.orderBy.required"> *</span></legend>
+          <div ng-model="params.orderBy" ui-sortable="{axis: 'y'}">
+            <div class="api4-input form-inline" ng-repeat="clause in params.orderBy">
+              <i class="crm-i fa-arrows" aria-hidden="true"></i>
+              <input class="form-control huge" type="text" ng-model="clause[0]" />
+              <select class="form-control" ng-model="clause[1]">
+                <option value="ASC">ASC</option>
+                <option value="DESC">DESC</option>
+              </select>
+              <a href class="crm-hover-button" title="Clear" ng-click="clearParam('orderBy', $index)"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+            </div>
+          </div>
+          <div class="api4-input form-inline">
+            <input class="collapsible-optgroups form-control huge" ng-model="controls.orderBy" crm-ui-select="{data: fieldsAndJoinsAndFunctionsWithSuffixes}" placeholder="Add orderBy" />
           </div>
+        </fieldset>
+        <fieldset ng-if="::availableParams.limit && availableParams.offset">
+          <div class="api4-input form-inline">
+            <span ng-mouseenter="help('limit', availableParams.limit)" ng-mouseleave="help()">
+              <label for="api4-param-limit">limit<span class="crm-marker" ng-if="::availableParams.limit.required"> *</span></label>
+              <input class="form-control" type="number" min="0" id="api4-param-limit" ng-model="params.limit"/>
+            </span>
+            <span ng-mouseenter="help('offset', availableParams.offset)" ng-mouseleave="help()">
+              <label for="api4-param-offset">offset<span class="crm-marker" ng-if="::availableParams.offset.required"> *</span></label>
+              <input class="form-control" type="number" min="0" id="api4-param-offset" ng-model="params.offset"/>
+            </span>
+            <a href class="crm-hover-button" title="Clear" ng-click="clearParam('limit');clearParam('offset');" ng-show="!!params.limit || !!params.offset"><i class="crm-i fa-times" aria-hidden="true"></i></a>
+          </div>
+        </fieldset>
+        <fieldset ng-if="::availableParams.chain" ng-mouseenter="help('chain', availableParams.chain)" ng-mouseleave="help()">
+          <legend>chain</legend>
+          <div class="api4-input form-inline" ng-repeat="clause in params.chain" api4-exp-chain="clause" entities="::entities" main-entity="::entity" >
+          </div>
+          <div class="api4-input form-inline">
+            <input class="form-control" ng-model="controls.chain" crm-ui-select="::{data: entities}" placeholder="Add chain" />
+          </div>
+        </fieldset>
+      </div>
+    </form>
+    <div class="panel panel-info explorer-help-panel">
+      <div class="panel-heading">
+        <h3 class="panel-title" crm-icon="fa-info-circle">{{ helpTitle }}</h3>
+      </div>
+      <div class="panel-body">
+        <h4 ng-bind-html="helpContent.description"></h4>
+        <div ng-bind-html="helpContent.comment"></div>
+        <p ng-repeat="(key, item) in helpContent" ng-if="key !== 'description' && key !== 'comment' && key !== 'see'">
+          <strong>{{ key }}:</strong> {{ item }}
+        </p>
+        <div ng-if="helpContent.see">
+          <strong>See:</strong>
+          <ul>
+            <li ng-repeat="ref in helpContent.see" ng-bind-html="ref"> </li>
+          </ul>
         </div>
       </div>
+    </div>
   </div>
   <div class="api4-explorer-row crm-flex-box">
-      <div class="panel panel-info explorer-code-panel">
-        <ul class="panel-heading nav nav-tabs">
+    <div class="panel panel-info explorer-code-panel">
+      <div class="panel-heading">
+        <ul class="nav nav-tabs">
           <li role="presentation" ng-repeat="lang in ::langs" ng-class="{active: selectedTab.code === lang}">
             <a href ng-click="selectLang(lang)">
               {{:: lang }}
             </a>
           </li>
         </ul>
-        <div class="panel-body">
-          <div ng-repeat="style in code[selectedTab.code]">
-            <label>{{:: style.label }}</label>
-            <div><pre class="prettyprint" ng-bind-html="style.code"></pre></div>
-          </div>
+      </div>
+      <div class="panel-body">
+        <div ng-repeat="style in code[selectedTab.code]">
+          <label>{{:: style.label }}</label>
+          <div><pre class="prettyprint" ng-bind-html="style.code"></pre></div>
         </div>
       </div>
-      <div class="panel explorer-result-panel panel-{{ status }}" >
-        <ul class="panel-heading nav nav-tabs">
+    </div>
+    <div class="panel explorer-result-panel panel-{{ status }}" >
+      <div class="panel-heading">
+        <div class="form-inline pull-right">
+          <select ng-show="selectedTab.result === 'result'" class="form-control" ng-model="$ctrl.resultFormat" ng-change="$ctrl.formatResult()">
+            <option ng-repeat="fmt in $ctrl.resultFormats" value="{{:: fmt.name }}">
+              {{:: fmt.label }}
+            </option>
+          </select>
+        </div>
+        <ul class="nav nav-tabs">
           <li role="presentation" ng-class="{active: selectedTab.result === 'result'}">
             <a href ng-click="selectedTab.result = 'result'">
               <span ng-switch="status">
             </a>
           </li>
         </ul>
-        <div class="panel-body">
-          <div ng-show="selectedTab.result === 'result'">
-            <pre class="prettyprint" ng-repeat="code in result" ng-bind-html="code"></pre>
-          </div>
-          <div ng-if="::perm.accessDebugOutput" ng-show="selectedTab.result === 'debug'">
-            <pre ng-if="debug" class="prettyprint" ng-bind-html="debug"></pre>
-            <div ng-if="!debug">
-              <p>
-                {{:: ts('To view debugging output, enable the debug param before executing.') }}
-              </p>
-              <p>
-                {{:: ts('Enable backtrace in system settings to see error backtraces.') }}
-              </p>
-            </div>
+      </div>
+      <div class="panel-body">
+        <div ng-show="selectedTab.result === 'result'">
+          <pre class="prettyprint" ng-repeat="code in result" ng-bind-html="code"></pre>
+        </div>
+        <div ng-if="::perm.accessDebugOutput" ng-show="selectedTab.result === 'debug'">
+          <pre ng-if="debug" class="prettyprint" ng-bind-html="debug"></pre>
+          <div ng-if="!debug">
+            <p>
+              {{:: ts('To view debugging output, enable the debug param before executing.') }}
+            </p>
+            <p>
+              {{:: ts('Enable backtrace in system settings to see error backtraces.') }}
+            </p>
           </div>
         </div>
       </div>
+    </div>
   </div>
 </div>
index 0d5404a8f4a189d17863de8968a8b1ce071a8f8f..991bc2f6f293de42dddb67badf0aa65d5a589c89 100644 (file)
@@ -42,6 +42,7 @@
     var getMetaParams = {},
       objectParams = {orderBy: 'ASC', values: '', defaults: '', chain: ['Entity', '', '{}']},
       docs = CRM.vars.api4.docs,
+      response,
       helpTitle = '',
       helpContent = {};
     $scope.helpTitle = '';
         {name: 'pipe', label: ts('CV (pipe)'), code: ''}
       ]
     };
+    this.resultFormat = 'json';
+    this.resultFormats = [
+      {
+        name: 'json',
+        label: ts('View as JSON')
+      },
+      {
+        name: 'php',
+        label: ts('View as PHP')
+      },
+    ];
 
     if (!entities.length) {
       formatForSelect2(schema, entities, 'name', ['description', 'icon']);
         return;
       }
       var info = {
-          description: field.description,
-          type: field.data_type
-        };
+        description: field.description,
+        type: field.data_type
+      };
       if (field.default_value) {
         info.default = field.default_value;
       }
           'X-Requested-With': 'XMLHttpRequest'
         }
       }).then(function(resp) {
-          $scope.loading = false;
-          $scope.status = resp.data && resp.data.debug && resp.data.debug.log ? 'warning' : 'success';
-          $scope.debug = debugFormat(resp.data);
-          $scope.result = [
-            formatMeta(resp.data),
-            prettyPrintOne((_.isArray(resp.data.values) ? '(' + resp.data.values.length + ') ' : '') + _.escape(JSON.stringify(resp.data.values, null, 2)), 'js', 1)
-          ];
-        }, function(resp) {
-          $scope.loading = false;
-          $scope.status = 'danger';
-          $scope.debug = debugFormat(resp.data);
-          $scope.result = [
-            formatMeta(resp),
-            prettyPrintOne(_.escape(JSON.stringify(resp.data, null, 2)))
-          ];
-        });
+        $scope.loading = false;
+        $scope.status = resp.data && resp.data.debug && resp.data.debug.log ? 'warning' : 'success';
+        $scope.debug = debugFormat(resp.data);
+        response = {
+          meta: resp.data,
+          values: resp.data.values
+        };
+        ctrl.formatResult();
+      }, function(resp) {
+        $scope.loading = false;
+        $scope.status = 'danger';
+        $scope.debug = debugFormat(resp.data);
+        response = {
+          meta: resp,
+          values: resp.data
+        };
+        ctrl.formatResult();
+      });
+    };
+
+    ctrl.formatResult = function() {
+      $scope.result = [formatMeta(response.meta)];
+      switch (ctrl.resultFormat) {
+        case 'json':
+          $scope.result.push(prettyPrintOne((_.isArray(response.values) ? '(' + response.values.length + ') ' : '') + _.escape(JSON.stringify(response.values, null, 2)), 'js', 1));
+          break;
+
+        case 'php':
+          $scope.result.push(prettyPrintOne((_.isArray(response.values) ? '(' + response.values.length + ') ' : '') + _.escape(phpFormat(response.values, 2, 2)), 'php', 1));
+          break;
+      }
     };
 
     function debugFormat(data) {
     /**
      * Format value to look like php code
      */
-    function phpFormat(val, indent) {
+    function phpFormat(val, indent, indentChildren) {
       if (typeof val === 'undefined') {
         return '';
       }
       if (val === null || val === true || val === false) {
         return JSON.stringify(val).toUpperCase();
       }
+      var indentChild = indentChildren ? indent + indentChildren : null;
       indent = (typeof indent === 'number') ? _.repeat(' ', indent) : (indent || '');
       var ret = '',
         baseLine = indent ? indent.slice(0, -2) : '',
         newLine = indent ? '\n' : '',
         trailingComma = indent ? ',' : '';
       if ($.isPlainObject(val)) {
+        if ($.isEmptyObject(val)) {
+          return '[]';
+        }
         $.each(val, function(k, v) {
-          ret += (ret ? ', ' : '') + newLine + indent + "'" + k + "' => " + phpFormat(v);
+          ret += (ret ? ', ' : '') + newLine + indent + "'" + k + "' => " + phpFormat(v, indentChild, indentChildren);
         });
         return '[' + ret + trailingComma + newLine + baseLine + ']';
       }
       if ($.isArray(val)) {
+        if (!val.length) {
+          return '[]';
+        }
         $.each(val, function(k, v) {
-          ret += (ret ? ', ' : '') + newLine + indent + phpFormat(v);
+          ret += (ret ? ', ' : '') + newLine + indent + phpFormat(v, indentChild, indentChildren);
         });
         return '[' + ret + trailingComma + newLine + baseLine + ']';
       }
index 02dd6dbadf1b426aa4ea0511b3e4adc35f876d61..b2cfa771cb117bcbdb0272bf452ba5f1f4e7bf82 100644 (file)
   border-bottom-left-radius: 0;
   margin-bottom: 0;
 }
-#bootstrap-theme.api4-explorer-page .panel-heading.nav-tabs {
-  padding: 8px 0 0 20px;
-}
-#bootstrap-theme .panel-heading>li>a {
+#bootstrap-theme .panel-heading li>a {
   background-color: #f1f1f18c
 }
 #bootstrap-theme.api4-explorer-page .explorer-code-panel pre {
@@ -37,7 +34,7 @@
   word-break: break-all;
   white-space: pre-wrap;
 }
-#bootstrap-theme.api4-explorer-page .explorer-code-panel .panel-heading.nav li a {
+#bootstrap-theme.api4-explorer-page .explorer-code-panel .panel-heading>.nav li a {
   text-transform: uppercase;
 }