Added the possiblity to define custom routes from plugins
This commit is contained in:
@@ -41,6 +41,16 @@ class Request extends Base
|
||||
$this->cookies = empty($cookies) ? $_COOKIE : $cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set GET parameters
|
||||
*
|
||||
* @param array $params
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->get = array_merge($this->get, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query string string parameter
|
||||
*
|
||||
@@ -146,6 +156,17 @@ class Request extends Base
|
||||
return isset($this->files[$name]['tmp_name']) ? $this->files[$name]['tmp_name'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTTP method
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->getServerVariable('REQUEST_METHOD');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the HTTP request is sent with the POST method
|
||||
*
|
||||
@@ -154,7 +175,7 @@ class Request extends Base
|
||||
*/
|
||||
public function isPost()
|
||||
{
|
||||
return isset($this->server['REQUEST_METHOD']) && $this->server['REQUEST_METHOD'] === 'POST';
|
||||
return $this->getServerVariable('REQUEST_METHOD') === 'POST';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,7 +224,7 @@ class Request extends Base
|
||||
public function getHeader($name)
|
||||
{
|
||||
$name = 'HTTP_'.str_replace('-', '_', strtoupper($name));
|
||||
return isset($this->server[$name]) ? $this->server[$name] : '';
|
||||
return $this->getServerVariable($name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,18 +235,18 @@ class Request extends Base
|
||||
*/
|
||||
public function getRemoteUser()
|
||||
{
|
||||
return isset($this->server[REVERSE_PROXY_USER_HEADER]) ? $this->server[REVERSE_PROXY_USER_HEADER] : '';
|
||||
return $this->getServerVariable(REVERSE_PROXY_USER_HEADER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current request's query string, useful for redirecting
|
||||
* Returns query string
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString()
|
||||
{
|
||||
return isset($this->server['QUERY_STRING']) ? $this->server['QUERY_STRING'] : '';
|
||||
return $this->getServerVariable('QUERY_STRING');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,7 +257,7 @@ class Request extends Base
|
||||
*/
|
||||
public function getUri()
|
||||
{
|
||||
return isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
|
||||
return $this->getServerVariable('REQUEST_URI');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +290,7 @@ class Request extends Base
|
||||
);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (! empty($this->server[$key])) {
|
||||
if ($this->getServerVariable($key) !== '') {
|
||||
foreach (explode(',', $this->server[$key]) as $ipAddress) {
|
||||
return trim($ipAddress);
|
||||
}
|
||||
@@ -287,6 +308,18 @@ class Request extends Base
|
||||
*/
|
||||
public function getStartTime()
|
||||
{
|
||||
return isset($this->server['REQUEST_TIME_FLOAT']) ? $this->server['REQUEST_TIME_FLOAT'] : 0;
|
||||
return $this->getServerVariable('REQUEST_TIME_FLOAT') ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server variable
|
||||
*
|
||||
* @access public
|
||||
* @param string $variable
|
||||
* @return string
|
||||
*/
|
||||
public function getServerVariable($variable)
|
||||
{
|
||||
return isset($this->server[$variable]) ? $this->server[$variable] : '';
|
||||
}
|
||||
}
|
||||
|
||||
188
app/Core/Http/Route.php
Normal file
188
app/Core/Http/Route.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Http;
|
||||
|
||||
use RuntimeException;
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Route Handler
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Route extends Base
|
||||
{
|
||||
/**
|
||||
* Flag that enable the routing table
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $activated = false;
|
||||
|
||||
/**
|
||||
* Store routes for path lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $paths = array();
|
||||
|
||||
/**
|
||||
* Store routes for url lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $urls = array();
|
||||
|
||||
/**
|
||||
* Enable routing table
|
||||
*
|
||||
* @access public
|
||||
* @return Route
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
$this->activated = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add route
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param string $plugin
|
||||
* @return Route
|
||||
*/
|
||||
public function addRoute($path, $controller, $action, $plugin = '')
|
||||
{
|
||||
if ($this->activated) {
|
||||
$path = ltrim($path, '/');
|
||||
$items = explode('/', $path);
|
||||
$params = $this->findParams($items);
|
||||
|
||||
$this->paths[] = array(
|
||||
'items' => $items,
|
||||
'count' => count($items),
|
||||
'controller' => $controller,
|
||||
'action' => $action,
|
||||
'plugin' => $plugin,
|
||||
);
|
||||
|
||||
$this->urls[$plugin][$controller][$action][] = array(
|
||||
'path' => $path,
|
||||
'params' => $params,
|
||||
'count' => count($params),
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a route according to the given path
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function findRoute($path)
|
||||
{
|
||||
$items = explode('/', ltrim($path, '/'));
|
||||
$count = count($items);
|
||||
|
||||
foreach ($this->paths as $route) {
|
||||
if ($count === $route['count']) {
|
||||
$params = array();
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if ($route['items'][$i]{0} === ':') {
|
||||
$params[substr($route['items'][$i], 1)] = $items[$i];
|
||||
} elseif ($route['items'][$i] !== $items[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($i === $count) {
|
||||
$this->request->setParams($params);
|
||||
return array(
|
||||
'controller' => $route['controller'],
|
||||
'action' => $route['action'],
|
||||
'plugin' => $route['plugin'],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'plugin' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find route url
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @param string $plugin
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($controller, $action, array $params = array(), $plugin = '')
|
||||
{
|
||||
if ($plugin === '' && isset($params['plugin'])) {
|
||||
$plugin = $params['plugin'];
|
||||
unset($params['plugin']);
|
||||
}
|
||||
|
||||
if (! isset($this->urls[$plugin][$controller][$action])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($this->urls[$plugin][$controller][$action] as $route) {
|
||||
if (array_diff_key($params, $route['params']) === array()) {
|
||||
$url = $route['path'];
|
||||
$i = 0;
|
||||
|
||||
foreach ($params as $variable => $value) {
|
||||
$url = str_replace(':'.$variable, $value, $url);
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($i === $route['count']) {
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find url params
|
||||
*
|
||||
* @access public
|
||||
* @param array $items
|
||||
* @return array
|
||||
*/
|
||||
public function findParams(array $items)
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item !== '' && $item{0} === ':') {
|
||||
$params[substr($item, 1)] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,21 @@ use RuntimeException;
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Router class
|
||||
* Route Dispatcher
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Router extends Base
|
||||
{
|
||||
/**
|
||||
* Plugin name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $plugin = '';
|
||||
|
||||
/**
|
||||
* Controller
|
||||
*
|
||||
@@ -30,30 +38,14 @@ class Router extends Base
|
||||
private $action = '';
|
||||
|
||||
/**
|
||||
* Store routes for path lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $paths = array();
|
||||
|
||||
/**
|
||||
* Store routes for url lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $urls = array();
|
||||
|
||||
/**
|
||||
* Get action
|
||||
* Get plugin name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAction()
|
||||
public function getPlugin()
|
||||
{
|
||||
return $this->action;
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,23 +59,32 @@ class Router extends Base
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAction()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to compare patterns
|
||||
*
|
||||
* @access public
|
||||
* @param string $uri
|
||||
* @param string $query_string
|
||||
* @return string
|
||||
*/
|
||||
public function getPath($uri, $query_string = '')
|
||||
public function getPath()
|
||||
{
|
||||
$path = substr($uri, strlen($this->helper->url->dir()));
|
||||
$path = substr($this->request->getUri(), strlen($this->helper->url->dir()));
|
||||
|
||||
if (! empty($query_string)) {
|
||||
$path = substr($path, 0, - strlen($query_string) - 1);
|
||||
if ($this->request->getQueryString() !== '') {
|
||||
$path = substr($path, 0, - strlen($this->request->getQueryString()) - 1);
|
||||
}
|
||||
|
||||
if (! empty($path) && $path{0} === '/') {
|
||||
if ($path !== '' && $path{0} === '/') {
|
||||
$path = substr($path, 1);
|
||||
}
|
||||
|
||||
@@ -91,140 +92,78 @@ class Router extends Base
|
||||
}
|
||||
|
||||
/**
|
||||
* Add route
|
||||
* Find controller/action from the route table or from get arguments
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
*/
|
||||
public function addRoute($path, $controller, $action, array $params = array())
|
||||
public function dispatch()
|
||||
{
|
||||
$pattern = explode('/', $path);
|
||||
$controller = $this->request->getStringParam('controller');
|
||||
$action = $this->request->getStringParam('action');
|
||||
$plugin = $this->request->getStringParam('plugin');
|
||||
|
||||
$this->paths[] = array(
|
||||
'pattern' => $pattern,
|
||||
'count' => count($pattern),
|
||||
'controller' => $controller,
|
||||
'action' => $action,
|
||||
);
|
||||
|
||||
$this->urls[$controller][$action][] = array(
|
||||
'path' => $path,
|
||||
'params' => array_flip($params),
|
||||
'count' => count($params),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a route according to the given path
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function findRoute($path)
|
||||
{
|
||||
$parts = explode('/', $path);
|
||||
$count = count($parts);
|
||||
|
||||
foreach ($this->paths as $route) {
|
||||
if ($count === $route['count']) {
|
||||
$params = array();
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if ($route['pattern'][$i]{0} === ':') {
|
||||
$params[substr($route['pattern'][$i], 1)] = $parts[$i];
|
||||
} elseif ($route['pattern'][$i] !== $parts[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($i === $count) {
|
||||
$_GET = array_merge($_GET, $params);
|
||||
return array($route['controller'], $route['action']);
|
||||
}
|
||||
}
|
||||
if ($controller === '') {
|
||||
$route = $this->route->findRoute($this->getPath());
|
||||
$controller = $route['controller'];
|
||||
$action = $route['action'];
|
||||
$plugin = $route['plugin'];
|
||||
}
|
||||
|
||||
return array('app', 'index');
|
||||
}
|
||||
$this->controller = ucfirst($this->sanitize($controller, 'app'));
|
||||
$this->action = $this->sanitize($action, 'index');
|
||||
$this->plugin = ucfirst($this->sanitize($plugin));
|
||||
|
||||
/**
|
||||
* Find route url
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($controller, $action, array $params = array())
|
||||
{
|
||||
if (! isset($this->urls[$controller][$action])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($this->urls[$controller][$action] as $pattern) {
|
||||
if (array_diff_key($params, $pattern['params']) === array()) {
|
||||
$url = $pattern['path'];
|
||||
$i = 0;
|
||||
|
||||
foreach ($params as $variable => $value) {
|
||||
$url = str_replace(':'.$variable, $value, $url);
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($i === $pattern['count']) {
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
return $this->executeAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check controller and action parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Controller or action name
|
||||
* @param string $default_value Default value if validation fail
|
||||
* @param string $value
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize($value, $default_value)
|
||||
public function sanitize($value, $default = '')
|
||||
{
|
||||
return ! preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $default_value : $value;
|
||||
return preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find controller/action from the route table or from get arguments
|
||||
* Execute controller action
|
||||
*
|
||||
* @access public
|
||||
* @param string $uri
|
||||
* @param string $query_string
|
||||
* @access private
|
||||
*/
|
||||
public function dispatch($uri, $query_string = '')
|
||||
private function executeAction()
|
||||
{
|
||||
if (! empty($_GET['controller']) && ! empty($_GET['action'])) {
|
||||
$this->controller = $this->sanitize($_GET['controller'], 'app');
|
||||
$this->action = $this->sanitize($_GET['action'], 'index');
|
||||
$plugin = ! empty($_GET['plugin']) ? $this->sanitize($_GET['plugin'], '') : '';
|
||||
} else {
|
||||
list($this->controller, $this->action) = $this->findRoute($this->getPath($uri, $query_string)); // TODO: add plugin for routes
|
||||
$plugin = '';
|
||||
$class = $this->getControllerClassName();
|
||||
|
||||
if (! class_exists($class)) {
|
||||
throw new RuntimeException('Controller not found');
|
||||
}
|
||||
|
||||
$class = '\Kanboard\\';
|
||||
$class .= empty($plugin) ? 'Controller\\'.ucfirst($this->controller) : 'Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($this->controller);
|
||||
|
||||
if (! class_exists($class) || ! method_exists($class, $this->action)) {
|
||||
throw new RuntimeException('Controller or method not found for the given url!');
|
||||
if (! method_exists($class, $this->action)) {
|
||||
throw new RuntimeException('Action not implemented');
|
||||
}
|
||||
|
||||
$instance = new $class($this->container);
|
||||
$instance->beforeAction($this->controller, $this->action);
|
||||
$instance->{$this->action}();
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controller class name
|
||||
*
|
||||
* @access private
|
||||
* @return string
|
||||
*/
|
||||
private function getControllerClassName()
|
||||
{
|
||||
if ($this->plugin !== '') {
|
||||
return '\Kanboard\Plugin\\'.$this->plugin.'\Controller\\'.$this->controller;
|
||||
}
|
||||
|
||||
return '\Kanboard\Controller\\'.$this->controller;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user