Merge pull request #23819 from nextcloud/td/routing/move_things_around
Cleanup routing
This commit is contained in:
commit
645e1c55b8
|
@ -607,6 +607,7 @@ return array(
|
|||
'OC\\AppFramework\\OCS\\V2Response' => $baseDir . '/lib/private/AppFramework/OCS/V2Response.php',
|
||||
'OC\\AppFramework\\Routing\\RouteActionHandler' => $baseDir . '/lib/private/AppFramework/Routing/RouteActionHandler.php',
|
||||
'OC\\AppFramework\\Routing\\RouteConfig' => $baseDir . '/lib/private/AppFramework/Routing/RouteConfig.php',
|
||||
'OC\\AppFramework\\Routing\\RouteParser' => $baseDir . '/lib/private/AppFramework/Routing/RouteParser.php',
|
||||
'OC\\AppFramework\\ScopedPsrLogger' => $baseDir . '/lib/private/AppFramework/ScopedPsrLogger.php',
|
||||
'OC\\AppFramework\\Services\\AppConfig' => $baseDir . '/lib/private/AppFramework/Services/AppConfig.php',
|
||||
'OC\\AppFramework\\Services\\InitialState' => $baseDir . '/lib/private/AppFramework/Services/InitialState.php',
|
||||
|
|
|
@ -636,6 +636,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\AppFramework\\OCS\\V2Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V2Response.php',
|
||||
'OC\\AppFramework\\Routing\\RouteActionHandler' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteActionHandler.php',
|
||||
'OC\\AppFramework\\Routing\\RouteConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteConfig.php',
|
||||
'OC\\AppFramework\\Routing\\RouteParser' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteParser.php',
|
||||
'OC\\AppFramework\\ScopedPsrLogger' => __DIR__ . '/../../..' . '/lib/private/AppFramework/ScopedPsrLogger.php',
|
||||
'OC\\AppFramework\\Services\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/AppConfig.php',
|
||||
'OC\\AppFramework\\Services\\InitialState' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/InitialState.php',
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Robin Appelman <robin@icewind.nl>
|
||||
* @author Robin McCorkell <robin@mccorkell.me.uk>
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
*
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\AppFramework\Routing;
|
||||
|
||||
use OC\Route\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class RouteParser {
|
||||
/** @var string[] */
|
||||
private $controllerNameCache = [];
|
||||
|
||||
private const rootUrlApps = [
|
||||
'cloud_federation_api',
|
||||
'core',
|
||||
'files_sharing',
|
||||
'files',
|
||||
'settings',
|
||||
'spreed',
|
||||
];
|
||||
|
||||
public function parseDefaultRoutes(array $routes, string $appName): RouteCollection {
|
||||
$collection = $this->processIndexRoutes($routes, $appName);
|
||||
$collection->addCollection($this->processIndexResources($routes, $appName));
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public function parseOCSRoutes(array $routes, string $appName): RouteCollection {
|
||||
$collection = $this->processOCS($routes, $appName);
|
||||
$collection->addCollection($this->processOCSResources($routes, $appName));
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
private function processOCS(array $routes, string $appName): RouteCollection {
|
||||
$collection = new RouteCollection();
|
||||
$ocsRoutes = $routes['ocs'] ?? [];
|
||||
foreach ($ocsRoutes as $ocsRoute) {
|
||||
$result = $this->processRoute($ocsRoute, $appName, 'ocs.');
|
||||
|
||||
$collection->add($result[0], $result[1]);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates one route base on the give configuration
|
||||
* @param array $routes
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
private function processIndexRoutes(array $routes, string $appName): RouteCollection {
|
||||
$collection = new RouteCollection();
|
||||
$simpleRoutes = $routes['routes'] ?? [];
|
||||
foreach ($simpleRoutes as $simpleRoute) {
|
||||
$result = $this->processRoute($simpleRoute, $appName);
|
||||
|
||||
$collection->add($result[0], $result[1]);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
private function processRoute(array $route, string $appName, string $routeNamePrefix = ''): array {
|
||||
$name = $route['name'];
|
||||
$postfix = $route['postfix'] ?? '';
|
||||
$root = $this->buildRootPrefix($route, $appName, $routeNamePrefix);
|
||||
|
||||
$url = $root . '/' . ltrim($route['url'], '/');
|
||||
$verb = strtoupper($route['verb'] ?? 'GET');
|
||||
|
||||
$split = explode('#', $name, 2);
|
||||
if (count($split) !== 2) {
|
||||
throw new \UnexpectedValueException('Invalid route name');
|
||||
}
|
||||
list($controller, $action) = $split;
|
||||
|
||||
$controllerName = $this->buildControllerName($controller);
|
||||
$actionName = $this->buildActionName($action);
|
||||
|
||||
$routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix;
|
||||
|
||||
$routeObject = new Route($url);
|
||||
$routeObject->method($verb);
|
||||
|
||||
// optionally register requirements for route. This is used to
|
||||
// tell the route parser how url parameters should be matched
|
||||
if (array_key_exists('requirements', $route)) {
|
||||
$routeObject->requirements($route['requirements']);
|
||||
}
|
||||
|
||||
// optionally register defaults for route. This is used to
|
||||
// tell the route parser how url parameters should be default valued
|
||||
$defaults = [];
|
||||
if (array_key_exists('defaults', $route)) {
|
||||
$defaults = $route['defaults'];
|
||||
}
|
||||
|
||||
$defaults['caller'] = [$appName, $controllerName, $actionName];
|
||||
$routeObject->defaults($defaults);
|
||||
|
||||
return [$routeName, $routeObject];
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful OCS routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
private function processOCSResources(array $routes, string $appName): RouteCollection {
|
||||
return $this->processResources($routes['ocs-resources'] ?? [], $appName, 'ocs.');
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
private function processIndexResources(array $routes, string $appName): RouteCollection {
|
||||
return $this->processResources($routes['resources'] ?? [], $appName);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given name and url restful routes are created:
|
||||
* - index
|
||||
* - show
|
||||
* - create
|
||||
* - update
|
||||
* - destroy
|
||||
*
|
||||
* @param array $resources
|
||||
* @param string $routeNamePrefix
|
||||
*/
|
||||
private function processResources(array $resources, string $appName, string $routeNamePrefix = ''): RouteCollection {
|
||||
// declaration of all restful actions
|
||||
$actions = [
|
||||
['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
|
||||
['name' => 'show', 'verb' => 'GET'],
|
||||
['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
|
||||
['name' => 'update', 'verb' => 'PUT'],
|
||||
['name' => 'destroy', 'verb' => 'DELETE'],
|
||||
];
|
||||
|
||||
$collection = new RouteCollection();
|
||||
foreach ($resources as $resource => $config) {
|
||||
$root = $this->buildRootPrefix($config, $appName, $routeNamePrefix);
|
||||
|
||||
// the url parameter used as id to the resource
|
||||
foreach ($actions as $action) {
|
||||
$url = $root . '/' . ltrim($config['url'], '/');
|
||||
$method = $action['name'];
|
||||
|
||||
$verb = strtoupper($action['verb'] ?? 'GET');
|
||||
$collectionAction = $action['on-collection'] ?? false;
|
||||
if (!$collectionAction) {
|
||||
$url .= '/{id}';
|
||||
}
|
||||
|
||||
$controller = $resource;
|
||||
|
||||
$controllerName = $this->buildControllerName($controller);
|
||||
$actionName = $this->buildActionName($method);
|
||||
|
||||
$routeName = $routeNamePrefix . $appName . '.' . strtolower($resource) . '.' . $method;
|
||||
|
||||
$route = new Route($url);
|
||||
$route->method($verb);
|
||||
|
||||
$route->defaults(['caller' => [$appName, $controllerName, $actionName]]);
|
||||
|
||||
$collection->add($routeName, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
private function buildRootPrefix(array $route, string $appName, string $routeNamePrefix): string {
|
||||
$defaultRoot = $appName === 'core' ? '' : '/apps/' . $appName;
|
||||
$root = $route['root'] ?? $defaultRoot;
|
||||
|
||||
if ($routeNamePrefix !== '') {
|
||||
// In OCS all apps are whitelisted
|
||||
return $root;
|
||||
}
|
||||
|
||||
if (!\in_array($appName, self::rootUrlApps, true)) {
|
||||
// Only allow root URLS for some apps
|
||||
return $defaultRoot;
|
||||
}
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on a given route name the controller name is generated
|
||||
* @param string $controller
|
||||
* @return string
|
||||
*/
|
||||
private function buildControllerName(string $controller): string {
|
||||
if (!isset($this->controllerNameCache[$controller])) {
|
||||
$this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
|
||||
}
|
||||
return $this->controllerNameCache[$controller];
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the action part of the route name the controller method name is generated
|
||||
* @param string $action
|
||||
* @return string
|
||||
*/
|
||||
private function buildActionName(string $action): string {
|
||||
return $this->underScoreToCamelCase($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Underscored strings are converted to camel case strings
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
private function underScoreToCamelCase(string $str): string {
|
||||
$pattern = '/_[a-z]?/';
|
||||
return preg_replace_callback(
|
||||
$pattern,
|
||||
function ($matches) {
|
||||
return strtoupper(ltrim($matches[0], '_'));
|
||||
},
|
||||
$str);
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
namespace OC\Route;
|
||||
|
||||
use OC\AppFramework\Routing\RouteParser;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\ILogger;
|
||||
use OCP\Route\IRouter;
|
||||
|
@ -426,8 +427,14 @@ class Router implements IRouter {
|
|||
*/
|
||||
private function setupRoutes($routes, $appName) {
|
||||
if (is_array($routes)) {
|
||||
$application = $this->getApplicationClass($appName);
|
||||
$application->registerRoutes($this, $routes);
|
||||
$routeParser = new RouteParser();
|
||||
|
||||
$defaultRoutes = $routeParser->parseDefaultRoutes($routes, $appName);
|
||||
$ocsRoutes = $routeParser->parseOCSRoutes($routes, $appName);
|
||||
|
||||
$this->root->addCollection($defaultRoutes);
|
||||
$ocsRoutes->addPrefix('/ocsapp');
|
||||
$this->root->addCollection($ocsRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue