2013-08-17 13:16:48 +04:00
|
|
|
<?php
|
|
|
|
/**
|
2016-07-21 18:07:57 +03:00
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
|
|
|
*
|
2015-03-26 13:44:34 +03:00
|
|
|
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
|
|
* @author Morris Jobke <hey@morrisjobke.de>
|
|
|
|
* @author Patrick Paysant <ppaysant@linagora.com>
|
2016-07-21 19:13:36 +03:00
|
|
|
* @author Robin Appelman <robin@icewind.nl>
|
2016-01-12 17:02:16 +03:00
|
|
|
* @author Robin McCorkell <robin@mccorkell.me.uk>
|
2016-07-21 18:07:57 +03:00
|
|
|
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
2015-03-26 13:44:34 +03:00
|
|
|
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
2015-03-26 13:44:34 +03:00
|
|
|
* @license AGPL-3.0
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
2015-03-26 13:44:34 +03:00
|
|
|
* This code is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License, version 3,
|
|
|
|
* as published by the Free Software Foundation.
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
2015-03-26 13:44:34 +03:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
2013-08-17 13:16:48 +04:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2015-03-26 13:44:34 +03:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
2015-03-26 13:44:34 +03:00
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3,
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
2013-08-17 13:16:48 +04:00
|
|
|
*
|
|
|
|
*/
|
2015-02-26 13:37:37 +03:00
|
|
|
|
2016-04-22 16:28:09 +03:00
|
|
|
namespace OC\AppFramework\Routing;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
use OC\AppFramework\DependencyInjection\DIContainer;
|
2014-03-10 17:04:58 +04:00
|
|
|
use OCP\Route\IRouter;
|
2013-08-17 13:16:48 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class RouteConfig
|
|
|
|
* @package OC\AppFramework\routing
|
|
|
|
*/
|
|
|
|
class RouteConfig {
|
2016-09-11 14:25:32 +03:00
|
|
|
/** @var DIContainer */
|
2013-08-17 13:16:48 +04:00
|
|
|
private $container;
|
2016-09-11 14:25:32 +03:00
|
|
|
|
|
|
|
/** @var IRouter */
|
2013-08-17 13:16:48 +04:00
|
|
|
private $router;
|
2016-09-11 14:25:32 +03:00
|
|
|
|
|
|
|
/** @var array */
|
2013-08-17 13:16:48 +04:00
|
|
|
private $routes;
|
2016-09-11 14:25:32 +03:00
|
|
|
|
|
|
|
/** @var string */
|
2013-08-17 13:16:48 +04:00
|
|
|
private $appName;
|
|
|
|
|
2016-09-11 14:25:32 +03:00
|
|
|
/** @var string[] */
|
|
|
|
private $controllerNameCache = [];
|
|
|
|
|
2013-08-17 13:16:48 +04:00
|
|
|
/**
|
|
|
|
* @param \OC\AppFramework\DependencyInjection\DIContainer $container
|
2014-03-10 17:04:58 +04:00
|
|
|
* @param \OCP\Route\IRouter $router
|
2016-09-11 14:25:32 +03:00
|
|
|
* @param array $routes
|
2013-08-17 13:16:48 +04:00
|
|
|
* @internal param $appName
|
|
|
|
*/
|
2014-03-10 17:04:58 +04:00
|
|
|
public function __construct(DIContainer $container, IRouter $router, $routes) {
|
2013-08-17 13:16:48 +04:00
|
|
|
$this->routes = $routes;
|
|
|
|
$this->container = $container;
|
|
|
|
$this->router = $router;
|
|
|
|
$this->appName = $container['AppName'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-03-10 17:04:58 +04:00
|
|
|
* The routes and resource will be registered to the \OCP\Route\IRouter
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
public function register() {
|
|
|
|
|
|
|
|
// parse simple
|
|
|
|
$this->processSimpleRoutes($this->routes);
|
|
|
|
|
|
|
|
// parse resources
|
|
|
|
$this->processResources($this->routes);
|
2016-05-17 11:11:27 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* OCS routes go into a different collection
|
|
|
|
*/
|
|
|
|
$oldCollection = $this->router->getCurrentCollection();
|
|
|
|
$this->router->useCollection($oldCollection.'.ocs');
|
|
|
|
|
|
|
|
// parse ocs simple routes
|
|
|
|
$this->processOCS($this->routes);
|
|
|
|
|
2019-01-21 14:02:36 +03:00
|
|
|
// parse ocs simple routes
|
|
|
|
$this->processOCSResources($this->routes);
|
|
|
|
|
2016-05-17 11:11:27 +03:00
|
|
|
$this->router->useCollection($oldCollection);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function processOCS(array $routes) {
|
|
|
|
$ocsRoutes = isset($routes['ocs']) ? $routes['ocs'] : [];
|
|
|
|
foreach ($ocsRoutes as $ocsRoute) {
|
|
|
|
$name = $ocsRoute['name'];
|
2016-07-14 21:45:19 +03:00
|
|
|
$postfix = '';
|
2016-05-17 11:11:27 +03:00
|
|
|
|
|
|
|
if (isset($ocsRoute['postfix'])) {
|
|
|
|
$postfix = $ocsRoute['postfix'];
|
|
|
|
}
|
|
|
|
|
2016-08-01 17:37:48 +03:00
|
|
|
if (isset($ocsRoute['root'])) {
|
|
|
|
$root = $ocsRoute['root'];
|
|
|
|
} else {
|
|
|
|
$root = '/apps/'.$this->appName;
|
|
|
|
}
|
|
|
|
|
|
|
|
$url = $root . $ocsRoute['url'];
|
2016-05-17 11:11:27 +03:00
|
|
|
$verb = isset($ocsRoute['verb']) ? strtoupper($ocsRoute['verb']) : 'GET';
|
|
|
|
|
|
|
|
$split = explode('#', $name, 2);
|
|
|
|
if (count($split) != 2) {
|
|
|
|
throw new \UnexpectedValueException('Invalid route name');
|
|
|
|
}
|
|
|
|
$controller = $split[0];
|
|
|
|
$action = $split[1];
|
|
|
|
|
|
|
|
$controllerName = $this->buildControllerName($controller);
|
|
|
|
$actionName = $this->buildActionName($action);
|
|
|
|
|
|
|
|
// register the route
|
|
|
|
$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
|
|
|
|
|
|
|
|
$router = $this->router->create('ocs.'.$this->appName.'.'.$controller.'.'.$action . $postfix, $url)
|
|
|
|
->method($verb)
|
|
|
|
->action($handler);
|
|
|
|
|
|
|
|
// optionally register requirements for route. This is used to
|
|
|
|
// tell the route parser how url parameters should be matched
|
|
|
|
if(array_key_exists('requirements', $ocsRoute)) {
|
|
|
|
$router->requirements($ocsRoute['requirements']);
|
|
|
|
}
|
|
|
|
|
|
|
|
// optionally register defaults for route. This is used to
|
|
|
|
// tell the route parser how url parameters should be default valued
|
|
|
|
if(array_key_exists('defaults', $ocsRoute)) {
|
|
|
|
$router->defaults($ocsRoute['defaults']);
|
|
|
|
}
|
|
|
|
}
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates one route base on the give configuration
|
2014-05-12 00:51:30 +04:00
|
|
|
* @param array $routes
|
2013-08-17 13:16:48 +04:00
|
|
|
* @throws \UnexpectedValueException
|
|
|
|
*/
|
|
|
|
private function processSimpleRoutes($routes)
|
|
|
|
{
|
|
|
|
$simpleRoutes = isset($routes['routes']) ? $routes['routes'] : array();
|
|
|
|
foreach ($simpleRoutes as $simpleRoute) {
|
|
|
|
$name = $simpleRoute['name'];
|
2014-11-19 14:00:41 +03:00
|
|
|
$postfix = '';
|
|
|
|
|
|
|
|
if (isset($simpleRoute['postfix'])) {
|
|
|
|
$postfix = $simpleRoute['postfix'];
|
|
|
|
}
|
|
|
|
|
2013-08-17 13:16:48 +04:00
|
|
|
$url = $simpleRoute['url'];
|
|
|
|
$verb = isset($simpleRoute['verb']) ? strtoupper($simpleRoute['verb']) : 'GET';
|
|
|
|
|
|
|
|
$split = explode('#', $name, 2);
|
|
|
|
if (count($split) != 2) {
|
|
|
|
throw new \UnexpectedValueException('Invalid route name');
|
|
|
|
}
|
|
|
|
$controller = $split[0];
|
|
|
|
$action = $split[1];
|
|
|
|
|
|
|
|
$controllerName = $this->buildControllerName($controller);
|
|
|
|
$actionName = $this->buildActionName($action);
|
|
|
|
|
|
|
|
// register the route
|
|
|
|
$handler = new RouteActionHandler($this->container, $controllerName, $actionName);
|
2014-11-19 14:00:41 +03:00
|
|
|
$router = $this->router->create($this->appName.'.'.$controller.'.'.$action . $postfix, $url)
|
2014-04-09 23:57:32 +04:00
|
|
|
->method($verb)
|
|
|
|
->action($handler);
|
|
|
|
|
2014-06-26 16:02:20 +04:00
|
|
|
// optionally register requirements for route. This is used to
|
2014-04-09 23:57:32 +04:00
|
|
|
// tell the route parser how url parameters should be matched
|
|
|
|
if(array_key_exists('requirements', $simpleRoute)) {
|
|
|
|
$router->requirements($simpleRoute['requirements']);
|
|
|
|
}
|
2014-09-07 14:20:48 +04:00
|
|
|
|
|
|
|
// optionally register defaults for route. This is used to
|
|
|
|
// tell the route parser how url parameters should be default valued
|
|
|
|
if(array_key_exists('defaults', $simpleRoute)) {
|
|
|
|
$router->defaults($simpleRoute['defaults']);
|
|
|
|
}
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For a given name and url restful routes are created:
|
|
|
|
* - index
|
|
|
|
* - show
|
2019-01-21 14:02:36 +03:00
|
|
|
* - create
|
|
|
|
* - update
|
|
|
|
* - destroy
|
|
|
|
*
|
|
|
|
* @param array $routes
|
|
|
|
*/
|
|
|
|
private function processOCSResources($routes)
|
|
|
|
{
|
|
|
|
// declaration of all restful actions
|
|
|
|
$actions = array(
|
|
|
|
array('name' => 'index', 'verb' => 'GET', 'on-collection' => true),
|
|
|
|
array('name' => 'show', 'verb' => 'GET'),
|
|
|
|
array('name' => 'create', 'verb' => 'POST', 'on-collection' => true),
|
|
|
|
array('name' => 'update', 'verb' => 'PUT'),
|
|
|
|
array('name' => 'destroy', 'verb' => 'DELETE'),
|
|
|
|
);
|
|
|
|
|
|
|
|
$resources = $routes['ocs-resources'] ?? [];
|
|
|
|
foreach ($resources as $resource => $config) {
|
|
|
|
$root = $config['root'] ?? '/apps/' . $this->appName;
|
|
|
|
|
|
|
|
// the url parameter used as id to the resource
|
|
|
|
foreach($actions as $action) {
|
|
|
|
$url = $root . $config['url'];
|
|
|
|
$method = $action['name'];
|
|
|
|
$verb = strtoupper($action['verb'] ?? 'GET');
|
|
|
|
$collectionAction = $action['on-collection'] ?? false;
|
|
|
|
if (!$collectionAction) {
|
|
|
|
$url .= '/{id}';
|
|
|
|
}
|
|
|
|
if (isset($action['url-postfix'])) {
|
|
|
|
$url .= '/' . $action['url-postfix'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$controller = $resource;
|
|
|
|
|
|
|
|
$controllerName = $this->buildControllerName($controller);
|
|
|
|
$actionName = $this->buildActionName($method);
|
|
|
|
|
|
|
|
$routeName = 'ocs.' . $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
|
|
|
|
|
|
|
|
$this->router->create($routeName, $url)->method($verb)->action(
|
|
|
|
new RouteActionHandler($this->container, $controllerName, $actionName)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* For a given name and url restful routes are created:
|
|
|
|
* - index
|
|
|
|
* - show
|
2013-08-17 13:16:48 +04:00
|
|
|
* - create
|
|
|
|
* - update
|
|
|
|
* - destroy
|
|
|
|
*
|
2014-05-12 00:51:30 +04:00
|
|
|
* @param array $routes
|
2013-08-17 13:16:48 +04:00
|
|
|
*/
|
|
|
|
private function processResources($routes)
|
|
|
|
{
|
|
|
|
// declaration of all restful actions
|
|
|
|
$actions = array(
|
|
|
|
array('name' => 'index', 'verb' => 'GET', 'on-collection' => true),
|
|
|
|
array('name' => 'show', 'verb' => 'GET'),
|
|
|
|
array('name' => 'create', 'verb' => 'POST', 'on-collection' => true),
|
|
|
|
array('name' => 'update', 'verb' => 'PUT'),
|
|
|
|
array('name' => 'destroy', 'verb' => 'DELETE'),
|
|
|
|
);
|
|
|
|
|
|
|
|
$resources = isset($routes['resources']) ? $routes['resources'] : array();
|
|
|
|
foreach ($resources as $resource => $config) {
|
|
|
|
|
|
|
|
// the url parameter used as id to the resource
|
|
|
|
foreach($actions as $action) {
|
|
|
|
$url = $config['url'];
|
|
|
|
$method = $action['name'];
|
|
|
|
$verb = isset($action['verb']) ? strtoupper($action['verb']) : 'GET';
|
|
|
|
$collectionAction = isset($action['on-collection']) ? $action['on-collection'] : false;
|
|
|
|
if (!$collectionAction) {
|
2014-06-26 16:20:35 +04:00
|
|
|
$url = $url . '/{id}';
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
if (isset($action['url-postfix'])) {
|
|
|
|
$url = $url . '/' . $action['url-postfix'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$controller = $resource;
|
|
|
|
|
|
|
|
$controllerName = $this->buildControllerName($controller);
|
|
|
|
$actionName = $this->buildActionName($method);
|
|
|
|
|
|
|
|
$routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method);
|
|
|
|
|
2017-04-18 22:50:25 +03:00
|
|
|
$this->router->create($routeName, $url)->method($verb)->action(
|
2013-08-17 13:16:48 +04:00
|
|
|
new RouteActionHandler($this->container, $controllerName, $actionName)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Based on a given route name the controller name is generated
|
2014-05-12 00:51:30 +04:00
|
|
|
* @param string $controller
|
2013-08-17 13:16:48 +04:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function buildControllerName($controller)
|
|
|
|
{
|
2016-09-11 14:25:32 +03:00
|
|
|
if (!isset($this->controllerNameCache[$controller])) {
|
|
|
|
$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
|
|
|
|
}
|
|
|
|
return $this->controllerNameCache[$controller];
|
2013-08-17 13:16:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Based on the action part of the route name the controller method name is generated
|
2014-05-12 00:51:30 +04:00
|
|
|
* @param string $action
|
2013-08-17 13:16:48 +04:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function buildActionName($action) {
|
|
|
|
return $this->underScoreToCamelCase($action);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Underscored strings are converted to camel case strings
|
2014-05-12 00:51:30 +04:00
|
|
|
* @param string $str
|
2013-08-17 13:16:48 +04:00
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function underScoreToCamelCase($str) {
|
|
|
|
$pattern = "/_[a-z]?/";
|
|
|
|
return preg_replace_callback(
|
|
|
|
$pattern,
|
|
|
|
function ($matches) {
|
|
|
|
return strtoupper(ltrim($matches[0], "_"));
|
|
|
|
},
|
|
|
|
$str);
|
|
|
|
}
|
|
|
|
}
|