1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/dolibarr_ynh.git synced 2024-09-03 18:35:53 +02:00
dolibarr_ynh/sources/dolibarr/htdocs/includes/restler/Resources.php
Laurent Peuch e6008fc691 init
2015-09-28 22:09:38 +02:00

1006 lines
35 KiB
PHP

<?php
namespace Luracast\Restler;
use Luracast\Restler\Data\String;
use Luracast\Restler\Scope;
use stdClass;
/**
* API Class to create Swagger Spec 1.1 compatible id and operation
* listing
*
* @category Framework
* @package Restler
* @author R.Arul Kumaran <arul@luracast.com>
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
*/
class Resources implements iUseAuthentication, iProvideMultiVersionApi
{
/**
* @var bool should protected resources be shown to unauthenticated users?
*/
public static $hideProtected = true;
/**
* @var bool should we use format as extension?
*/
public static $useFormatAsExtension = true;
/**
* @var bool should we include newer apis in the list? works only when
* Defaults::$useUrlBasedVersioning is set to true;
*/
public static $listHigherVersions = true;
/**
* @var array all http methods specified here will be excluded from
* documentation
*/
public static $excludedHttpMethods = array('OPTIONS');
/**
* @var array all paths beginning with any of the following will be excluded
* from documentation
*/
public static $excludedPaths = array();
/**
* @var bool
*/
public static $placeFormatExtensionBeforeDynamicParts = true;
/**
* @var bool should we group all the operations with the same url or not
*/
public static $groupOperations = false;
/**
* @var null|callable if the api methods are under access control mechanism
* you can attach a function here that returns true or false to determine
* visibility of a protected api method. this function will receive method
* info as the only parameter.
*/
public static $accessControlFunction = null;
/**
* @var array type mapping for converting data types to javascript / swagger
*/
public static $dataTypeAlias = array(
'string' => 'string',
'int' => 'int',
'number' => 'float',
'float' => 'float',
'bool' => 'boolean',
'boolean' => 'boolean',
'NULL' => 'null',
'array' => 'Array',
'object' => 'Object',
'stdClass' => 'Object',
'mixed' => 'string',
'DateTime' => 'Date'
);
/**
* @var array configurable symbols to differentiate public, hybrid and
* protected api
*/
public static $apiDescriptionSuffixSymbols = array(
0 => '&nbsp; <i class="icon-unlock-alt icon-large"></i>', //public api
1 => '&nbsp; <i class="icon-adjust icon-large"></i>', //hybrid api
2 => '&nbsp; <i class="icon-lock icon-large"></i>', //protected api
);
/**
* Injected at runtime
*
* @var Restler instance of restler
*/
public $restler;
/**
* @var string when format is not used as the extension this property is
* used to set the extension manually
*/
public $formatString = '';
protected $_models;
protected $_bodyParam;
/**
* @var bool|stdClass
*/
protected $_fullDataRequested = false;
protected $crud = array(
'POST' => 'create',
'GET' => 'retrieve',
'PUT' => 'update',
'DELETE' => 'delete',
'PATCH' => 'partial update'
);
protected static $prefixes = array(
'get' => 'retrieve',
'index' => 'list',
'post' => 'create',
'put' => 'update',
'patch' => 'modify',
'delete' => 'remove',
);
protected $_authenticated = false;
protected $cacheName = '';
public function __construct()
{
if (static::$useFormatAsExtension) {
$this->formatString = '.{format}';
}
}
/**
* This method will be called first for filter classes and api classes so
* that they can respond accordingly for filer method call and api method
* calls
*
*
* @param bool $isAuthenticated passes true when the authentication is
* done, false otherwise
*
* @return mixed
*/
public function __setAuthenticationStatus($isAuthenticated = false)
{
$this->_authenticated = $isAuthenticated;
}
/**
* pre call for get($id)
*
* if cache is present, use cache
*/
public function _pre_get_json($id)
{
$userClass = Defaults::$userIdentifierClass;
$this->cacheName = $userClass::getCacheIdentifier() . '_resources_' . $id;
if ($this->restler->getProductionMode()
&& !$this->restler->refreshCache
&& $this->restler->cache->isCached($this->cacheName)
) {
//by pass call, compose, postCall stages and directly send response
$this->restler->composeHeaders();
die($this->restler->cache->get($this->cacheName));
}
}
/**
* post call for get($id)
*
* create cache if in production mode
*
* @param $responseData
*
* @internal param string $data composed json output
*
* @return string
*/
public function _post_get_json($responseData)
{
if ($this->restler->getProductionMode()) {
$this->restler->cache->set($this->cacheName, $responseData);
}
return $responseData;
}
/**
* @access hybrid
*
* @param string $id
*
* @throws RestException
* @return null|stdClass
*
* @url GET {id}
*/
public function get($id = '')
{
$version = $this->restler->getRequestedApiVersion();
if (empty($id)) {
//do nothing
} elseif (false !== ($pos = strpos($id, '-v'))) {
//$version = intval(substr($id, $pos + 2));
$id = substr($id, 0, $pos);
} elseif ($id{0} == 'v' && is_numeric($v = substr($id, 1))) {
$id = '';
//$version = $v;
} elseif ($id == 'root' || $id == 'index') {
$id = '';
}
$this->_models = new stdClass();
$r = null;
$count = 0;
$tSlash = !empty($id);
$target = empty($id) ? '' : $id;
$tLen = strlen($target);
$filter = array();
$routes
= Util::nestedValue(Routes::toArray(), "v$version")
? : array();
$prefix = Defaults::$useUrlBasedVersioning ? "/v$version" : '';
foreach ($routes as $value) {
foreach ($value as $httpMethod => $route) {
if (in_array($httpMethod, static::$excludedHttpMethods)) {
continue;
}
$fullPath = $route['url'];
if ($fullPath !== $target && !String::beginsWith($fullPath, $target)) {
continue;
}
$fLen = strlen($fullPath);
if ($tSlash) {
if ($fLen != $tLen && !String::beginsWith($fullPath, $target . '/'))
continue;
} elseif ($fLen > $tLen + 1 && $fullPath{$tLen + 1} != '{' && !String::beginsWith($fullPath, '{')) {
//when mapped to root exclude paths that have static parts
//they are listed else where under that static part name
continue;
}
if (!static::verifyAccess($route)) {
continue;
}
foreach (static::$excludedPaths as $exclude) {
if (empty($exclude)) {
if ($fullPath == $exclude)
continue 2;
} elseif (String::beginsWith($fullPath, $exclude)) {
continue 2;
}
}
$m = $route['metadata'];
if ($id == '' && $m['resourcePath'] != '') {
continue;
}
if (isset($filter[$httpMethod][$fullPath])) {
continue;
}
$filter[$httpMethod][$fullPath] = true;
// reset body params
$this->_bodyParam = array(
'required' => false,
'description' => array()
);
$count++;
$className = $this->_noNamespace($route['className']);
if (!$r) {
$resourcePath = '/'
. trim($m['resourcePath'], '/');
$r = $this->_operationListing($resourcePath);
}
$parts = explode('/', $fullPath);
$pos = count($parts) - 1;
if (count($parts) == 1 && $httpMethod == 'GET') {
} else {
for ($i = 0; $i < count($parts); $i++) {
if (strlen($parts[$i]) && $parts[$i]{0} == '{') {
$pos = $i - 1;
break;
}
}
}
$nickname = $this->_nickname($route);
$index = static::$placeFormatExtensionBeforeDynamicParts && $pos > 0 ? $pos : 0;
if (!empty($parts[$index]))
$parts[$index] .= $this->formatString;
$fullPath = implode('/', $parts);
$description = isset(
$m['classDescription'])
? $m['classDescription']
: $className . ' API';
if (empty($m['description'])) {
$m['description'] = $this->restler->getProductionMode()
? ''
: 'routes to <mark>'
. $route['className']
. '::'
. $route['methodName'] . '();</mark>';
}
if (empty($m['longDescription'])) {
$m['longDescription'] = $this->restler->getProductionMode()
? ''
: 'Add PHPDoc long description to '
. "<mark>$className::"
. $route['methodName'] . '();</mark>'
. ' (the api method) to write here';
}
$operation = $this->_operation(
$route,
$nickname,
$httpMethod,
$m['description'],
$m['longDescription']
);
if (isset($m['throws'])) {
foreach ($m['throws'] as $exception) {
$operation->errorResponses[] = array(
'reason' => $exception['reason'],
'code' => $exception['code']);
}
}
if (isset($m['param'])) {
foreach ($m['param'] as $param) {
//combine body params as one
$p = $this->_parameter($param);
if ($p->paramType == 'body') {
$this->_appendToBody($p);
} else {
$operation->parameters[] = $p;
}
}
}
if (
count($this->_bodyParam['description']) ||
(
$this->_fullDataRequested &&
$httpMethod != 'GET' &&
$httpMethod != 'DELETE'
)
) {
$operation->parameters[] = $this->_getBody();
}
if (isset($m['return']['type'])) {
$responseClass = $m['return']['type'];
if (is_string($responseClass)) {
if (class_exists($responseClass)) {
$this->_model($responseClass);
$operation->responseClass
= $this->_noNamespace($responseClass);
} elseif (strtolower($responseClass) == 'array') {
$operation->responseClass = 'Array';
$rt = $m['return'];
if (isset(
$rt[CommentParser::$embeddedDataName]['type'])
) {
$rt = $rt[CommentParser::$embeddedDataName]
['type'];
if (class_exists($rt)) {
$this->_model($rt);
$operation->responseClass .= '[' .
$this->_noNamespace($rt) . ']';
}
}
}
}
}
$api = false;
if (static::$groupOperations) {
foreach ($r->apis as $a) {
if ($a->path == "/$fullPath") {
$api = $a;
break;
}
}
}
if (!$api) {
$api = $this->_api("$prefix/$fullPath", $description);
$r->apis[] = $api;
}
$api->operations[] = $operation;
}
}
if (!$count) {
throw new RestException(404);
}
if (!is_null($r))
$r->models = $this->_models;
usort(
$r->apis,
function ($a, $b) {
$order = array(
'GET' => 1,
'POST' => 2,
'PUT' => 3,
'PATCH' => 4,
'DELETE' => 5
);
return
$a->operations[0]->httpMethod ==
$b->operations[0]->httpMethod
? $a->path > $b->path
: $order[$a->operations[0]->httpMethod] >
$order[$b->operations[0]->httpMethod];
}
);
return $r;
}
protected function _nickname(array $route)
{
static $hash = array();
$method = $route['methodName'];
if (isset(static::$prefixes[$method])) {
$method = static::$prefixes[$method];
} else {
$method = str_replace(
array_keys(static::$prefixes),
array_values(static::$prefixes),
$method
);
}
while (isset($hash[$method]) && $route['url'] != $hash[$method]) {
//create another one
$method .= '_';
}
$hash[$method] = $route['url'];
return $method;
}
protected function _noNamespace($className)
{
$className = explode('\\', $className);
return end($className);
}
protected function _operationListing($resourcePath = '/')
{
$r = $this->_resourceListing();
$r->resourcePath = $resourcePath;
$r->models = new stdClass();
return $r;
}
protected function _resourceListing()
{
$r = new stdClass();
$r->apiVersion = (string)$this->restler->_requestedApiVersion;
$r->swaggerVersion = "1.1";
$r->basePath = $this->restler->getBaseUrl();
$r->produces = $this->restler->getWritableMimeTypes();
$r->consumes = $this->restler->getReadableMimeTypes();
$r->apis = array();
return $r;
}
protected function _api($path, $description = '')
{
$r = new stdClass();
$r->path = $path;
$r->description =
empty($description) && $this->restler->getProductionMode()
? 'Use PHPDoc comment to describe here'
: $description;
$r->operations = array();
return $r;
}
protected function _operation(
$route,
$nickname,
$httpMethod = 'GET',
$summary = 'description',
$notes = 'long description',
$responseClass = 'void'
)
{
//reset body params
$this->_bodyParam = array(
'required' => false,
'description' => array()
);
$r = new stdClass();
$r->httpMethod = $httpMethod;
$r->nickname = $nickname;
$r->responseClass = $responseClass;
$r->parameters = array();
$r->summary = $summary . ($route['accessLevel'] > 2
? static::$apiDescriptionSuffixSymbols[2]
: static::$apiDescriptionSuffixSymbols[$route['accessLevel']]
);
$r->notes = $notes;
$r->errorResponses = array();
return $r;
}
protected function _parameter($param)
{
$r = new stdClass();
$r->name = $param['name'];
$r->description = !empty($param['description'])
? $param['description'] . '.'
: ($this->restler->getProductionMode()
? ''
: 'add <mark>@param {type} $' . $r->name
. ' {comment}</mark> to describe here');
//paramType can be path or query or body or header
$r->paramType = Util::nestedValue($param, CommentParser::$embeddedDataName, 'from') ? : 'query';
$r->required = isset($param['required']) && $param['required'];
if (isset($param['default'])) {
$r->defaultValue = $param['default'];
} elseif (isset($param[CommentParser::$embeddedDataName]['example'])) {
$r->defaultValue
= $param[CommentParser::$embeddedDataName]['example'];
}
$r->allowMultiple = false;
$type = 'string';
if (isset($param['type'])) {
$type = $param['type'];
if (is_array($type)) {
$type = array_shift($type);
}
if ($type == 'array') {
$contentType = Util::nestedValue(
$param,
CommentParser::$embeddedDataName,
'type'
);
if ($contentType) {
if ($contentType == 'indexed') {
$type = 'Array';
} elseif ($contentType == 'associative') {
$type = 'Object';
} else {
$type = "Array[$contentType]";
}
if (Util::isObjectOrArray($contentType)) {
$this->_model($contentType);
}
} elseif (isset(static::$dataTypeAlias[$type])) {
$type = static::$dataTypeAlias[$type];
}
} elseif (Util::isObjectOrArray($type)) {
$this->_model($type);
} elseif (isset(static::$dataTypeAlias[$type])) {
$type = static::$dataTypeAlias[$type];
}
}
$r->dataType = $type;
if (isset($param[CommentParser::$embeddedDataName])) {
$p = $param[CommentParser::$embeddedDataName];
if (isset($p['min']) && isset($p['max'])) {
$r->allowableValues = array(
'valueType' => 'RANGE',
'min' => $p['min'],
'max' => $p['max'],
);
} elseif (isset($p['choice'])) {
$r->allowableValues = array(
'valueType' => 'LIST',
'values' => $p['choice']
);
}
}
return $r;
}
protected function _appendToBody($p)
{
if ($p->name === Defaults::$fullRequestDataName) {
$this->_fullDataRequested = $p;
unset($this->_bodyParam['names'][Defaults::$fullRequestDataName]);
return;
}
$this->_bodyParam['description'][$p->name]
= "$p->name"
. ' : <tag>' . $p->dataType . '</tag> '
. ($p->required ? ' <i>(required)</i> - ' : ' - ')
. $p->description;
$this->_bodyParam['required'] = $p->required
|| $this->_bodyParam['required'];
$this->_bodyParam['names'][$p->name] = $p;
}
protected function _getBody()
{
$r = new stdClass();
$n = isset($this->_bodyParam['names'])
? array_values($this->_bodyParam['names'])
: array();
if (count($n) == 1) {
if (isset($this->_models->{$n[0]->dataType})) {
// ============ custom class ===================
$r = $n[0];
$c = $this->_models->{$r->dataType};
$a = $c->properties;
$r->description = "Paste JSON data here";
if (count($a)) {
$r->description .= " with the following"
. (count($a) > 1 ? ' properties.' : ' property.');
foreach ($a as $k => $v) {
$r->description .= "<hr/>$k : <tag>"
. $v['type'] . '</tag> '
. (isset($v['required']) ? '(required)' : '')
. ' - ' . $v['description'];
}
}
$r->defaultValue = "{\n \""
. implode("\": \"\",\n \"",
array_keys($c->properties))
. "\": \"\"\n}";
return $r;
} elseif (false !== ($p = strpos($n[0]->dataType, '['))) {
// ============ array of custom class ===============
$r = $n[0];
$t = substr($r->dataType, $p + 1, -1);
if ($c = Util::nestedValue($this->_models, $t)) {
$a = $c->properties;
$r->description = "Paste JSON data here";
if (count($a)) {
$r->description .= " with an array of objects with the following"
. (count($a) > 1 ? ' properties.' : ' property.');
foreach ($a as $k => $v) {
$r->description .= "<hr/>$k : <tag>"
. $v['type'] . '</tag> '
. (isset($v['required']) ? '(required)' : '')
. ' - ' . $v['description'];
}
}
$r->defaultValue = "[\n {\n \""
. implode("\": \"\",\n \"",
array_keys($c->properties))
. "\": \"\"\n }\n]";
return $r;
} else {
$r->description = "Paste JSON data here with an array of $t values.";
$r->defaultValue = "[ ]";
return $r;
}
} elseif ($n[0]->dataType == 'Array') {
// ============ array ===============================
$r = $n[0];
$r->description = "Paste JSON array data here"
. ($r->required ? ' (required) . ' : '. ')
. "<br/>$r->description";
$r->defaultValue = "[\n {\n \""
. "property\" : \"\"\n }\n]";
return $r;
} elseif ($n[0]->dataType == 'Object') {
// ============ object ==============================
$r = $n[0];
$r->description = "Paste JSON object data here"
. ($r->required ? ' (required) . ' : '. ')
. "<br/>$r->description";
$r->defaultValue = "{\n \""
. "property\" : \"\"\n}";
return $r;
}
}
$p = array_values($this->_bodyParam['description']);
$r->name = 'REQUEST_BODY';
$r->description = "Paste JSON data here";
if (count($p) == 0 && $this->_fullDataRequested) {
$r->required = $this->_fullDataRequested->required;
$r->defaultValue = "{\n \"property\" : \"\"\n}";
} else {
$r->description .= " with the following"
. (count($p) > 1 ? ' properties.' : ' property.')
. '<hr/>'
. implode("<hr/>", $p);
$r->required = $this->_bodyParam['required'];
// Create default object that includes parameters to be submitted
$defaultObject = new \StdClass();
foreach ($this->_bodyParam['names'] as $name => $values) {
if (!$values->required)
continue;
if (class_exists($values->dataType)) {
$myClassName = $values->dataType;
$defaultObject->$name = new $myClassName();
} else {
$defaultObject->$name = '';
}
}
$r->defaultValue = Scope::get('JsonFormat')->encode($defaultObject, true);
}
$r->paramType = 'body';
$r->allowMultiple = false;
$r->dataType = 'Object';
return $r;
}
protected function _model($className, $instance = null)
{
$id = $this->_noNamespace($className);
if (isset($this->_models->{$id})) {
return;
}
$properties = array();
if (!$instance) {
if (!class_exists($className))
return;
$instance = new $className();
}
$data = get_object_vars($instance);
$reflectionClass = new \ReflectionClass($className);
foreach ($data as $key => $value) {
$propertyMetaData = null;
try {
$property = $reflectionClass->getProperty($key);
if ($c = $property->getDocComment()) {
$propertyMetaData = Util::nestedValue(
CommentParser::parse($c),
'var'
);
}
} catch (\ReflectionException $e) {
}
if (is_null($propertyMetaData)) {
$type = $this->getType($value, true);
$description = '';
} else {
$type = Util::nestedValue(
$propertyMetaData,
'type'
) ? : $this->getType($value, true);
$description = Util::nestedValue(
$propertyMetaData,
'description'
) ? : '';
if (class_exists($type)) {
$this->_model($type);
$type = $this->_noNamespace($type);
}
}
if (isset(static::$dataTypeAlias[$type])) {
$type = static::$dataTypeAlias[$type];
}
$properties[$key] = array(
'type' => $type,
'description' => $description
);
if (Util::nestedValue(
$propertyMetaData,
CommentParser::$embeddedDataName,
'required'
)
) {
$properties[$key]['required'] = true;
}
if ($type == 'Array') {
$itemType = Util::nestedValue(
$propertyMetaData,
CommentParser::$embeddedDataName,
'type'
) ? :
(count($value)
? $this->getType(end($value), true)
: 'string');
if (class_exists($itemType)) {
$this->_model($itemType);
$itemType = $this->_noNamespace($itemType);
}
$properties[$key]['item'] = array(
'type' => $itemType,
/*'description' => '' */ //TODO: add description
);
} else if (preg_match('/^Array\[(.+)\]$/', $type, $matches)) {
$itemType = $matches[1];
$properties[$key]['type'] = 'Array';
$properties[$key]['item']['type'] = $itemType;
if (class_exists($itemType)) {
$this->_model($itemType);
}
}
}
if (!empty($properties)) {
$model = new stdClass();
$model->id = $id;
$model->properties = $properties;
$this->_models->{$id} = $model;
}
}
/**
* Find the data type of the given value.
*
*
* @param mixed $o given value for finding type
*
* @param bool $appendToModels if an object is found should we append to
* our models list?
*
* @return string
*
* @access private
*/
public function getType($o, $appendToModels = false)
{
if (is_object($o)) {
$oc = get_class($o);
if ($appendToModels) {
$this->_model($oc, $o);
}
return $this->_noNamespace($oc);
}
if (is_array($o)) {
if (count($o)) {
$child = end($o);
if (Util::isObjectOrArray($child)) {
$childType = $this->getType($child, $appendToModels);
return "Array[$childType]";
}
}
return 'array';
}
if (is_bool($o)) return 'boolean';
if (is_numeric($o)) return is_float($o) ? 'float' : 'int';
return 'string';
}
/**
* pre call for index()
*
* if cache is present, use cache
*/
public function _pre_index_json()
{
$userClass = Defaults::$userIdentifierClass;
$this->cacheName = $userClass::getCacheIdentifier()
. '_resources-v'
. $this->restler->_requestedApiVersion;
if ($this->restler->getProductionMode()
&& !$this->restler->refreshCache
&& $this->restler->cache->isCached($this->cacheName)
) {
//by pass call, compose, postCall stages and directly send response
$this->restler->composeHeaders();
die($this->restler->cache->get($this->cacheName));
}
}
/**
* post call for index()
*
* create cache if in production mode
*
* @param $responseData
*
* @internal param string $data composed json output
*
* @return string
*/
public function _post_index_json($responseData)
{
if ($this->restler->getProductionMode()) {
$this->restler->cache->set($this->cacheName, $responseData);
}
return $responseData;
}
/**
* @access hybrid
* @return \stdClass
*/
public function index()
{
if (!static::$accessControlFunction && Defaults::$accessControlFunction)
static::$accessControlFunction = Defaults::$accessControlFunction;
$version = $this->restler->getRequestedApiVersion();
$allRoutes = Util::nestedValue(Routes::toArray(), "v$version");
$r = $this->_resourceListing();
$map = array();
if (isset($allRoutes['*'])) {
$this->_mapResources($allRoutes['*'], $map, $version);
unset($allRoutes['*']);
}
$this->_mapResources($allRoutes, $map, $version);
foreach ($map as $path => $description) {
if (!String::contains($path, '{')) {
//add id
$r->apis[] = array(
'path' => $path . $this->formatString,
'description' => $description
);
}
}
if (Defaults::$useUrlBasedVersioning && static::$listHigherVersions) {
$nextVersion = $version + 1;
if ($nextVersion <= $this->restler->getApiVersion()) {
list($status, $data) = $this->_loadResource("/v$nextVersion/resources.json");
if ($status == 200) {
$r->apis = array_merge($r->apis, $data->apis);
$r->apiVersion = $data->apiVersion;
}
}
}
return $r;
}
protected function _loadResource($url)
{
$ch = curl_init($this->restler->getBaseUrl() . $url
. (empty($_GET) ? '' : '?' . http_build_query($_GET)));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Accept:application/json',
));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
$result = json_decode(curl_exec($ch));
$http_status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
return array($http_status, $result);
}
protected function _mapResources(array $allRoutes, array &$map, $version = 1)
{
foreach ($allRoutes as $fullPath => $routes) {
$path = explode('/', $fullPath);
$resource = isset($path[0]) ? $path[0] : '';
if ($resource == 'resources' || String::endsWith($resource, 'index'))
continue;
foreach ($routes as $httpMethod => $route) {
if (in_array($httpMethod, static::$excludedHttpMethods)) {
continue;
}
if (!static::verifyAccess($route)) {
continue;
}
foreach (static::$excludedPaths as $exclude) {
if (empty($exclude)) {
if ($fullPath == $exclude)
continue 2;
} elseif (String::beginsWith($fullPath, $exclude)) {
continue 2;
}
}
$res = $resource
? ($version == 1 ? "/resources/$resource" : "/v$version/resources/$resource-v$version")
: ($version == 1 ? "/resources/root" : "/v$version/resources/root-v$version");
if (empty($map[$res])) {
$map[$res] = isset(
$route['metadata']['classDescription'])
? $route['metadata']['classDescription'] : '';
}
}
}
}
/**
* Maximum api version supported by the api class
* @return int
*/
public static function __getMaximumSupportedVersion()
{
return Scope::get('Restler')->getApiVersion();
}
/**
* Verifies that the requesting user is allowed to view the docs for this API
*
* @param $route
*
* @return boolean True if the user should be able to view this API's docs
*/
protected function verifyAccess($route)
{
if ($route['accessLevel'] < 2) {
return true;
}
if (
static::$hideProtected
&& !$this->_authenticated
&& $route['accessLevel'] > 1
) {
return false;
}
if ($this->_authenticated
&& static::$accessControlFunction
&& (!call_user_func(
static::$accessControlFunction, $route['metadata']))
) {
return false;
}
return true;
}
}