summaryrefslogtreecommitdiff
path: root/app/Core
diff options
context:
space:
mode:
Diffstat (limited to 'app/Core')
-rw-r--r--app/Core/HttpClient.php21
-rw-r--r--app/Core/Lexer.php2
-rw-r--r--app/Core/OAuth2.php120
-rw-r--r--app/Core/Request.php11
-rw-r--r--app/Core/Router.php195
-rw-r--r--app/Core/Session.php2
6 files changed, 304 insertions, 47 deletions
diff --git a/app/Core/HttpClient.php b/app/Core/HttpClient.php
index 805c1e5a..b808f756 100644
--- a/app/Core/HttpClient.php
+++ b/app/Core/HttpClient.php
@@ -32,6 +32,20 @@ class HttpClient extends Base
const HTTP_USER_AGENT = 'Kanboard';
/**
+ * Send a GET HTTP request and parse JSON response
+ *
+ * @access public
+ * @param string $url
+ * @param string[] $headers
+ * @return array
+ */
+ public function getJson($url, array $headers = array())
+ {
+ $response = $this->doRequest('GET', $url, '', array_merge(array('Accept: application/json'), $headers));
+ return json_decode($response, true) ?: array();
+ }
+
+ /**
* Send a POST HTTP request encoded in JSON
*
* @access public
@@ -43,6 +57,7 @@ class HttpClient extends Base
public function postJson($url, array $data, array $headers = array())
{
return $this->doRequest(
+ 'POST',
$url,
json_encode($data),
array_merge(array('Content-type: application/json'), $headers)
@@ -61,6 +76,7 @@ class HttpClient extends Base
public function postForm($url, array $data, array $headers = array())
{
return $this->doRequest(
+ 'POST',
$url,
http_build_query($data),
array_merge(array('Content-type: application/x-www-form-urlencoded'), $headers)
@@ -71,12 +87,13 @@ class HttpClient extends Base
* Make the HTTP request
*
* @access private
+ * @param string $method
* @param string $url
* @param string $content
* @param string[] $headers
* @return string
*/
- private function doRequest($url, $content, array $headers)
+ private function doRequest($method, $url, $content, array $headers)
{
if (empty($url)) {
return '';
@@ -86,7 +103,7 @@ class HttpClient extends Base
$context = stream_context_create(array(
'http' => array(
- 'method' => 'POST',
+ 'method' => $method,
'protocol_version' => 1.1,
'timeout' => self::HTTP_TIMEOUT,
'max_redirects' => self::HTTP_MAX_REDIRECTS,
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php
index 3887dc82..0a237254 100644
--- a/app/Core/Lexer.php
+++ b/app/Core/Lexer.php
@@ -33,6 +33,7 @@ class Lexer
"/^(category:)/" => 'T_CATEGORY',
"/^(column:)/" => 'T_COLUMN',
"/^(project:)/" => 'T_PROJECT',
+ "/^(swimlane:)/" => 'T_SWIMLANE',
"/^(ref:)/" => 'T_REFERENCE',
"/^(reference:)/" => 'T_REFERENCE',
"/^(\s+)/" => 'T_WHITESPACE',
@@ -116,6 +117,7 @@ class Lexer
case 'T_CATEGORY':
case 'T_COLUMN':
case 'T_PROJECT':
+ case 'T_SWIMLANE':
$next = next($tokens);
if ($next !== false && $next['token'] === 'T_STRING') {
diff --git a/app/Core/OAuth2.php b/app/Core/OAuth2.php
new file mode 100644
index 00000000..a7d04f33
--- /dev/null
+++ b/app/Core/OAuth2.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Core;
+
+/**
+ * OAuth2 client
+ *
+ * @package core
+ * @author Frederic Guillot
+ */
+class OAuth2 extends Base
+{
+ private $clientId;
+ private $secret;
+ private $callbackUrl;
+ private $authUrl;
+ private $tokenUrl;
+ private $scopes;
+ private $tokenType;
+ private $accessToken;
+
+ /**
+ * Create OAuth2 service
+ *
+ * @access public
+ * @param string $clientId
+ * @param string $secret
+ * @param string $callbackUrl
+ * @param string $authUrl
+ * @param string $tokenUrl
+ * @param array $scopes
+ * @return OAuth2
+ */
+ public function createService($clientId, $secret, $callbackUrl, $authUrl, $tokenUrl, array $scopes)
+ {
+ $this->clientId = $clientId;
+ $this->secret = $secret;
+ $this->callbackUrl = $callbackUrl;
+ $this->authUrl = $authUrl;
+ $this->tokenUrl = $tokenUrl;
+ $this->scopes = $scopes;
+
+ return $this;
+ }
+
+ /**
+ * Get authorization url
+ *
+ * @access public
+ * @return string
+ */
+ public function getAuthorizationUrl()
+ {
+ $params = array(
+ 'response_type' => 'code',
+ 'client_id' => $this->clientId,
+ 'redirect_uri' => $this->callbackUrl,
+ 'scope' => implode(' ', $this->scopes),
+ );
+
+ return $this->authUrl.'?'.http_build_query($params);
+ }
+
+ /**
+ * Get authorization header
+ *
+ * @access public
+ * @return string
+ */
+ public function getAuthorizationHeader()
+ {
+ if (strtolower($this->tokenType) === 'bearer') {
+ return 'Authorization: Bearer '.$this->accessToken;
+ }
+
+ return '';
+ }
+
+ /**
+ * Get access token
+ *
+ * @access public
+ * @param string $code
+ * @return string
+ */
+ public function getAccessToken($code)
+ {
+ if (empty($this->accessToken) && ! empty($code)) {
+
+ $params = array(
+ 'code' => $code,
+ 'client_id' => $this->clientId,
+ 'client_secret' => $this->secret,
+ 'redirect_uri' => $this->callbackUrl,
+ 'grant_type' => 'authorization_code',
+ );
+
+ $response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true);
+
+ $this->tokenType = isset($response['token_type']) ? $response['token_type'] : '';
+ $this->accessToken = isset($response['access_token']) ? $response['access_token'] : '';
+ }
+
+ return $this->accessToken;
+ }
+
+ /**
+ * Set access token
+ *
+ * @access public
+ * @param string $token
+ * @param string $type
+ * @return string
+ */
+ public function setAccessToken($token, $type = 'bearer')
+ {
+ $this->accessToken = $token;
+ $this->tokenType = $type;
+ }
+}
diff --git a/app/Core/Request.php b/app/Core/Request.php
index b399a1f0..1eff66fa 100644
--- a/app/Core/Request.php
+++ b/app/Core/Request.php
@@ -163,6 +163,17 @@ class Request
}
/**
+ * Returns uri
+ *
+ * @access public
+ * @return string
+ */
+ public function getUri()
+ {
+ return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
+ }
+
+ /**
* Get the user agent
*
* @static
diff --git a/app/Core/Router.php b/app/Core/Router.php
index 36c11a0a..ae989de5 100644
--- a/app/Core/Router.php
+++ b/app/Core/Router.php
@@ -2,53 +2,151 @@
namespace Core;
-use Pimple\Container;
-
/**
* Router class
*
* @package core
* @author Frederic Guillot
*/
-class Router
+class Router extends Base
{
/**
- * Controller name
+ * Store routes for path lookup
*
* @access private
- * @var string
+ * @var array
*/
- private $controller = '';
+ private $paths = array();
/**
- * Action name
+ * Store routes for url lookup
*
* @access private
- * @var string
+ * @var array
*/
- private $action = '';
+ private $urls = array();
/**
- * Container instance
+ * Get the path to compare patterns
*
- * @access private
- * @var \Pimple\Container
+ * @access public
+ * @param string $uri
+ * @param string $query_string
+ * @return string
*/
- private $container;
+ public function getPath($uri, $query_string = '')
+ {
+ $path = substr($uri, strlen($this->helper->url->dir()));
+
+ if (! empty($query_string)) {
+ $path = substr($path, 0, - strlen($query_string) - 1);
+ }
+
+ if ($path{0} === '/') {
+ $path = substr($path, 1);
+ }
+
+ return $path;
+ }
+
+ /**
+ * Add route
+ *
+ * @access public
+ * @param string $path
+ * @param string $controller
+ * @param string $action
+ * @param array $params
+ */
+ public function addRoute($path, $controller, $action, array $params = array())
+ {
+ $pattern = explode('/', $path);
+
+ $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];
+ }
+ else if ($route['pattern'][$i] !== $parts[$i]) {
+ break;
+ }
+ }
+
+ if ($i === $count) {
+ $_GET = array_merge($_GET, $params);
+ return array($route['controller'], $route['action']);
+ }
+ }
+ }
+
+ return array('app', 'index');
+ }
/**
- * Constructor
+ * Find route url
*
* @access public
- * @param \Pimple\Container $container Container instance
- * @param string $controller Controller name
- * @param string $action Action name
+ * @param string $controller
+ * @param string $action
+ * @param array $params
+ * @return string
*/
- public function __construct(Container $container, $controller = '', $action = '')
+ public function findUrl($controller, $action, array $params = array())
{
- $this->container = $container;
- $this->controller = empty($_GET['controller']) ? $controller : $_GET['controller'];
- $this->action = empty($_GET['action']) ? $action : $_GET['action'];
+ 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 '';
}
/**
@@ -65,15 +163,42 @@ class Router
}
/**
- * Load a controller and execute the action
+ * Find controller/action from the route table or from get arguments
*
* @access public
- * @param string $filename Controller filename
- * @param string $class Class name
- * @param string $method Method name
+ * @param string $uri
+ * @param string $query_string
+ * @return boolean
+ */
+ public function dispatch($uri, $query_string = '')
+ {
+ if (! empty($_GET['controller']) && ! empty($_GET['action'])) {
+ $controller = $this->sanitize($_GET['controller'], 'app');
+ $action = $this->sanitize($_GET['action'], 'index');
+ }
+ else {
+ list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string));
+ }
+
+ return $this->load(
+ __DIR__.'/../Controller/'.ucfirst($controller).'.php',
+ $controller,
+ '\Controller\\'.ucfirst($controller),
+ $action
+ );
+ }
+
+ /**
+ * Load a controller and execute the action
+ *
+ * @access private
+ * @param string $filename
+ * @param string $controller
+ * @param string $class
+ * @param string $method
* @return bool
*/
- public function load($filename, $class, $method)
+ private function load($filename, $controller, $class, $method)
{
if (file_exists($filename)) {
@@ -84,7 +209,7 @@ class Router
}
$instance = new $class($this->container);
- $instance->beforeAction($this->controller, $this->action);
+ $instance->beforeAction($controller, $method);
$instance->$method();
return true;
@@ -92,20 +217,4 @@ class Router
return false;
}
-
- /**
- * Find a route
- *
- * @access public
- */
- public function execute()
- {
- $this->controller = $this->sanitize($this->controller, 'app');
- $this->action = $this->sanitize($this->action, 'index');
- $filename = __DIR__.'/../Controller/'.ucfirst($this->controller).'.php';
-
- if (! $this->load($filename, '\Controller\\'.$this->controller, $this->action)) {
- die('Page not found!');
- }
- }
}
diff --git a/app/Core/Session.php b/app/Core/Session.php
index c35014cd..0e5f7426 100644
--- a/app/Core/Session.php
+++ b/app/Core/Session.php
@@ -41,8 +41,6 @@ class Session implements ArrayAccess
*/
public function open($base_path = '/')
{
- $base_path = str_replace('\\', '/', $base_path);
-
// HttpOnly and secure flags for session cookie
session_set_cookie_params(
self::SESSION_LIFETIME,