summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/Action.php2
-rw-r--r--app/Controller/ActionCreation.php2
-rw-r--r--app/Controller/ActionProject.php2
-rw-r--r--app/Controller/Activity.php2
-rw-r--r--app/Controller/Analytic.php2
-rw-r--r--app/Controller/AppController.php45
-rw-r--r--app/Controller/Auth.php28
-rw-r--r--app/Controller/AvatarFile.php6
-rw-r--r--app/Controller/Base.php290
-rw-r--r--app/Controller/BaseController.php158
-rw-r--r--app/Controller/Board.php28
-rw-r--r--app/Controller/BoardPopover.php2
-rw-r--r--app/Controller/BoardTooltip.php2
-rw-r--r--app/Controller/Calendar.php2
-rw-r--r--app/Controller/Captcha.php4
-rw-r--r--app/Controller/Category.php31
-rw-r--r--app/Controller/Column.php19
-rw-r--r--app/Controller/Comment.php23
-rw-r--r--app/Controller/Config.php97
-rw-r--r--app/Controller/Currency.php8
-rw-r--r--app/Controller/Customfilter.php13
-rw-r--r--app/Controller/DashboardController.php (renamed from app/Controller/App.php)38
-rw-r--r--app/Controller/Doc.php2
-rw-r--r--app/Controller/Export.php10
-rw-r--r--app/Controller/Feed.php9
-rw-r--r--app/Controller/FileViewer.php48
-rw-r--r--app/Controller/Gantt.php17
-rw-r--r--app/Controller/Group.php22
-rw-r--r--app/Controller/GroupHelper.php2
-rw-r--r--app/Controller/Ical.php9
-rw-r--r--app/Controller/Link.php20
-rw-r--r--app/Controller/Listing.php2
-rw-r--r--app/Controller/Oauth.php4
-rw-r--r--app/Controller/PasswordReset.php18
-rw-r--r--app/Controller/Project.php45
-rw-r--r--app/Controller/ProjectCreation.php8
-rw-r--r--app/Controller/ProjectEdit.php14
-rw-r--r--app/Controller/ProjectFile.php2
-rw-r--r--app/Controller/ProjectOverview.php2
-rw-r--r--app/Controller/ProjectPermission.php9
-rw-r--r--app/Controller/Projectuser.php4
-rw-r--r--app/Controller/Search.php2
-rw-r--r--app/Controller/Subtask.php24
-rw-r--r--app/Controller/SubtaskRestriction.php2
-rw-r--r--app/Controller/SubtaskStatus.php2
-rw-r--r--app/Controller/Swimlane.php42
-rw-r--r--app/Controller/Task.php16
-rw-r--r--app/Controller/TaskBulk.php2
-rw-r--r--app/Controller/TaskExternalLink.php27
-rw-r--r--app/Controller/TaskFile.php4
-rw-r--r--app/Controller/TaskHelper.php2
-rw-r--r--app/Controller/TaskImport.php7
-rw-r--r--app/Controller/TaskInternalLink.php19
-rw-r--r--app/Controller/TaskPopover.php2
-rw-r--r--app/Controller/TaskRecurrence.php10
-rw-r--r--app/Controller/Taskcreation.php9
-rw-r--r--app/Controller/Taskduplication.php16
-rw-r--r--app/Controller/Taskmodification.php14
-rw-r--r--app/Controller/Taskstatus.php4
-rw-r--r--app/Controller/Twofactor.php12
-rw-r--r--app/Controller/User.php52
-rw-r--r--app/Controller/UserHelper.php12
-rw-r--r--app/Controller/UserImport.php4
-rw-r--r--app/Controller/UserStatus.php2
-rw-r--r--app/Controller/WebNotification.php6
-rw-r--r--app/Controller/Webhook.php6
-rw-r--r--app/Core/Controller/AccessForbiddenException.php14
-rw-r--r--app/Core/Controller/BaseException.php52
-rw-r--r--app/Core/Controller/BaseMiddleware.php58
-rw-r--r--app/Core/Controller/PageNotFoundException.php14
-rw-r--r--app/Core/Controller/Runner.php102
-rw-r--r--app/Core/Http/Response.php355
-rw-r--r--app/Core/Http/Route.php4
-rw-r--r--app/Core/Http/Router.php62
-rw-r--r--app/Helper/LayoutHelper.php2
-rw-r--r--app/Middleware/ApplicationAuthorizationMiddleware.php27
-rw-r--r--app/Middleware/AuthenticationMiddleware.php56
-rw-r--r--app/Middleware/BootstrapMiddleware.php44
-rw-r--r--app/Middleware/PostAuthenticationMiddleware.php36
-rw-r--r--app/Middleware/ProjectAuthorizationMiddleware.php34
-rw-r--r--app/ServiceProvider/RouteProvider.php16
-rw-r--r--app/Subscriber/BootstrapSubscriber.php2
-rw-r--r--app/Template/app/sidebar.php27
-rw-r--r--app/Template/config/application.php2
-rw-r--r--app/Template/config/board.php2
-rw-r--r--app/Template/config/calendar.php4
-rw-r--r--app/Template/config/integrations.php4
-rw-r--r--app/Template/config/layout.php3
-rw-r--r--app/Template/config/project.php2
-rw-r--r--app/Template/config/webhook.php4
-rw-r--r--app/Template/dashboard/activity.php (renamed from app/Template/app/activity.php)0
-rw-r--r--app/Template/dashboard/calendar.php (renamed from app/Template/app/calendar.php)0
-rw-r--r--app/Template/dashboard/layout.php (renamed from app/Template/app/layout.php)0
-rw-r--r--app/Template/dashboard/notifications.php (renamed from app/Template/app/notifications.php)0
-rw-r--r--app/Template/dashboard/projects.php (renamed from app/Template/app/projects.php)2
-rw-r--r--app/Template/dashboard/show.php (renamed from app/Template/app/overview.php)6
-rw-r--r--app/Template/dashboard/sidebar.php27
-rw-r--r--app/Template/dashboard/subtasks.php (renamed from app/Template/app/subtasks.php)4
-rw-r--r--app/Template/dashboard/tasks.php (renamed from app/Template/app/tasks.php)2
-rw-r--r--app/Template/header.php6
-rw-r--r--app/Template/layout.php2
-rw-r--r--app/Template/user/sidebar.php4
102 files changed, 1411 insertions, 917 deletions
diff --git a/app/Controller/Action.php b/app/Controller/Action.php
index 8881e8ec..40497a62 100644
--- a/app/Controller/Action.php
+++ b/app/Controller/Action.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Action extends Base
+class Action extends BaseController
{
/**
* List of automatic actions for a given project
diff --git a/app/Controller/ActionCreation.php b/app/Controller/ActionCreation.php
index 61b7b5e2..388b30e2 100644
--- a/app/Controller/ActionCreation.php
+++ b/app/Controller/ActionCreation.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ActionCreation extends Base
+class ActionCreation extends BaseController
{
/**
* Show the form (step 1)
diff --git a/app/Controller/ActionProject.php b/app/Controller/ActionProject.php
index e5063f73..10b3c9d4 100644
--- a/app/Controller/ActionProject.php
+++ b/app/Controller/ActionProject.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ActionProject extends Base
+class ActionProject extends BaseController
{
public function project()
{
diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php
index 47a66e0a..0c6aa3f3 100644
--- a/app/Controller/Activity.php
+++ b/app/Controller/Activity.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Activity extends Base
+class Activity extends BaseController
{
/**
* Activity page for a project
diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php
index 35bc3048..ba73c15c 100644
--- a/app/Controller/Analytic.php
+++ b/app/Controller/Analytic.php
@@ -11,7 +11,7 @@ use Kanboard\Model\Task as TaskModel;
* @package controller
* @author Frederic Guillot
*/
-class Analytic extends Base
+class Analytic extends BaseController
{
/**
* Show average Lead and Cycle time
diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php
new file mode 100644
index 00000000..60bc154a
--- /dev/null
+++ b/app/Controller/AppController.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class AppController
+ *
+ * @package Kanboard\Controller
+ */
+class AppController extends Base
+{
+ /**
+ * Forbidden page
+ *
+ * @access public
+ * @param bool $withoutLayout
+ */
+ public function accessForbidden($withoutLayout = false)
+ {
+ if ($this->request->isAjax()) {
+ $this->response->json(array('message' => 'Access Forbidden'), 403);
+ }
+
+ $this->response->html($this->helper->layout->app('app/forbidden', array(
+ 'title' => t('Access Forbidden'),
+ 'no_layout' => $withoutLayout,
+ )));
+ }
+
+ /**
+ * Page not found
+ *
+ * @access public
+ * @param boolean $withoutLayout
+ */
+ public function notFound($withoutLayout = false)
+ {
+ $this->response->html($this->helper->layout->app('app/notfound', array(
+ 'title' => t('Page not found'),
+ 'no_layout' => $withoutLayout,
+ )));
+ }
+}
diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php
index b882a720..cad44a34 100644
--- a/app/Controller/Auth.php
+++ b/app/Controller/Auth.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Auth extends Base
+class Auth extends BaseController
{
/**
* Display the form login
@@ -20,16 +20,16 @@ class Auth extends Base
public function login(array $values = array(), array $errors = array())
{
if ($this->userSession->isLogged()) {
- $this->response->redirect($this->helper->url->to('app', 'index'));
+ $this->response->redirect($this->helper->url->to('DashboardController', 'show'));
+ } else {
+ $this->response->html($this->helper->layout->app('auth/index', array(
+ 'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']),
+ 'errors' => $errors,
+ 'values' => $values,
+ 'no_layout' => true,
+ 'title' => t('Login')
+ )));
}
-
- $this->response->html($this->helper->layout->app('auth/index', array(
- 'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']),
- 'errors' => $errors,
- 'values' => $values,
- 'no_layout' => true,
- 'title' => t('Login')
- )));
}
/**
@@ -45,9 +45,9 @@ class Auth extends Base
if ($valid) {
$this->redirectAfterLogin();
+ } else {
+ $this->login($values, $errors);
}
-
- $this->login($values, $errors);
}
/**
@@ -76,8 +76,8 @@ class Auth extends Base
$redirect = $this->sessionStorage->redirectAfterLogin;
unset($this->sessionStorage->redirectAfterLogin);
$this->response->redirect($redirect);
+ } else {
+ $this->response->redirect($this->helper->url->to('DashboardController', 'show'));
}
-
- $this->response->redirect($this->helper->url->to('app', 'index'));
}
}
diff --git a/app/Controller/AvatarFile.php b/app/Controller/AvatarFile.php
index 45cb1615..67c3efe1 100644
--- a/app/Controller/AvatarFile.php
+++ b/app/Controller/AvatarFile.php
@@ -11,7 +11,7 @@ use Kanboard\Core\Thumbnail;
* @package controller
* @author Frederic Guillot
*/
-class AvatarFile extends Base
+class AvatarFile extends BaseController
{
/**
* Display avatar page
@@ -61,8 +61,8 @@ class AvatarFile extends Base
$filename = $this->avatarFile->getFilename($user_id);
$etag = md5($filename.$size);
- $this->response->cache(365 * 86400, $etag);
- $this->response->contentType('image/jpeg');
+ $this->response->withCache(365 * 86400, $etag);
+ $this->response->withContentType('image/jpeg');
if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') {
$this->render($filename, $size);
diff --git a/app/Controller/Base.php b/app/Controller/Base.php
deleted file mode 100644
index beb56909..00000000
--- a/app/Controller/Base.php
+++ /dev/null
@@ -1,290 +0,0 @@
-<?php
-
-namespace Kanboard\Controller;
-
-use Kanboard\Core\Security\Role;
-
-/**
- * Base controller
- *
- * @package controller
- * @author Frederic Guillot
- */
-abstract class Base extends \Kanboard\Core\Base
-{
- /**
- * Method executed before each action
- *
- * @access public
- */
- public function beforeAction()
- {
- $this->sessionManager->open();
- $this->dispatcher->dispatch('app.bootstrap');
- $this->sendHeaders();
- $this->authenticationManager->checkCurrentSession();
-
- if (! $this->applicationAuthorization->isAllowed($this->router->getController(), $this->router->getAction(), Role::APP_PUBLIC)) {
- $this->handleAuthentication();
- $this->handlePostAuthentication();
- $this->checkApplicationAuthorization();
- $this->checkProjectAuthorization();
- }
- }
-
- /**
- * Send HTTP headers
- *
- * @access private
- */
- private function sendHeaders()
- {
- // HTTP secure headers
- $this->response->csp($this->container['cspRules']);
- $this->response->nosniff();
- $this->response->xss();
-
- // Allow the public board iframe inclusion
- if (ENABLE_XFRAME && $this->router->getAction() !== 'readonly') {
- $this->response->xframe();
- }
-
- if (ENABLE_HSTS) {
- $this->response->hsts();
- }
- }
-
- /**
- * Check authentication
- *
- * @access private
- */
- private function handleAuthentication()
- {
- if (! $this->userSession->isLogged() && ! $this->authenticationManager->preAuthentication()) {
- if ($this->request->isAjax()) {
- $this->response->text('Not Authorized', 401);
- }
-
- $this->sessionStorage->redirectAfterLogin = $this->request->getUri();
- $this->response->redirect($this->helper->url->to('auth', 'login'));
- }
- }
-
- /**
- * Handle Post-Authentication (2FA)
- *
- * @access private
- */
- private function handlePostAuthentication()
- {
- $controller = strtolower($this->router->getController());
- $action = strtolower($this->router->getAction());
- $ignore = ($controller === 'twofactor' && in_array($action, array('code', 'check'))) || ($controller === 'auth' && $action === 'logout');
-
- if ($ignore === false && $this->userSession->hasPostAuthentication() && ! $this->userSession->isPostAuthenticationValidated()) {
- if ($this->request->isAjax()) {
- $this->response->text('Not Authorized', 401);
- }
-
- $this->response->redirect($this->helper->url->to('twofactor', 'code'));
- }
- }
-
- /**
- * Check application authorization
- *
- * @access private
- */
- private function checkApplicationAuthorization()
- {
- if (! $this->helper->user->hasAccess($this->router->getController(), $this->router->getAction())) {
- $this->forbidden();
- }
- }
-
- /**
- * Check project authorization
- *
- * @access private
- */
- private function checkProjectAuthorization()
- {
- $project_id = $this->request->getIntegerParam('project_id');
- $task_id = $this->request->getIntegerParam('task_id');
-
- // Allow urls without "project_id"
- if ($task_id > 0 && $project_id === 0) {
- $project_id = $this->taskFinder->getProjectId($task_id);
- }
-
- if ($project_id > 0 && ! $this->helper->user->hasProjectAccess($this->router->getController(), $this->router->getAction(), $project_id)) {
- $this->forbidden();
- }
- }
-
- /**
- * Application not found page (404 error)
- *
- * @access protected
- * @param boolean $no_layout Display the layout or not
- */
- protected function notfound($no_layout = false)
- {
- $this->response->html($this->helper->layout->app('app/notfound', array(
- 'title' => t('Page not found'),
- 'no_layout' => $no_layout,
- )));
- }
-
- /**
- * Application forbidden page
- *
- * @access protected
- * @param boolean $no_layout Display the layout or not
- */
- protected function forbidden($no_layout = false)
- {
- if ($this->request->isAjax()) {
- $this->response->text('Access Forbidden', 403);
- }
-
- $this->response->html($this->helper->layout->app('app/forbidden', array(
- 'title' => t('Access Forbidden'),
- 'no_layout' => $no_layout,
- )));
- }
-
- /**
- * Check if the CSRF token from the URL is correct
- *
- * @access protected
- */
- protected function checkCSRFParam()
- {
- if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
- $this->forbidden();
- }
- }
-
- /**
- * Check webhook token
- *
- * @access protected
- */
- protected function checkWebhookToken()
- {
- if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
- $this->response->text('Not Authorized', 401);
- }
- }
-
- /**
- * Common method to get a task for task views
- *
- * @access protected
- * @return array
- */
- protected function getTask()
- {
- $project_id = $this->request->getIntegerParam('project_id');
- $task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
-
- if (empty($task)) {
- $this->notfound();
- }
-
- if ($project_id !== 0 && $project_id != $task['project_id']) {
- $this->forbidden();
- }
-
- return $task;
- }
-
- /**
- * Get Task or Project file
- *
- * @access protected
- */
- protected function getFile()
- {
- $task_id = $this->request->getIntegerParam('task_id');
- $file_id = $this->request->getIntegerParam('file_id');
- $model = 'projectFile';
-
- if ($task_id > 0) {
- $model = 'taskFile';
- $project_id = $this->taskFinder->getProjectId($task_id);
-
- if ($project_id !== $this->request->getIntegerParam('project_id')) {
- $this->forbidden();
- }
- }
-
- $file = $this->$model->getById($file_id);
-
- if (empty($file)) {
- $this->notfound();
- }
-
- $file['model'] = $model;
- return $file;
- }
-
- /**
- * Common method to get a project
- *
- * @access protected
- * @param integer $project_id Default project id
- * @return array
- */
- protected function getProject($project_id = 0)
- {
- $project_id = $this->request->getIntegerParam('project_id', $project_id);
- $project = $this->project->getByIdWithOwner($project_id);
-
- if (empty($project)) {
- $this->notfound();
- }
-
- return $project;
- }
-
- /**
- * Common method to get the user
- *
- * @access protected
- * @return array
- */
- protected function getUser()
- {
- $user = $this->user->getById($this->request->getIntegerParam('user_id', $this->userSession->getId()));
-
- if (empty($user)) {
- $this->notfound();
- }
-
- if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) {
- $this->forbidden();
- }
-
- return $user;
- }
-
- /**
- * Get the current subtask
- *
- * @access protected
- * @return array
- */
- protected function getSubtask()
- {
- $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
-
- if (empty($subtask)) {
- $this->notfound();
- }
-
- return $subtask;
- }
-}
diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php
new file mode 100644
index 00000000..ad02f708
--- /dev/null
+++ b/app/Controller/BaseController.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\PageNotFoundException;
+
+/**
+ * Base Controller
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+abstract class BaseController extends Base
+{
+ /**
+ * Check if the CSRF token from the URL is correct
+ *
+ * @access protected
+ */
+ protected function checkCSRFParam()
+ {
+ if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
+ throw new AccessForbiddenException();
+ }
+ }
+
+ /**
+ * Check webhook token
+ *
+ * @access protected
+ */
+ protected function checkWebhookToken()
+ {
+ if ($this->config->get('webhook_token') !== $this->request->getStringParam('token')) {
+ $this->response->text('Not Authorized', 401);
+ }
+ }
+
+ /**
+ * Common method to get a task for task views
+ *
+ * @access protected
+ * @return array
+ * @throws PageNotFoundException
+ * @throws AccessForbiddenException
+ */
+ protected function getTask()
+ {
+ $project_id = $this->request->getIntegerParam('project_id');
+ $task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
+
+ if (empty($task)) {
+ throw new PageNotFoundException();
+ }
+
+ if ($project_id !== 0 && $project_id != $task['project_id']) {
+ throw new AccessForbiddenException();
+ }
+
+ return $task;
+ }
+
+ /**
+ * Get Task or Project file
+ *
+ * @access protected
+ * @return array
+ * @throws PageNotFoundException
+ * @throws AccessForbiddenException
+ */
+ protected function getFile()
+ {
+ $task_id = $this->request->getIntegerParam('task_id');
+ $file_id = $this->request->getIntegerParam('file_id');
+ $model = 'projectFile';
+
+ if ($task_id > 0) {
+ $model = 'taskFile';
+ $project_id = $this->taskFinder->getProjectId($task_id);
+
+ if ($project_id !== $this->request->getIntegerParam('project_id')) {
+ throw new AccessForbiddenException();
+ }
+ }
+
+ $file = $this->$model->getById($file_id);
+
+ if (empty($file)) {
+ throw new PageNotFoundException();
+ }
+
+ $file['model'] = $model;
+ return $file;
+ }
+
+ /**
+ * Common method to get a project
+ *
+ * @access protected
+ * @param integer $project_id Default project id
+ * @return array
+ * @throws PageNotFoundException
+ */
+ protected function getProject($project_id = 0)
+ {
+ $project_id = $this->request->getIntegerParam('project_id', $project_id);
+ $project = $this->project->getByIdWithOwner($project_id);
+
+ if (empty($project)) {
+ throw new PageNotFoundException();
+ }
+
+ return $project;
+ }
+
+ /**
+ * Common method to get the user
+ *
+ * @access protected
+ * @return array
+ * @throws PageNotFoundException
+ * @throws AccessForbiddenException
+ */
+ protected function getUser()
+ {
+ $user = $this->user->getById($this->request->getIntegerParam('user_id', $this->userSession->getId()));
+
+ if (empty($user)) {
+ throw new PageNotFoundException();
+ }
+
+ if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) {
+ throw new AccessForbiddenException();
+ }
+
+ return $user;
+ }
+
+ /**
+ * Get the current subtask
+ *
+ * @access protected
+ * @return array
+ * @throws PageNotFoundException
+ */
+ protected function getSubtask()
+ {
+ $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
+
+ if (empty($subtask)) {
+ throw new PageNotFoundException();
+ }
+
+ return $subtask;
+ }
+}
diff --git a/app/Controller/Board.php b/app/Controller/Board.php
index 67e99b81..0f6b3b14 100644
--- a/app/Controller/Board.php
+++ b/app/Controller/Board.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Formatter\BoardFormatter;
/**
@@ -10,7 +11,7 @@ use Kanboard\Formatter\BoardFormatter;
* @package controller
* @author Frederic Guillot
*/
-class Board extends Base
+class Board extends BaseController
{
/**
* Display the public version of a board
@@ -25,7 +26,7 @@ class Board extends Base
// Token verification
if (empty($project)) {
- $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
// Display the board with a specific layout
@@ -74,7 +75,7 @@ class Board extends Base
$project_id = $this->request->getIntegerParam('project_id');
if (! $project_id || ! $this->request->isAjax()) {
- return $this->response->status(403);
+ throw new AccessForbiddenException();
}
$values = $this->request->getJson();
@@ -88,10 +89,10 @@ class Board extends Base
);
if (! $result) {
- return $this->response->status(400);
+ $this->response->status(400);
+ } else {
+ $this->response->html($this->renderBoard($project_id), 201);
}
-
- $this->response->html($this->renderBoard($project_id), 201);
}
/**
@@ -105,14 +106,12 @@ class Board extends Base
$timestamp = $this->request->getIntegerParam('timestamp');
if (! $project_id || ! $this->request->isAjax()) {
- return $this->response->status(403);
- }
-
- if (! $this->project->isModifiedSince($project_id, $timestamp)) {
- return $this->response->status(304);
+ $this->response->status(403);
+ } elseif (! $this->project->isModifiedSince($project_id, $timestamp)) {
+ $this->response->status(304);
+ } else {
+ $this->response->html($this->renderBoard($project_id));
}
-
- return $this->response->html($this->renderBoard($project_id));
}
/**
@@ -125,7 +124,7 @@ class Board extends Base
$project_id = $this->request->getIntegerParam('project_id');
if (! $project_id || ! $this->request->isAjax()) {
- return $this->response->status(403);
+ throw new AccessForbiddenException();
}
$values = $this->request->getJson();
@@ -177,6 +176,7 @@ class Board extends Base
*
* @access private
* @param integer $project_id
+ * @return string
*/
private function renderBoard($project_id)
{
diff --git a/app/Controller/BoardPopover.php b/app/Controller/BoardPopover.php
index b1a017f4..d3117f78 100644
--- a/app/Controller/BoardPopover.php
+++ b/app/Controller/BoardPopover.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class BoardPopover extends Base
+class BoardPopover extends BaseController
{
/**
* Confirmation before to close all column tasks
diff --git a/app/Controller/BoardTooltip.php b/app/Controller/BoardTooltip.php
index c7819bc1..49d02ced 100644
--- a/app/Controller/BoardTooltip.php
+++ b/app/Controller/BoardTooltip.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class BoardTooltip extends Base
+class BoardTooltip extends BaseController
{
/**
* Get links on mouseover
diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php
index 2517286d..706c1d3b 100644
--- a/app/Controller/Calendar.php
+++ b/app/Controller/Calendar.php
@@ -14,7 +14,7 @@ use Kanboard\Model\Task as TaskModel;
* @author Frederic Guillot
* @author Timo Litzbarski
*/
-class Calendar extends Base
+class Calendar extends BaseController
{
/**
* Show calendar view for projects
diff --git a/app/Controller/Captcha.php b/app/Controller/Captcha.php
index fcf081ea..fb9d6fe3 100644
--- a/app/Controller/Captcha.php
+++ b/app/Controller/Captcha.php
@@ -10,7 +10,7 @@ use Gregwar\Captcha\CaptchaBuilder;
* @package controller
* @author Frederic Guillot
*/
-class Captcha extends Base
+class Captcha extends BaseController
{
/**
* Display captcha image
@@ -19,7 +19,7 @@ class Captcha extends Base
*/
public function image()
{
- $this->response->contentType('image/jpeg');
+ $this->response->withContentType('image/jpeg');
$builder = new CaptchaBuilder;
$builder->build();
diff --git a/app/Controller/Category.php b/app/Controller/Category.php
index 258a3b78..954d92cc 100644
--- a/app/Controller/Category.php
+++ b/app/Controller/Category.php
@@ -2,28 +2,29 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
+
/**
* Category management
*
* @package controller
* @author Frederic Guillot
*/
-class Category extends Base
+class Category extends BaseController
{
/**
* Get the category (common method between actions)
*
* @access private
- * @param integer $project_id
* @return array
+ * @throws PageNotFoundException
*/
- private function getCategory($project_id)
+ private function getCategory()
{
$category = $this->category->getById($this->request->getIntegerParam('category_id'));
if (empty($category)) {
- $this->flash->failure(t('Category not found.'));
- $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project_id)));
+ throw new PageNotFoundException();
}
return $category;
@@ -33,6 +34,9 @@ class Category extends Base
* List of categories for a given project
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
*/
public function index(array $values = array(), array $errors = array())
{
@@ -62,24 +66,27 @@ class Category extends Base
if ($valid) {
if ($this->category->create($values)) {
$this->flash->success(t('Your category have been created successfully.'));
- $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to create your category.'));
}
}
- $this->index($values, $errors);
+ return $this->index($values, $errors);
}
/**
* Edit a category (display the form)
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
- $category = $this->getCategory($project['id']);
+ $category = $this->getCategory();
$this->response->html($this->helper->layout->project('category/edit', array(
'values' => empty($values) ? $category : $values,
@@ -104,13 +111,13 @@ class Category extends Base
if ($valid) {
if ($this->category->update($values)) {
$this->flash->success(t('Your category have been updated successfully.'));
- $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to update your category.'));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
@@ -121,7 +128,7 @@ class Category extends Base
public function confirm()
{
$project = $this->getProject();
- $category = $this->getCategory($project['id']);
+ $category = $this->getCategory();
$this->response->html($this->helper->layout->project('category/remove', array(
'project' => $project,
@@ -139,7 +146,7 @@ class Category extends Base
{
$this->checkCSRFParam();
$project = $this->getProject();
- $category = $this->getCategory($project['id']);
+ $category = $this->getCategory();
if ($this->category->remove($category['id'])) {
$this->flash->success(t('Category removed successfully.'));
diff --git a/app/Controller/Column.php b/app/Controller/Column.php
index bbe12fe1..294c31d8 100644
--- a/app/Controller/Column.php
+++ b/app/Controller/Column.php
@@ -2,13 +2,15 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+
/**
* Column controller
*
* @package controller
* @author Frederic Guillot
*/
-class Column extends Base
+class Column extends BaseController
{
/**
* Display columns list
@@ -31,6 +33,9 @@ class Column extends Base
* Show form to create a new column
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -69,7 +74,7 @@ class Column extends Base
}
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
@@ -108,13 +113,13 @@ class Column extends Base
if ($valid) {
if ($this->column->update($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
$this->flash->success(t('Board updated successfully.'));
- $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to update this board.'));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
@@ -129,10 +134,10 @@ class Column extends Base
if (! empty($values) && isset($values['column_id']) && isset($values['position'])) {
$result = $this->column->changePosition($project['id'], $values['column_id'], $values['position']);
- return $this->response->json(array('result' => $result));
+ $this->response->json(array('result' => $result));
+ } else {
+ throw new AccessForbiddenException();
}
-
- $this->forbidden();
}
/**
diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php
index 0b39f390..83a67b41 100644
--- a/app/Controller/Comment.php
+++ b/app/Controller/Comment.php
@@ -2,30 +2,35 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\PageNotFoundException;
+
/**
* Comment controller
*
* @package controller
* @author Frederic Guillot
*/
-class Comment extends Base
+class Comment extends BaseController
{
/**
* Get the current comment
*
* @access private
* @return array
+ * @throws PageNotFoundException
+ * @throws AccessForbiddenException
*/
private function getComment()
{
$comment = $this->comment->getById($this->request->getIntegerParam('comment_id'));
if (empty($comment)) {
- return $this->notfound();
+ throw new PageNotFoundException();
}
if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
- return $this->forbidden();
+ throw new AccessForbiddenException();
}
return $comment;
@@ -35,6 +40,10 @@ class Comment extends Base
* Add comment form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ * @throws PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -76,13 +85,17 @@ class Comment extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
* Edit a comment
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ * @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -121,7 +134,7 @@ class Comment extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), false);
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
diff --git a/app/Controller/Config.php b/app/Controller/Config.php
index 021534cf..ebb541d2 100644
--- a/app/Controller/Config.php
+++ b/app/Controller/Config.php
@@ -8,51 +8,9 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Config extends Base
+class Config extends BaseController
{
/**
- * Common method between pages
- *
- * @access private
- * @param string $redirect Action to redirect after saving the form
- */
- private function common($redirect)
- {
- if ($this->request->isPost()) {
- $values = $this->request->getValues();
-
- switch ($redirect) {
- case 'application':
- $values += array('password_reset' => 0);
- break;
- case 'project':
- $values += array(
- 'subtask_restriction' => 0,
- 'subtask_time_tracking' => 0,
- 'cfd_include_closed_tasks' => 0,
- 'disable_private_project' => 0,
- );
- break;
- case 'integrations':
- $values += array('integration_gravatar' => 0);
- break;
- case 'calendar':
- $values += array('calendar_user_subtasks_time_tracking' => 0);
- break;
- }
-
- if ($this->config->save($values)) {
- $this->language->loadCurrentLanguage();
- $this->flash->success(t('Settings saved successfully.'));
- } else {
- $this->flash->failure(t('Unable to save your settings.'));
- }
-
- $this->response->redirect($this->helper->url->to('config', $redirect));
- }
- }
-
- /**
* Display the about page
*
* @access public
@@ -68,6 +26,45 @@ class Config extends Base
}
/**
+ * Save settings
+ *
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ $redirect = $this->request->getStringParam('redirect', 'application');
+
+ switch ($redirect) {
+ case 'application':
+ $values += array('password_reset' => 0);
+ break;
+ case 'project':
+ $values += array(
+ 'subtask_restriction' => 0,
+ 'subtask_time_tracking' => 0,
+ 'cfd_include_closed_tasks' => 0,
+ 'disable_private_project' => 0,
+ );
+ break;
+ case 'integrations':
+ $values += array('integration_gravatar' => 0);
+ break;
+ case 'calendar':
+ $values += array('calendar_user_subtasks_time_tracking' => 0);
+ break;
+ }
+
+ if ($this->config->save($values)) {
+ $this->language->loadCurrentLanguage();
+ $this->flash->success(t('Settings saved successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to save your settings.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('config', $redirect));
+ }
+
+ /**
* Display the plugin page
*
* @access public
@@ -87,8 +84,6 @@ class Config extends Base
*/
public function application()
{
- $this->common('application');
-
$this->response->html($this->helper->layout->config('config/application', array(
'languages' => $this->language->getLanguages(),
'timezones' => $this->timezone->getTimezones(),
@@ -106,8 +101,6 @@ class Config extends Base
*/
public function project()
{
- $this->common('project');
-
$this->response->html($this->helper->layout->config('config/project', array(
'colors' => $this->color->getList(),
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
@@ -122,8 +115,6 @@ class Config extends Base
*/
public function board()
{
- $this->common('board');
-
$this->response->html($this->helper->layout->config('config/board', array(
'title' => t('Settings').' &gt; '.t('Board settings'),
)));
@@ -136,8 +127,6 @@ class Config extends Base
*/
public function calendar()
{
- $this->common('calendar');
-
$this->response->html($this->helper->layout->config('config/calendar', array(
'title' => t('Settings').' &gt; '.t('Calendar settings'),
)));
@@ -150,8 +139,6 @@ class Config extends Base
*/
public function integrations()
{
- $this->common('integrations');
-
$this->response->html($this->helper->layout->config('config/integrations', array(
'title' => t('Settings').' &gt; '.t('Integrations'),
)));
@@ -164,8 +151,6 @@ class Config extends Base
*/
public function webhook()
{
- $this->common('webhook');
-
$this->response->html($this->helper->layout->config('config/webhook', array(
'title' => t('Settings').' &gt; '.t('Webhook settings'),
)));
@@ -191,7 +176,7 @@ class Config extends Base
public function downloadDb()
{
$this->checkCSRFParam();
- $this->response->forceDownload('db.sqlite.gz');
+ $this->response->withDownload('db.sqlite.gz');
$this->response->binary($this->config->downloadDatabase());
}
diff --git a/app/Controller/Currency.php b/app/Controller/Currency.php
index 3e0de905..872d6929 100644
--- a/app/Controller/Currency.php
+++ b/app/Controller/Currency.php
@@ -8,12 +8,14 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Currency extends Base
+class Currency extends BaseController
{
/**
* Display all currency rates and form
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function index(array $values = array(), array $errors = array())
{
@@ -40,13 +42,13 @@ class Currency extends Base
if ($valid) {
if ($this->currency->create($values['currency'], $values['rate'])) {
$this->flash->success(t('The currency rate have been added successfully.'));
- $this->response->redirect($this->helper->url->to('currency', 'index'));
+ return $this->response->redirect($this->helper->url->to('currency', 'index'));
} else {
$this->flash->failure(t('Unable to add this currency rate.'));
}
}
- $this->index($values, $errors);
+ return $this->index($values, $errors);
}
/**
diff --git a/app/Controller/Customfilter.php b/app/Controller/Customfilter.php
index 41da0b11..d0794366 100644
--- a/app/Controller/Customfilter.php
+++ b/app/Controller/Customfilter.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Security\Role;
/**
@@ -10,7 +11,7 @@ use Kanboard\Core\Security\Role;
* @package controller
* @author Timo Litzbarski
*/
-class Customfilter extends Base
+class Customfilter extends BaseController
{
/**
* Display list of filters
@@ -47,13 +48,13 @@ class Customfilter extends Base
if ($valid) {
if ($this->customFilter->create($values)) {
$this->flash->success(t('Your custom filter have been created successfully.'));
- $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to create your custom filter.'));
}
}
- $this->index($values, $errors);
+ return $this->index($values, $errors);
}
/**
@@ -143,13 +144,13 @@ class Customfilter extends Base
if ($valid) {
if ($this->customFilter->update($values)) {
$this->flash->success(t('Your custom filter have been updated successfully.'));
- $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to update custom filter.'));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
private function checkPermission(array $project, array $filter)
@@ -157,7 +158,7 @@ class Customfilter extends Base
$user_id = $this->userSession->getId();
if ($filter['user_id'] != $user_id && ($this->projectUserRole->getUserRole($project['id'], $user_id) === Role::PROJECT_MANAGER || ! $this->userSession->isAdmin())) {
- $this->forbidden();
+ throw new AccessForbiddenException();
}
}
}
diff --git a/app/Controller/App.php b/app/Controller/DashboardController.php
index 01f733ff..b05cd209 100644
--- a/app/Controller/App.php
+++ b/app/Controller/DashboardController.php
@@ -6,12 +6,12 @@ use Kanboard\Model\Project as ProjectModel;
use Kanboard\Model\Subtask as SubtaskModel;
/**
- * Application controller
+ * Dashboard Controller
*
- * @package controller
+ * @package Kanboard\Controller
* @author Frederic Guillot
*/
-class App extends Base
+class DashboardController extends BaseController
{
/**
* Get project pagination
@@ -25,7 +25,7 @@ class App extends Base
private function getProjectPaginator($user_id, $action, $max)
{
return $this->paginator
- ->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
+ ->setUrl('DashboardController', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax($max)
->setOrder(ProjectModel::TABLE.'.name')
->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveProjectIds($user_id)))
@@ -44,7 +44,7 @@ class App extends Base
private function getTaskPaginator($user_id, $action, $max)
{
return $this->paginator
- ->setUrl('app', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
+ ->setUrl('DashboardController', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->taskFinder->getUserQuery($user_id))
@@ -63,7 +63,7 @@ class App extends Base
private function getSubtaskPaginator($user_id, $action, $max)
{
return $this->paginator
- ->setUrl('app', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
+ ->setUrl('DashboardController', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
->setMax($max)
->setOrder('tasks.id')
->setQuery($this->subtask->getUserQuery($user_id, array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS)))
@@ -71,25 +71,15 @@ class App extends Base
}
/**
- * Check if the user is connected
- *
- * @access public
- */
- public function status()
- {
- $this->response->text('OK');
- }
-
- /**
* Dashboard overview
*
* @access public
*/
- public function index()
+ public function show()
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/overview', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/show', array(
'title' => t('Dashboard'),
'project_paginator' => $this->getProjectPaginator($user['id'], 'index', 10),
'task_paginator' => $this->getTaskPaginator($user['id'], 'index', 10),
@@ -107,7 +97,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/tasks', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/tasks', array(
'title' => t('My tasks'),
'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
'user' => $user,
@@ -123,7 +113,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/subtasks', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array(
'title' => t('My subtasks'),
'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
'user' => $user,
@@ -139,7 +129,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/projects', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/projects', array(
'title' => t('My projects'),
'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
'user' => $user,
@@ -155,7 +145,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/activity', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/activity', array(
'title' => t('My activity stream'),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermission->getActiveProjectIds($user['id']), 100),
'user' => $user,
@@ -169,7 +159,7 @@ class App extends Base
*/
public function calendar()
{
- $this->response->html($this->helper->layout->dashboard('app/calendar', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/calendar', array(
'title' => t('My calendar'),
'user' => $this->getUser(),
)));
@@ -184,7 +174,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->helper->layout->dashboard('app/notifications', array(
+ $this->response->html($this->helper->layout->dashboard('dashboard/notifications', array(
'title' => t('My notifications'),
'notifications' => $this->userUnreadNotification->getAll($user['id']),
'user' => $user,
diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php
index 219ef8ad..5caf5f5f 100644
--- a/app/Controller/Doc.php
+++ b/app/Controller/Doc.php
@@ -10,7 +10,7 @@ use Parsedown;
* @package controller
* @author Frederic Guillot
*/
-class Doc extends Base
+class Doc extends BaseController
{
public function show()
{
diff --git a/app/Controller/Export.php b/app/Controller/Export.php
index c2ff652e..f5783b72 100644
--- a/app/Controller/Export.php
+++ b/app/Controller/Export.php
@@ -8,12 +8,18 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Export extends Base
+class Export extends BaseController
{
/**
* Common export method
*
* @access private
+ * @param string $model
+ * @param string $method
+ * @param string $filename
+ * @param string $action
+ * @param string $page_title
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
private function common($model, $method, $filename, $action, $page_title)
{
@@ -23,7 +29,7 @@ class Export extends Base
if ($from && $to) {
$data = $this->$model->$method($project['id'], $from, $to);
- $this->response->forceDownload($filename.'.csv');
+ $this->response->withDownload($filename.'.csv');
$this->response->csv($data);
}
diff --git a/app/Controller/Feed.php b/app/Controller/Feed.php
index f8b3d320..7554a499 100644
--- a/app/Controller/Feed.php
+++ b/app/Controller/Feed.php
@@ -2,13 +2,15 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+
/**
* Atom/RSS Feed controller
*
* @package controller
* @author Frederic Guillot
*/
-class Feed extends Base
+class Feed extends BaseController
{
/**
* RSS feed for a user
@@ -22,7 +24,7 @@ class Feed extends Base
// Token verification
if (empty($user)) {
- $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
$this->response->xml($this->template->render('feed/user', array(
@@ -41,9 +43,8 @@ class Feed extends Base
$token = $this->request->getStringParam('token');
$project = $this->project->getByToken($token);
- // Token verification
if (empty($project)) {
- $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
$this->response->xml($this->template->render('feed/project', array(
diff --git a/app/Controller/FileViewer.php b/app/Controller/FileViewer.php
index 3be4ea14..a990e12a 100644
--- a/app/Controller/FileViewer.php
+++ b/app/Controller/FileViewer.php
@@ -10,7 +10,7 @@ use Kanboard\Core\ObjectStorage\ObjectStorageException;
* @package controller
* @author Frederic Guillot
*/
-class FileViewer extends Base
+class FileViewer extends BaseController
{
/**
* Get file content from object storage
@@ -24,11 +24,9 @@ class FileViewer extends Base
$content = '';
try {
-
if ($file['is_image'] == 0) {
$content = $this->objectStorage->get($file['path']);
}
-
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
@@ -68,17 +66,18 @@ class FileViewer extends Base
{
$file = $this->getFile();
$etag = md5($file['path']);
- $this->response->contentType($this->helper->file->getImageMimeType($file['name']));
- $this->response->cache(5 * 86400, $etag);
+ $this->response->withContentType($this->helper->file->getImageMimeType($file['name']));
+ $this->response->withCache(5 * 86400, $etag);
if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
- return $this->response->status(304);
- }
+ $this->response->status(304);
+ } else {
- try {
- $this->objectStorage->output($file['path']);
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
+ try {
+ $this->objectStorage->output($file['path']);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
}
}
@@ -94,23 +93,24 @@ class FileViewer extends Base
$filename = $this->$model->getThumbnailPath($file['path']);
$etag = md5($filename);
- $this->response->cache(5 * 86400, $etag);
- $this->response->contentType('image/jpeg');
+ $this->response->withCache(5 * 86400, $etag);
+ $this->response->withContentType('image/jpeg');
if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
- return $this->response->status(304);
- }
+ $this->response->status(304);
+ } else {
- try {
+ try {
- $this->objectStorage->output($filename);
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
+ $this->objectStorage->output($filename);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
- // Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
- $data = $this->objectStorage->get($file['path']);
- $this->$model->generateThumbnailFromData($file['path'], $data);
- $this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
+ // Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
+ $data = $this->objectStorage->get($file['path']);
+ $this->$model->generateThumbnailFromData($file['path'], $data);
+ $this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
+ }
}
}
@@ -123,7 +123,7 @@ class FileViewer extends Base
{
try {
$file = $this->getFile();
- $this->response->forceDownload($file['name']);
+ $this->response->withDownload($file['name']);
$this->objectStorage->output($file['path']);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php
index 5e9ad55e..d062b2fe 100644
--- a/app/Controller/Gantt.php
+++ b/app/Controller/Gantt.php
@@ -17,7 +17,7 @@ use Kanboard\Model\Project as ProjectModel;
* @package controller
* @author Frederic Guillot
*/
-class Gantt extends Base
+class Gantt extends BaseController
{
/**
* Show Gantt chart for all projects
@@ -53,9 +53,9 @@ class Gantt extends Base
if (! $result) {
$this->response->json(array('message' => 'Unable to save project'), 400);
+ } else {
+ $this->response->json(array('message' => 'OK'), 201);
}
-
- $this->response->json(array('message' => 'OK'), 201);
}
/**
@@ -99,15 +99,18 @@ class Gantt extends Base
if (! $result) {
$this->response->json(array('message' => 'Unable to save task'), 400);
+ } else {
+ $this->response->json(array('message' => 'OK'), 201);
}
-
- $this->response->json(array('message' => 'OK'), 201);
}
/**
* Simplified form to create a new task
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function task(array $values = array(), array $errors = array())
{
@@ -151,12 +154,12 @@ class Gantt extends Base
if ($task_id !== false) {
$this->flash->success(t('Task created successfully.'));
- $this->response->redirect($this->helper->url->to('gantt', 'project', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('gantt', 'project', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to create your task.'));
}
}
- $this->task($values, $errors);
+ return $this->task($values, $errors);
}
}
diff --git a/app/Controller/Group.php b/app/Controller/Group.php
index fa47f428..2f91e72a 100644
--- a/app/Controller/Group.php
+++ b/app/Controller/Group.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Group extends Base
+class Group extends BaseController
{
/**
* List all groups
@@ -58,6 +58,8 @@ class Group extends Base
* Display a form to create a new group
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function create(array $values = array(), array $errors = array())
{
@@ -81,19 +83,21 @@ class Group extends Base
if ($valid) {
if ($this->group->create($values['name']) !== false) {
$this->flash->success(t('Group created successfully.'));
- $this->response->redirect($this->helper->url->to('group', 'index'));
+ return $this->response->redirect($this->helper->url->to('group', 'index'));
} else {
$this->flash->failure(t('Unable to create your group.'));
}
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
* Display a form to update a group
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -121,24 +125,26 @@ class Group extends Base
if ($valid) {
if ($this->group->update($values) !== false) {
$this->flash->success(t('Group updated successfully.'));
- $this->response->redirect($this->helper->url->to('group', 'index'));
+ return $this->response->redirect($this->helper->url->to('group', 'index'));
} else {
$this->flash->failure(t('Unable to update your group.'));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
* Form to associate a user to a group
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function associate(array $values = array(), array $errors = array())
{
$group_id = $this->request->getIntegerParam('group_id');
- $group = $this->group->getbyId($group_id);
+ $group = $this->group->getById($group_id);
if (empty($values)) {
$values['group_id'] = $group_id;
@@ -165,13 +171,13 @@ class Group extends Base
if (isset($values['group_id']) && isset($values['user_id'])) {
if ($this->groupMember->addUser($values['group_id'], $values['user_id'])) {
$this->flash->success(t('Group member added successfully.'));
- $this->response->redirect($this->helper->url->to('group', 'users', array('group_id' => $values['group_id'])));
+ return $this->response->redirect($this->helper->url->to('group', 'users', array('group_id' => $values['group_id'])));
} else {
$this->flash->failure(t('Unable to add group member.'));
}
}
- $this->associate($values);
+ return $this->associate($values);
}
/**
diff --git a/app/Controller/GroupHelper.php b/app/Controller/GroupHelper.php
index 429614c2..06e5ec71 100644
--- a/app/Controller/GroupHelper.php
+++ b/app/Controller/GroupHelper.php
@@ -10,7 +10,7 @@ use Kanboard\Formatter\GroupAutoCompleteFormatter;
* @package controller
* @author Frederic Guillot
*/
-class GroupHelper extends Base
+class GroupHelper extends BaseController
{
/**
* Group auto-completion (Ajax)
diff --git a/app/Controller/Ical.php b/app/Controller/Ical.php
index 8fe97b46..091ea5f4 100644
--- a/app/Controller/Ical.php
+++ b/app/Controller/Ical.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskAssigneeFilter;
use Kanboard\Filter\TaskProjectFilter;
@@ -16,7 +17,7 @@ use Eluceo\iCal\Component\Calendar as iCalendar;
* @package controller
* @author Frederic Guillot
*/
-class Ical extends Base
+class Ical extends BaseController
{
/**
* Get user iCalendar
@@ -30,7 +31,7 @@ class Ical extends Base
// Token verification
if (empty($user)) {
- $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
// Common filter
@@ -61,7 +62,7 @@ class Ical extends Base
// Token verification
if (empty($project)) {
- $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
// Common filter
@@ -84,6 +85,8 @@ class Ical extends Base
* Common method to render iCal events
*
* @access private
+ * @param QueryBuilder $queryBuilder
+ * @param iCalendar $calendar
*/
private function renderCalendar(QueryBuilder $queryBuilder, iCalendar $calendar)
{
diff --git a/app/Controller/Link.php b/app/Controller/Link.php
index ec7ab1af..d28f5e4e 100644
--- a/app/Controller/Link.php
+++ b/app/Controller/Link.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
+
/**
* Link controller
*
@@ -9,20 +11,21 @@ namespace Kanboard\Controller;
* @author Olivier Maridat
* @author Frederic Guillot
*/
-class Link extends Base
+class Link extends BaseController
{
/**
* Get the current link
*
* @access private
* @return array
+ * @throws PageNotFoundException
*/
private function getLink()
{
$link = $this->link->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
- $this->notfound();
+ throw new PageNotFoundException();
}
return $link;
@@ -32,6 +35,8 @@ class Link extends Base
* List of links
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function index(array $values = array(), array $errors = array())
{
@@ -56,19 +61,22 @@ class Link extends Base
if ($valid) {
if ($this->link->create($values['label'], $values['opposite_label']) !== false) {
$this->flash->success(t('Link added successfully.'));
- $this->response->redirect($this->helper->url->to('link', 'index'));
+ return $this->response->redirect($this->helper->url->to('link', 'index'));
} else {
$this->flash->failure(t('Unable to create your link.'));
}
}
- $this->index($values, $errors);
+ return $this->index($values, $errors);
}
/**
* Edit form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -97,13 +105,13 @@ class Link extends Base
if ($valid) {
if ($this->link->update($values)) {
$this->flash->success(t('Link updated successfully.'));
- $this->response->redirect($this->helper->url->to('link', 'index'));
+ return $this->response->redirect($this->helper->url->to('link', 'index'));
} else {
$this->flash->failure(t('Unable to update your link.'));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
diff --git a/app/Controller/Listing.php b/app/Controller/Listing.php
index 2024ff03..93a7b836 100644
--- a/app/Controller/Listing.php
+++ b/app/Controller/Listing.php
@@ -11,7 +11,7 @@ use Kanboard\Model\Task as TaskModel;
* @package controller
* @author Frederic Guillot
*/
-class Listing extends Base
+class Listing extends BaseController
{
/**
* Show list view for projects
diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php
index 12b91144..c38654be 100644
--- a/app/Controller/Oauth.php
+++ b/app/Controller/Oauth.php
@@ -10,7 +10,7 @@ use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
* @package controller
* @author Frederic Guillot
*/
-class Oauth extends Base
+class Oauth extends BaseController
{
/**
* Redirect to the provider if no code received
@@ -106,7 +106,7 @@ class Oauth extends Base
protected function authenticate($providerName)
{
if ($this->authenticationManager->oauthAuthentication($providerName)) {
- $this->response->redirect($this->helper->url->to('app', 'index'));
+ $this->response->redirect($this->helper->url->to('DashboardController', 'show'));
} else {
$this->authenticationFailure(t('External authentication failed'));
}
diff --git a/app/Controller/PasswordReset.php b/app/Controller/PasswordReset.php
index f6a0eb8e..7050d6d2 100644
--- a/app/Controller/PasswordReset.php
+++ b/app/Controller/PasswordReset.php
@@ -2,13 +2,15 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+
/**
* Password Reset Controller
*
* @package controller
* @author Frederic Guillot
*/
-class PasswordReset extends Base
+class PasswordReset extends BaseController
{
/**
* Show the form to reset the password
@@ -37,9 +39,9 @@ class PasswordReset extends Base
if ($valid) {
$this->sendEmail($values['username']);
$this->response->redirect($this->helper->url->to('auth', 'login'));
+ } else {
+ $this->create($values, $errors);
}
-
- $this->create($values, $errors);
}
/**
@@ -59,9 +61,9 @@ class PasswordReset extends Base
'values' => $values,
'no_layout' => true,
)));
+ } else {
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
}
-
- $this->response->redirect($this->helper->url->to('auth', 'login'));
}
/**
@@ -83,10 +85,10 @@ class PasswordReset extends Base
$this->passwordReset->disable($user_id);
}
- $this->response->redirect($this->helper->url->to('auth', 'login'));
+ return $this->response->redirect($this->helper->url->to('auth', 'login'));
}
- $this->change($values, $errors);
+ return $this->change($values, $errors);
}
/**
@@ -114,7 +116,7 @@ class PasswordReset extends Base
private function checkActivation()
{
if ($this->config->get('password_reset', 0) == 0) {
- $this->response->redirect($this->helper->url->to('auth', 'login'));
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
}
}
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index cdfbd94a..22a9ad30 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Project extends Base
+class Project extends BaseController
{
/**
* List of projects
@@ -75,12 +75,9 @@ class Project extends Base
}
$this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id'])));
+ } else {
+ $this->show();
}
-
- $this->response->html($this->helper->layout->project('project/share', array(
- 'project' => $project,
- 'title' => t('Public access'),
- )));
}
/**
@@ -96,15 +93,15 @@ class Project extends Base
$this->projectMetadata->save($project['id'], $this->request->getValues());
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id'])));
+ } else {
+ $this->response->html($this->helper->layout->project('project/integrations', array(
+ 'project' => $project,
+ 'title' => t('Integrations'),
+ 'webhook_token' => $this->config->get('webhook_token'),
+ 'values' => $this->projectMetadata->getAll($project['id']),
+ 'errors' => array(),
+ )));
}
-
- $this->response->html($this->helper->layout->project('project/integrations', array(
- 'project' => $project,
- 'title' => t('Integrations'),
- 'webhook_token' => $this->config->get('webhook_token'),
- 'values' => $this->projectMetadata->getAll($project['id']),
- 'errors' => array(),
- )));
}
/**
@@ -120,10 +117,10 @@ class Project extends Base
$values = $this->request->getValues();
$this->projectNotification->saveSettings($project['id'], $values);
$this->flash->success(t('Project updated successfully.'));
- $this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
}
- $this->response->html($this->helper->layout->project('project/notifications', array(
+ return $this->response->html($this->helper->layout->project('project/notifications', array(
'notifications' => $this->projectNotification->readSettings($project['id']),
'types' => $this->projectNotificationType->getTypes(),
'project' => $project,
@@ -149,10 +146,10 @@ class Project extends Base
$this->flash->failure(t('Unable to remove this project.'));
}
- $this->response->redirect($this->helper->url->to('project', 'index'));
+ return $this->response->redirect($this->helper->url->to('project', 'index'));
}
- $this->response->html($this->helper->layout->project('project/remove', array(
+ return $this->response->html($this->helper->layout->project('project/remove', array(
'project' => $project,
'title' => t('Remove project')
)));
@@ -178,10 +175,10 @@ class Project extends Base
$this->flash->failure(t('Unable to clone this project.'));
}
- $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
+ return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
}
- $this->response->html($this->helper->layout->project('project/duplicate', array(
+ return $this->response->html($this->helper->layout->project('project/duplicate', array(
'project' => $project,
'title' => t('Clone this project')
)));
@@ -205,10 +202,10 @@ class Project extends Base
$this->flash->failure(t('Unable to disable this project.'));
}
- $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
}
- $this->response->html($this->helper->layout->project('project/disable', array(
+ return $this->response->html($this->helper->layout->project('project/disable', array(
'project' => $project,
'title' => t('Project activation')
)));
@@ -232,10 +229,10 @@ class Project extends Base
$this->flash->failure(t('Unable to activate this project.'));
}
- $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
}
- $this->response->html($this->helper->layout->project('project/enable', array(
+ return $this->response->html($this->helper->layout->project('project/enable', array(
'project' => $project,
'title' => t('Project activation')
)));
diff --git a/app/Controller/ProjectCreation.php b/app/Controller/ProjectCreation.php
index 88f41fcd..ed997fea 100644
--- a/app/Controller/ProjectCreation.php
+++ b/app/Controller/ProjectCreation.php
@@ -8,12 +8,14 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ProjectCreation extends Base
+class ProjectCreation extends BaseController
{
/**
* Display a form to create a new project
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function create(array $values = array(), array $errors = array())
{
@@ -33,6 +35,8 @@ class ProjectCreation extends Base
* Display a form to create a private project
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function createPrivate(array $values = array(), array $errors = array())
{
@@ -61,7 +65,7 @@ class ProjectCreation extends Base
$this->flash->failure(t('Unable to create your project.'));
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
diff --git a/app/Controller/ProjectEdit.php b/app/Controller/ProjectEdit.php
index 94be0206..e3ba74c3 100644
--- a/app/Controller/ProjectEdit.php
+++ b/app/Controller/ProjectEdit.php
@@ -8,12 +8,14 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ProjectEdit extends Base
+class ProjectEdit extends BaseController
{
/**
* General edition (most common operations)
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -24,6 +26,8 @@ class ProjectEdit extends Base
* Change start and end dates
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function dates(array $values = array(), array $errors = array())
{
@@ -34,6 +38,8 @@ class ProjectEdit extends Base
* Change project description
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function description(array $values = array(), array $errors = array())
{
@@ -44,6 +50,8 @@ class ProjectEdit extends Base
* Change task priority
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function priority(array $values = array(), array $errors = array())
{
@@ -67,13 +75,13 @@ class ProjectEdit extends Base
if ($valid) {
if ($this->project->update($values)) {
$this->flash->success(t('Project updated successfully.'));
- $this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id'])), true);
+ return $this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id'])), true);
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
- $this->$redirect($values, $errors);
+ return $this->$redirect($values, $errors);
}
/**
diff --git a/app/Controller/ProjectFile.php b/app/Controller/ProjectFile.php
index 96764a92..6ec5ff27 100644
--- a/app/Controller/ProjectFile.php
+++ b/app/Controller/ProjectFile.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ProjectFile extends Base
+class ProjectFile extends BaseController
{
/**
* File upload form
diff --git a/app/Controller/ProjectOverview.php b/app/Controller/ProjectOverview.php
index b2bb33d6..f8837f95 100644
--- a/app/Controller/ProjectOverview.php
+++ b/app/Controller/ProjectOverview.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class ProjectOverview extends Base
+class ProjectOverview extends BaseController
{
/**
* Show project overview
diff --git a/app/Controller/ProjectPermission.php b/app/Controller/ProjectPermission.php
index e203c0db..f50a96b8 100644
--- a/app/Controller/ProjectPermission.php
+++ b/app/Controller/ProjectPermission.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Security\Role;
/**
@@ -10,7 +11,7 @@ use Kanboard\Core\Security\Role;
* @package controller
* @author Frederic Guillot
*/
-class ProjectPermission extends Base
+class ProjectPermission extends BaseController
{
/**
* Permissions are only available for team projects
@@ -18,13 +19,14 @@ class ProjectPermission extends Base
* @access protected
* @param integer $project_id Default project id
* @return array
+ * @throws AccessForbiddenException
*/
protected function getProject($project_id = 0)
{
$project = parent::getProject($project_id);
if ($project['is_private'] == 1) {
- $this->forbidden();
+ throw new AccessForbiddenException();
}
return $project;
@@ -34,6 +36,9 @@ class ProjectPermission extends Base
* Show all permissions
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
*/
public function index(array $values = array(), array $errors = array())
{
diff --git a/app/Controller/Projectuser.php b/app/Controller/Projectuser.php
index a6d4fe4e..fe1fe0f1 100644
--- a/app/Controller/Projectuser.php
+++ b/app/Controller/Projectuser.php
@@ -12,7 +12,7 @@ use Kanboard\Core\Security\Role;
* @package controller
* @author Frederic Guillot
*/
-class Projectuser extends Base
+class Projectuser extends BaseController
{
private function common()
{
@@ -94,7 +94,7 @@ class Projectuser extends Base
*/
public function members()
{
- $this->role(ROLE::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member');
+ $this->role(Role::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member');
}
/**
diff --git a/app/Controller/Search.php b/app/Controller/Search.php
index a42e9d3d..a092cba6 100644
--- a/app/Controller/Search.php
+++ b/app/Controller/Search.php
@@ -10,7 +10,7 @@ use Kanboard\Filter\TaskProjectsFilter;
* @package controller
* @author Frederic Guillot
*/
-class Search extends Base
+class Search extends BaseController
{
public function index()
{
diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php
index dea2b08e..dfe4415e 100644
--- a/app/Controller/Subtask.php
+++ b/app/Controller/Subtask.php
@@ -2,18 +2,24 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+
/**
* Subtask controller
*
* @package controller
* @author Frederic Guillot
*/
-class Subtask extends Base
+class Subtask extends BaseController
{
/**
* Creation form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -60,18 +66,22 @@ class Subtask extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true);
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
* Edit form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
- $subtask = $this->getSubTask();
+ $subtask = $this->getSubtask();
$this->response->html($this->template->render('subtask/edit', array(
'values' => empty($values) ? $subtask : $values,
@@ -106,7 +116,7 @@ class Subtask extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
@@ -158,9 +168,9 @@ class Subtask extends Base
if (! empty($values) && $this->helper->user->hasProjectAccess('Subtask', 'movePosition', $project_id)) {
$result = $this->subtask->changePosition($task_id, $values['subtask_id'], $values['position']);
- return $this->response->json(array('result' => $result));
+ $this->response->json(array('result' => $result));
+ } else {
+ throw new AccessForbiddenException();
}
-
- $this->forbidden();
}
}
diff --git a/app/Controller/SubtaskRestriction.php b/app/Controller/SubtaskRestriction.php
index 56024867..bfa3031c 100644
--- a/app/Controller/SubtaskRestriction.php
+++ b/app/Controller/SubtaskRestriction.php
@@ -10,7 +10,7 @@ use Kanboard\Model\Subtask as SubtaskModel;
* @package controller
* @author Frederic Guillot
*/
-class SubtaskRestriction extends Base
+class SubtaskRestriction extends BaseController
{
/**
* Show popup
diff --git a/app/Controller/SubtaskStatus.php b/app/Controller/SubtaskStatus.php
index 4fb82fc0..e22e825e 100644
--- a/app/Controller/SubtaskStatus.php
+++ b/app/Controller/SubtaskStatus.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class SubtaskStatus extends Base
+class SubtaskStatus extends BaseController
{
/**
* Change status to the next status: Toto -> In Progress -> Done
diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php
index 8270a16f..4575e909 100644
--- a/app/Controller/Swimlane.php
+++ b/app/Controller/Swimlane.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Model\Swimlane as SwimlaneModel;
/**
@@ -10,22 +12,21 @@ use Kanboard\Model\Swimlane as SwimlaneModel;
* @package controller
* @author Frederic Guillot
*/
-class Swimlane extends Base
+class Swimlane extends BaseController
{
/**
* Get the swimlane (common method between actions)
*
* @access private
- * @param integer $project_id
* @return array
+ * @throws PageNotFoundException
*/
- private function getSwimlane($project_id)
+ private function getSwimlane()
{
$swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
if (empty($swimlane)) {
- $this->flash->failure(t('Swimlane not found.'));
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project_id)));
+ throw new PageNotFoundException();
}
return $swimlane;
@@ -53,6 +54,9 @@ class Swimlane extends Base
* Create a new swimlane
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -79,19 +83,22 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->create($values)) {
$this->flash->success(t('Your swimlane have been created successfully.'));
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
* Edit default swimlane (display the form)
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function editDefault(array $values = array(), array $errors = array())
{
@@ -120,24 +127,27 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->updateDefault($values)) {
$this->flash->success(t('The default swimlane have been updated successfully.'));
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])), true);
+ return $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])), true);
} else {
$this->flash->failure(t('Unable to update this swimlane.'));
}
}
- $this->editDefault($values, $errors);
+ return $this->editDefault($values, $errors);
}
/**
* Edit a swimlane (display the form)
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
- $swimlane = $this->getSwimlane($project['id']);
+ $swimlane = $this->getSwimlane();
$this->response->html($this->helper->layout->project('swimlane/edit', array(
'values' => empty($values) ? $swimlane : $values,
@@ -161,13 +171,13 @@ class Swimlane extends Base
if ($valid) {
if ($this->swimlane->update($values)) {
$this->flash->success(t('Swimlane updated successfully.'));
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
+ return $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
@@ -178,7 +188,7 @@ class Swimlane extends Base
public function confirm()
{
$project = $this->getProject();
- $swimlane = $this->getSwimlane($project['id']);
+ $swimlane = $this->getSwimlane();
$this->response->html($this->helper->layout->project('swimlane/remove', array(
'project' => $project,
@@ -296,9 +306,9 @@ class Swimlane extends Base
if (! empty($values) && isset($values['swimlane_id']) && isset($values['position'])) {
$result = $this->swimlane->changePosition($project['id'], $values['swimlane_id'], $values['position']);
- return $this->response->json(array('result' => $result));
+ $this->response->json(array('result' => $result));
+ } else {
+ throw new AccessForbiddenException();
}
-
- $this->forbidden();
}
}
diff --git a/app/Controller/Task.php b/app/Controller/Task.php
index 072df87b..1ce13f69 100644
--- a/app/Controller/Task.php
+++ b/app/Controller/Task.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Core\DateParser;
/**
@@ -10,7 +12,7 @@ use Kanboard\Core\DateParser;
* @package controller
* @author Frederic Guillot
*/
-class Task extends Base
+class Task extends BaseController
{
/**
* Public access (display a task)
@@ -23,17 +25,17 @@ class Task extends Base
// Token verification
if (empty($project)) {
- return $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
$task = $this->taskFinder->getDetails($this->request->getIntegerParam('task_id'));
if (empty($task)) {
- return $this->notfound(true);
+ throw PageNotFoundException::getInstance()->withoutLayout();
}
if ($task['project_id'] != $project['id']) {
- return $this->forbidden(true);
+ throw AccessForbiddenException::getInstance()->withoutLayout();
}
$this->response->html($this->helper->layout->app('task/public', array(
@@ -152,7 +154,7 @@ class Task extends Base
$task = $this->getTask();
if (! $this->helper->user->canRemoveTask($task)) {
- $this->forbidden();
+ throw new AccessForbiddenException();
}
if ($this->request->getStringParam('confirmation') === 'yes') {
@@ -164,10 +166,10 @@ class Task extends Base
$this->flash->failure(t('Unable to remove this task.'));
}
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])), true);
+ return $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])), true);
}
- $this->response->html($this->template->render('task/remove', array(
+ return $this->response->html($this->template->render('task/remove', array(
'task' => $task,
)));
}
diff --git a/app/Controller/TaskBulk.php b/app/Controller/TaskBulk.php
index 4e9d4443..d0a1b276 100644
--- a/app/Controller/TaskBulk.php
+++ b/app/Controller/TaskBulk.php
@@ -7,7 +7,7 @@ namespace Kanboard\Controller;
*
* @package Kanboard\Controller
*/
-class TaskBulk extends Base
+class TaskBulk extends BaseController
{
/**
* Show the form
diff --git a/app/Controller/TaskExternalLink.php b/app/Controller/TaskExternalLink.php
index 0db8ec37..9f040957 100644
--- a/app/Controller/TaskExternalLink.php
+++ b/app/Controller/TaskExternalLink.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
/**
@@ -10,12 +11,16 @@ use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
* @package controller
* @author Frederic Guillot
*/
-class TaskExternalLink extends Base
+class TaskExternalLink extends BaseController
{
/**
* First creation form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function find(array $values = array(), array $errors = array())
{
@@ -36,11 +41,10 @@ class TaskExternalLink extends Base
*/
public function create()
{
- try {
-
- $task = $this->getTask();
- $values = $this->request->getValues();
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+ try {
$provider = $this->externalLinkManager->setUserInput($values)->find();
$link = $provider->getLink();
@@ -77,13 +81,18 @@ class TaskExternalLink extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
* Edit form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws ExternalLinkProviderNotFound
+ * @throws PageNotFoundException
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -95,7 +104,7 @@ class TaskExternalLink extends Base
}
if (empty($values)) {
- return $this->notfound();
+ throw new PageNotFoundException();
}
$provider = $this->externalLinkManager->getProvider($values['link_type']);
@@ -124,7 +133,7 @@ class TaskExternalLink extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
@@ -139,7 +148,7 @@ class TaskExternalLink extends Base
$link = $this->taskExternalLink->getById($link_id);
if (empty($link)) {
- return $this->notfound();
+ throw new PageNotFoundException();
}
$this->response->html($this->template->render('task_external_link/remove', array(
diff --git a/app/Controller/TaskFile.php b/app/Controller/TaskFile.php
index 2b0152a7..0fcd2d69 100644
--- a/app/Controller/TaskFile.php
+++ b/app/Controller/TaskFile.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class TaskFile extends Base
+class TaskFile extends BaseController
{
/**
* Screenshot
@@ -24,7 +24,7 @@ class TaskFile extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
- $this->response->html($this->template->render('task_file/screenshot', array(
+ return $this->response->html($this->template->render('task_file/screenshot', array(
'task' => $task,
)));
}
diff --git a/app/Controller/TaskHelper.php b/app/Controller/TaskHelper.php
index 6835ab2b..2f14c0eb 100644
--- a/app/Controller/TaskHelper.php
+++ b/app/Controller/TaskHelper.php
@@ -14,7 +14,7 @@ use Kanboard\Formatter\TaskAutoCompleteFormatter;
* @package controller
* @author Frederic Guillot
*/
-class TaskHelper extends Base
+class TaskHelper extends BaseController
{
/**
* Task auto-completion (Ajax)
diff --git a/app/Controller/TaskImport.php b/app/Controller/TaskImport.php
index 460c608c..5dbf8678 100644
--- a/app/Controller/TaskImport.php
+++ b/app/Controller/TaskImport.php
@@ -10,11 +10,14 @@ use Kanboard\Core\Csv;
* @package controller
* @author Frederic Guillot
*/
-class TaskImport extends Base
+class TaskImport extends BaseController
{
/**
* Upload the file and ask settings
*
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function step1(array $values = array(), array $errors = array())
{
@@ -66,7 +69,7 @@ class TaskImport extends Base
*/
public function template()
{
- $this->response->forceDownload('tasks.csv');
+ $this->response->withDownload('tasks.csv');
$this->response->csv(array($this->taskImport->getColumnMapping()));
}
}
diff --git a/app/Controller/TaskInternalLink.php b/app/Controller/TaskInternalLink.php
index ac5e04b7..6ff20252 100644
--- a/app/Controller/TaskInternalLink.php
+++ b/app/Controller/TaskInternalLink.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
+
/**
* TaskInternalLink Controller
*
@@ -9,20 +11,21 @@ namespace Kanboard\Controller;
* @author Olivier Maridat
* @author Frederic Guillot
*/
-class TaskInternalLink extends Base
+class TaskInternalLink extends BaseController
{
/**
* Get the current link
*
* @access private
* @return array
+ * @throws PageNotFoundException
*/
private function getTaskLink()
{
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
- return $this->notfound();
+ throw new PageNotFoundException();
}
return $link;
@@ -32,6 +35,10 @@ class TaskInternalLink extends Base
* Creation form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -67,13 +74,17 @@ class TaskInternalLink extends Base
$this->flash->failure(t('Unable to create your link.'));
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
* Edit form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -116,7 +127,7 @@ class TaskInternalLink extends Base
$this->flash->failure(t('Unable to update your link.'));
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
/**
diff --git a/app/Controller/TaskPopover.php b/app/Controller/TaskPopover.php
index 422a99c7..0e47cffe 100644
--- a/app/Controller/TaskPopover.php
+++ b/app/Controller/TaskPopover.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class TaskPopover extends Base
+class TaskPopover extends BaseController
{
/**
* Change a task assignee directly from the board
diff --git a/app/Controller/TaskRecurrence.php b/app/Controller/TaskRecurrence.php
index 569ef8d9..72dce3a5 100644
--- a/app/Controller/TaskRecurrence.php
+++ b/app/Controller/TaskRecurrence.php
@@ -8,12 +8,16 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class TaskRecurrence extends Base
+class TaskRecurrence extends BaseController
{
/**
* Edit recurrence form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -53,9 +57,9 @@ class TaskRecurrence extends Base
$this->flash->failure(t('Unable to update your task.'));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
- $this->edit($values, $errors);
+ return $this->edit($values, $errors);
}
}
diff --git a/app/Controller/Taskcreation.php b/app/Controller/Taskcreation.php
index 1c9e0d82..af7d0c80 100644
--- a/app/Controller/Taskcreation.php
+++ b/app/Controller/Taskcreation.php
@@ -8,12 +8,15 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Taskcreation extends Base
+class Taskcreation extends BaseController
{
/**
* Display a form to create a new task
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
@@ -63,7 +66,7 @@ class Taskcreation extends Base
}
$this->flash->failure(t('Unable to create your task.'));
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
private function afterSave(array $project, array &$values)
@@ -79,6 +82,6 @@ class Taskcreation extends Base
));
}
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])), true);
+ return $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])), true);
}
}
diff --git a/app/Controller/Taskduplication.php b/app/Controller/Taskduplication.php
index 8fca930d..ff60228e 100644
--- a/app/Controller/Taskduplication.php
+++ b/app/Controller/Taskduplication.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Taskduplication extends Base
+class Taskduplication extends BaseController
{
/**
* Duplicate a task
@@ -25,14 +25,14 @@ class Taskduplication extends Base
if ($task_id > 0) {
$this->flash->success(t('Task created successfully.'));
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
} else {
$this->flash->failure(t('Unable to create this task.'));
- $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
+ return $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
}
- $this->response->html($this->template->render('task_duplication/duplicate', array(
+ return $this->response->html($this->template->render('task_duplication/duplicate', array(
'task' => $task,
)));
}
@@ -57,13 +57,13 @@ class Taskduplication extends Base
$values['category_id'],
$values['owner_id'])) {
$this->flash->success(t('Task updated successfully.'));
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task['id'])));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task['id'])));
}
$this->flash->failure(t('Unable to update your task.'));
}
- $this->chooseDestination($task, 'task_duplication/move');
+ return $this->chooseDestination($task, 'task_duplication/move');
}
/**
@@ -87,14 +87,14 @@ class Taskduplication extends Base
if ($task_id > 0) {
$this->flash->success(t('Task created successfully.'));
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task_id)));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task_id)));
}
}
$this->flash->failure(t('Unable to create your task.'));
}
- $this->chooseDestination($task, 'task_duplication/copy');
+ return $this->chooseDestination($task, 'task_duplication/copy');
}
/**
diff --git a/app/Controller/Taskmodification.php b/app/Controller/Taskmodification.php
index 6b945f37..e8eafe20 100644
--- a/app/Controller/Taskmodification.php
+++ b/app/Controller/Taskmodification.php
@@ -10,7 +10,7 @@ use Kanboard\Core\DateParser;
* @package controller
* @author Frederic Guillot
*/
-class Taskmodification extends Base
+class Taskmodification extends BaseController
{
/**
* Set automatically the start date
@@ -28,6 +28,10 @@ class Taskmodification extends Base
* Edit description form
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function description(array $values = array(), array $errors = array())
{
@@ -66,13 +70,17 @@ class Taskmodification extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
- $this->description($values, $errors);
+ return $this->description($values, $errors);
}
/**
* Display a form to edit a task
*
* @access public
+ * @param array $values
+ * @param array $errors
+ * @throws \Kanboard\Core\Controller\AccessForbiddenException
+ * @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
@@ -113,7 +121,7 @@ class Taskmodification extends Base
if ($valid && $this->taskModification->update($values)) {
$this->flash->success(t('Task updated successfully.'));
- return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
+ $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
} else {
$this->flash->failure(t('Unable to update your task.'));
$this->edit($values, $errors);
diff --git a/app/Controller/Taskstatus.php b/app/Controller/Taskstatus.php
index a67459c9..eeaf8513 100644
--- a/app/Controller/Taskstatus.php
+++ b/app/Controller/Taskstatus.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Taskstatus extends Base
+class Taskstatus extends BaseController
{
/**
* Close a task
@@ -55,7 +55,7 @@ class Taskstatus extends Base
return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
- $this->response->html($this->template->render($template, array(
+ return $this->response->html($this->template->render($template, array(
'task' => $task,
)));
}
diff --git a/app/Controller/Twofactor.php b/app/Controller/Twofactor.php
index 10292261..2eb61594 100644
--- a/app/Controller/Twofactor.php
+++ b/app/Controller/Twofactor.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+
/**
* Two Factor Auth controller
*
@@ -14,11 +16,13 @@ class Twofactor extends User
* Only the current user can access to 2FA settings
*
* @access private
+ * @param array $user
+ * @throws AccessForbiddenException
*/
private function checkCurrentUser(array $user)
{
if ($user['id'] != $this->userSession->getId()) {
- $this->forbidden();
+ throw new AccessForbiddenException();
}
}
@@ -145,7 +149,7 @@ class Twofactor extends User
if ($provider->authenticate()) {
$this->userSession->validatePostAuthentication();
$this->flash->success(t('The two factor authentication code is valid.'));
- $this->response->redirect($this->helper->url->to('app', 'index'));
+ $this->response->redirect($this->helper->url->to('DashboardController', 'show'));
} else {
$this->flash->failure(t('The two factor authentication code is not valid.'));
$this->response->redirect($this->helper->url->to('twofactor', 'code'));
@@ -188,10 +192,10 @@ class Twofactor extends User
'twofactor_secret' => '',
));
- $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
- $this->response->html($this->helper->layout->user('twofactor/disable', array(
+ return $this->response->html($this->helper->layout->user('twofactor/disable', array(
'user' => $user,
)));
}
diff --git a/app/Controller/User.php b/app/Controller/User.php
index 4caed1e6..11a7a01e 100644
--- a/app/Controller/User.php
+++ b/app/Controller/User.php
@@ -2,6 +2,7 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Notification\Mail as MailNotification;
use Kanboard\Model\Project as ProjectModel;
use Kanboard\Core\Security\Role;
@@ -12,7 +13,7 @@ use Kanboard\Core\Security\Role;
* @package controller
* @author Frederic Guillot
*/
-class User extends Base
+class User extends BaseController
{
/**
* List all users
@@ -28,39 +29,38 @@ class User extends Base
->setQuery($this->user->getQuery())
->calculate();
- $this->response->html(
- $this->helper->layout->app('user/index', array(
- 'title' => t('Users').' ('.$paginator->getTotal().')',
- 'paginator' => $paginator,
- )
- ));
+ $this->response->html($this->helper->layout->app('user/index', array(
+ 'title' => t('Users').' ('.$paginator->getTotal().')',
+ 'paginator' => $paginator,
+ )));
}
/**
* Public user profile
*
* @access public
+ * @throws PageNotFoundException
*/
public function profile()
{
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
if (empty($user)) {
- $this->notfound();
+ throw new PageNotFoundException();
}
- $this->response->html(
- $this->helper->layout->app('user/profile', array(
- 'title' => $user['name'] ?: $user['username'],
- 'user' => $user,
- )
- ));
+ $this->response->html($this->helper->layout->app('user/profile', array(
+ 'title' => $user['name'] ?: $user['username'],
+ 'user' => $user,
+ )));
}
/**
* Display a form to create a new user
*
* @access public
+ * @param array $values
+ * @param array $errors
*/
public function create(array $values = array(), array $errors = array())
{
@@ -101,14 +101,14 @@ class User extends Base
}
$this->flash->success(t('User created successfully.'));
- $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user_id)));
+ return $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user_id)));
} else {
$this->flash->failure(t('Unable to create your user.'));
$values['project_id'] = $project_id;
}
}
- $this->create($values, $errors);
+ return $this->create($values, $errors);
}
/**
@@ -217,10 +217,10 @@ class User extends Base
$values = $this->request->getValues();
$this->userNotification->saveSettings($user['id'], $values);
$this->flash->success(t('User updated successfully.'));
- $this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id'])));
}
- $this->response->html($this->helper->layout->user('user/notifications', array(
+ return $this->response->html($this->helper->layout->user('user/notifications', array(
'projects' => $this->projectUserRole->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)),
'notifications' => $this->userNotification->readSettings($user['id']),
'types' => $this->userNotificationType->getTypes(),
@@ -284,10 +284,10 @@ class User extends Base
$this->flash->failure(t('Unable to update this user.'));
}
- $this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id'])));
}
- $this->response->html($this->helper->layout->user('user/share', array(
+ return $this->response->html($this->helper->layout->user('user/share', array(
'user' => $user,
'title' => t('Public access'),
)));
@@ -315,11 +315,11 @@ class User extends Base
$this->flash->failure(t('Unable to change the password.'));
}
- $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->helper->layout->user('user/password', array(
+ return $this->response->html($this->helper->layout->user('user/password', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
@@ -357,11 +357,11 @@ class User extends Base
$this->flash->failure(t('Unable to update your user.'));
}
- $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->helper->layout->user('user/edit', array(
+ return $this->response->html($this->helper->layout->user('user/edit', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
@@ -395,11 +395,11 @@ class User extends Base
$this->flash->failure(t('Unable to update your user.'));
}
- $this->response->redirect($this->helper->url->to('user', 'authentication', array('user_id' => $user['id'])));
+ return $this->response->redirect($this->helper->url->to('user', 'authentication', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->helper->layout->user('user/authentication', array(
+ return $this->response->html($this->helper->layout->user('user/authentication', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
diff --git a/app/Controller/UserHelper.php b/app/Controller/UserHelper.php
index 47bbe554..d5e0920d 100644
--- a/app/Controller/UserHelper.php
+++ b/app/Controller/UserHelper.php
@@ -12,7 +12,7 @@ use Kanboard\Model\User as UserModel;
* @package controller
* @author Frederic Guillot
*/
-class UserHelper extends Base
+class UserHelper extends BaseController
{
/**
* User auto-completion (Ajax)
@@ -39,4 +39,14 @@ class UserHelper extends Base
$users = $this->projectPermission->findUsernames($project_id, $query);
$this->response->json($users);
}
+
+ /**
+ * Check if the user is connected
+ *
+ * @access public
+ */
+ public function status()
+ {
+ $this->response->text('OK');
+ }
}
diff --git a/app/Controller/UserImport.php b/app/Controller/UserImport.php
index debd69e5..b99e56a0 100644
--- a/app/Controller/UserImport.php
+++ b/app/Controller/UserImport.php
@@ -10,7 +10,7 @@ use Kanboard\Core\Csv;
* @package controller
* @author Frederic Guillot
*/
-class UserImport extends Base
+class UserImport extends BaseController
{
/**
* Upload the file and ask settings
@@ -60,7 +60,7 @@ class UserImport extends Base
*/
public function template()
{
- $this->response->forceDownload('users.csv');
+ $this->response->withDownload('users.csv');
$this->response->csv(array($this->userImport->getColumnMapping()));
}
}
diff --git a/app/Controller/UserStatus.php b/app/Controller/UserStatus.php
index b8ee5c91..6f93e953 100644
--- a/app/Controller/UserStatus.php
+++ b/app/Controller/UserStatus.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class UserStatus extends Base
+class UserStatus extends BaseController
{
/**
* Confirm remove a user
diff --git a/app/Controller/WebNotification.php b/app/Controller/WebNotification.php
index dca5cb46..a62da0e3 100644
--- a/app/Controller/WebNotification.php
+++ b/app/Controller/WebNotification.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class WebNotification extends Base
+class WebNotification extends BaseController
{
/**
* Mark all notifications as read
@@ -20,7 +20,7 @@ class WebNotification extends Base
$user_id = $this->getUserId();
$this->userUnreadNotification->markAllAsRead($user_id);
- $this->response->redirect($this->helper->url->to('app', 'notifications', array('user_id' => $user_id)));
+ $this->response->redirect($this->helper->url->to('DashboardController', 'notifications', array('user_id' => $user_id)));
}
/**
@@ -34,7 +34,7 @@ class WebNotification extends Base
$notification_id = $this->request->getIntegerParam('notification_id');
$this->userUnreadNotification->markAsRead($user_id, $notification_id);
- $this->response->redirect($this->helper->url->to('app', 'notifications', array('user_id' => $user_id)));
+ $this->response->redirect($this->helper->url->to('DashboardController', 'notifications', array('user_id' => $user_id)));
}
private function getUserId()
diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php
index 0eafe3e5..6e172aeb 100644
--- a/app/Controller/Webhook.php
+++ b/app/Controller/Webhook.php
@@ -8,7 +8,7 @@ namespace Kanboard\Controller;
* @package controller
* @author Frederic Guillot
*/
-class Webhook extends Base
+class Webhook extends BaseController
{
/**
* Webhook to create a task
@@ -34,9 +34,9 @@ class Webhook extends Base
list($valid, ) = $this->taskValidator->validateCreation($values);
if ($valid && $this->taskCreation->create($values)) {
- $this->response->text('OK');
+ return $this->response->text('OK');
}
- $this->response->text('FAILED');
+ return $this->response->text('FAILED');
}
}
diff --git a/app/Core/Controller/AccessForbiddenException.php b/app/Core/Controller/AccessForbiddenException.php
new file mode 100644
index 00000000..b5dccb78
--- /dev/null
+++ b/app/Core/Controller/AccessForbiddenException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Kanboard\Core\Controller;
+
+/**
+ * Class AccessForbiddenException
+ *
+ * @package Kanboard\Core\Controller
+ * @author Frederic Guillot
+ */
+class AccessForbiddenException extends BaseException
+{
+
+}
diff --git a/app/Core/Controller/BaseException.php b/app/Core/Controller/BaseException.php
new file mode 100644
index 00000000..13836d2c
--- /dev/null
+++ b/app/Core/Controller/BaseException.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Kanboard\Core\Controller;
+
+use Exception;
+
+/**
+ * Class AccessForbiddenException
+ *
+ * @package Kanboard\Core\Controller
+ * @author Frederic Guillot
+ */
+class BaseException extends Exception
+{
+ protected $withoutLayout = false;
+
+ /**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param string $message
+ * @return static
+ */
+ public static function getInstance($message = '')
+ {
+ return new static($message);
+ }
+
+ /**
+ * There is no layout
+ *
+ * @access public
+ * @return BaseException
+ */
+ public function withoutLayout()
+ {
+ $this->withoutLayout = true;
+ return $this;
+ }
+
+ /**
+ * Return true if no layout
+ *
+ * @access public
+ * @return boolean
+ */
+ public function hasLayout()
+ {
+ return $this->withoutLayout;
+ }
+}
diff --git a/app/Core/Controller/BaseMiddleware.php b/app/Core/Controller/BaseMiddleware.php
new file mode 100644
index 00000000..f2862d13
--- /dev/null
+++ b/app/Core/Controller/BaseMiddleware.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Kanboard\Core\Controller;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class BaseMiddleware
+ *
+ * @package Kanboard\Core\Controller
+ * @author Frederic Guillot
+ */
+abstract class BaseMiddleware extends Base
+{
+ /**
+ * @var BaseMiddleware
+ */
+ protected $nextMiddleware = null;
+
+ /**
+ * Execute middleware
+ */
+ abstract public function execute();
+
+ /**
+ * Set next middleware
+ *
+ * @param BaseMiddleware $nextMiddleware
+ * @return BaseMiddleware
+ */
+ public function setNextMiddleware($nextMiddleware)
+ {
+ $this->nextMiddleware = $nextMiddleware;
+ return $this;
+ }
+
+ /**
+ * @return BaseMiddleware
+ */
+ public function getNextMiddleware()
+ {
+ return $this->nextMiddleware;
+ }
+
+ /**
+ * Move to next middleware
+ */
+ public function next()
+ {
+ if ($this->nextMiddleware !== null) {
+ if (DEBUG) {
+ $this->logger->debug(__METHOD__.' => ' . get_class($this->nextMiddleware));
+ }
+
+ $this->nextMiddleware->execute();
+ }
+ }
+}
diff --git a/app/Core/Controller/PageNotFoundException.php b/app/Core/Controller/PageNotFoundException.php
new file mode 100644
index 00000000..e96a2057
--- /dev/null
+++ b/app/Core/Controller/PageNotFoundException.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Kanboard\Core\Controller;
+
+/**
+ * Class PageNotFoundException
+ *
+ * @package Kanboard\Core\Controller
+ * @author Frederic Guillot
+ */
+class PageNotFoundException extends BaseException
+{
+
+}
diff --git a/app/Core/Controller/Runner.php b/app/Core/Controller/Runner.php
new file mode 100644
index 00000000..b973c098
--- /dev/null
+++ b/app/Core/Controller/Runner.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Kanboard\Core\Controller;
+
+use Kanboard\Controller\AppController;
+use Kanboard\Core\Base;
+use Kanboard\Middleware\ApplicationAuthorizationMiddleware;
+use Kanboard\Middleware\AuthenticationMiddleware;
+use Kanboard\Middleware\BootstrapMiddleware;
+use Kanboard\Middleware\PostAuthenticationMiddleware;
+use Kanboard\Middleware\ProjectAuthorizationMiddleware;
+use RuntimeException;
+
+/**
+ * Class Runner
+ *
+ * @package Kanboard\Core\Controller
+ * @author Frederic Guillot
+ */
+class Runner extends Base
+{
+ /**
+ * Execute middleware and controller
+ */
+ public function execute()
+ {
+ try {
+ $this->executeMiddleware();
+ $this->executeController();
+ } catch (PageNotFoundException $e) {
+ $controllerObject = new AppController($this->container);
+ $controllerObject->notFound($e->hasLayout());
+ } catch (AccessForbiddenException $e) {
+ $controllerObject = new AppController($this->container);
+ $controllerObject->accessForbidden($e->hasLayout());
+ }
+ }
+
+ /**
+ * Execute all middleware
+ */
+ protected function executeMiddleware()
+ {
+ if (DEBUG) {
+ $this->logger->debug(__METHOD__);
+ }
+
+ $bootstrapMiddleware = new BootstrapMiddleware($this->container);
+ $authenticationMiddleware = new AuthenticationMiddleware($this->container);
+ $postAuthenticationMiddleware = new PostAuthenticationMiddleware($this->container);
+ $appAuthorizationMiddleware = new ApplicationAuthorizationMiddleware($this->container);
+ $projectAuthorizationMiddleware = new ProjectAuthorizationMiddleware($this->container);
+
+ $bootstrapMiddleware->setNextMiddleware($authenticationMiddleware);
+ $authenticationMiddleware->setNextMiddleware($postAuthenticationMiddleware);
+ $postAuthenticationMiddleware->setNextMiddleware($appAuthorizationMiddleware);
+ $appAuthorizationMiddleware->setNextMiddleware($projectAuthorizationMiddleware);
+
+ $bootstrapMiddleware->execute();
+ }
+
+ /**
+ * Execute the controller
+ */
+ protected function executeController()
+ {
+ $className = $this->getControllerClassName();
+
+ if (DEBUG) {
+ $this->logger->debug(__METHOD__.' => '.$className.'::'.$this->router->getAction());
+ }
+
+ $controllerObject = new $className($this->container);
+ $controllerObject->{$this->router->getAction()}();
+ }
+
+ /**
+ * Get controller class name
+ *
+ * @access protected
+ * @return string
+ * @throws RuntimeException
+ */
+ protected function getControllerClassName()
+ {
+ if ($this->router->getPlugin() !== '') {
+ $className = '\Kanboard\Plugin\\'.$this->router->getPlugin().'\Controller\\'.$this->router->getController();
+ } else {
+ $className = '\Kanboard\Controller\\'.$this->router->getController();
+ }
+
+ if (! class_exists($className)) {
+ throw new RuntimeException('Controller not found');
+ }
+
+ if (! method_exists($className, $this->router->getAction())) {
+ throw new RuntimeException('Action not implemented');
+ }
+
+ return $className;
+ }
+}
diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php
index 996fc58d..fd67ec95 100644
--- a/app/Core/Http/Response.php
+++ b/app/Core/Http/Response.php
@@ -13,296 +13,359 @@ use Kanboard\Core\Csv;
*/
class Response extends Base
{
+ private $httpStatusCode = 200;
+ private $httpHeaders = array();
+ private $httpBody = '';
+
/**
- * Send headers to cache a resource
+ * Set HTTP status code
*
* @access public
- * @param integer $duration
- * @param string $etag
+ * @param integer $statusCode
+ * @return $this
*/
- public function cache($duration, $etag = '')
+ public function withStatusCode($statusCode)
{
- header('Pragma: cache');
- header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT');
- header('Cache-Control: public, max-age=' . $duration);
-
- if ($etag) {
- header('ETag: "' . $etag . '"');
- }
+ $this->httpStatusCode = $statusCode;
+ return $this;
}
/**
- * Send no cache headers
+ * Set HTTP header
*
* @access public
+ * @param string $header
+ * @param string $value
+ * @return $this
*/
- public function nocache()
+ public function withHeader($header, $value)
{
- header('Pragma: no-cache');
- header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
-
- // Use no-store due to a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=28035
- header('Cache-Control: no-store, must-revalidate');
+ $this->httpHeaders[$header] = $value;
+ return $this;
}
/**
- * Send a custom Content-Type header
+ * Set content type header
*
* @access public
- * @param string $mimetype Mime-type
+ * @param string $value
+ * @return $this
*/
- public function contentType($mimetype)
+ public function withContentType($value)
{
- header('Content-Type: '.$mimetype);
+ $this->httpHeaders['Content-Type'] = $value;
+ return $this;
}
/**
- * Force the browser to download an attachment
+ * Set default security headers
*
* @access public
- * @param string $filename File name
+ * @return $this
*/
- public function forceDownload($filename)
+ public function withSecurityHeaders()
{
- header('Content-Disposition: attachment; filename="'.$filename.'"');
- header('Content-Transfer-Encoding: binary');
- header('Content-Type: application/octet-stream');
+ $this->httpHeaders['X-Content-Type-Options'] = 'nosniff';
+ $this->httpHeaders['X-XSS-Protection'] = '1; mode=block';
+ return $this;
}
/**
- * Send a custom HTTP status code
+ * Set header Content-Security-Policy
*
* @access public
- * @param integer $status_code HTTP status code
+ * @param array $policies
+ * @return $this
*/
- public function status($status_code)
+ public function withContentSecurityPolicy(array $policies = array())
{
- header('Status: '.$status_code);
- header($this->request->getServerVariable('SERVER_PROTOCOL').' '.$status_code);
+ $values = '';
+
+ foreach ($policies as $policy => $acl) {
+ $values .= $policy.' '.trim($acl).'; ';
+ }
+
+ $this->withHeader('Content-Security-Policy', $values);
+ return $this;
}
/**
- * Redirect to another URL
+ * Set header X-Frame-Options
*
* @access public
- * @param string $url Redirection URL
- * @param boolean $self If Ajax request and true: refresh the current page
+ * @return $this
*/
- public function redirect($url, $self = false)
+ public function withXframe()
{
- if ($this->request->isAjax()) {
- header('X-Ajax-Redirect: '.($self ? 'self' : $url));
- } else {
- header('Location: '.$url);
- }
-
- exit;
+ $this->withHeader('X-Frame-Options', 'DENY');
+ return $this;
}
/**
- * Send a CSV response
+ * Set header Strict-Transport-Security (only if we use HTTPS)
*
* @access public
- * @param array $data Data to serialize in csv
- * @param integer $status_code HTTP status code
+ * @return $this
*/
- public function csv(array $data, $status_code = 200)
+ public function withStrictTransportSecurity()
{
- $this->status($status_code);
- $this->nocache();
+ if ($this->request->isHTTPS()) {
+ $this->withHeader('Strict-Transport-Security', 'max-age=31536000');
+ }
- header('Content-Type: text/csv');
- Csv::output($data);
- exit;
+ return $this;
}
/**
- * Send a Json response
+ * Set HTTP response body
*
* @access public
- * @param array $data Data to serialize in json
- * @param integer $status_code HTTP status code
+ * @param string $body
+ * @return $this
*/
- public function json(array $data, $status_code = 200)
+ public function withBody($body)
{
- $this->status($status_code);
- $this->nocache();
- header('Content-Type: application/json');
- echo json_encode($data);
- exit;
+ $this->httpBody = $body;
+ return $this;
}
/**
- * Send a text response
+ * Send headers to cache a resource
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @param integer $duration
+ * @param string $etag
+ * @return $this
*/
- public function text($data, $status_code = 200)
+ public function withCache($duration, $etag = '')
{
- $this->status($status_code);
- $this->nocache();
- header('Content-Type: text/plain; charset=utf-8');
- echo $data;
- exit;
+ $this
+ ->withHeader('Pragma', 'cache')
+ ->withHeader('Expires', gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT')
+ ->withHeader('Cache-Control', 'public, max-age=' . $duration)
+ ;
+
+ if ($etag) {
+ $this->withHeader('ETag', '"' . $etag . '"');
+ }
+
+ return $this;
}
/**
- * Send a HTML response
+ * Send no cache headers
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @return $this
*/
- public function html($data, $status_code = 200)
+ public function withoutCache()
{
- $this->status($status_code);
- $this->nocache();
- header('Content-Type: text/html; charset=utf-8');
- echo $data;
- exit;
+ $this->withHeader('Pragma', 'no-cache');
+ $this->withHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
+ return $this;
}
/**
- * Send a XML response
+ * Force the browser to download an attachment
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @param string $filename
+ * @return $this
*/
- public function xml($data, $status_code = 200)
+ public function withDownload($filename)
{
- $this->status($status_code);
- $this->nocache();
- header('Content-Type: text/xml; charset=utf-8');
- echo $data;
- exit;
+ $this->withHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
+ $this->withHeader('Content-Transfer-Encoding', 'binary');
+ $this->withHeader('Content-Type', 'application/octet-stream');
+ return $this;
}
/**
- * Send a javascript response
+ * Send headers and body
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
*/
- public function js($data, $status_code = 200)
+ public function send()
{
- $this->status($status_code);
+ if ($this->httpStatusCode !== 200) {
+ header('Status: '.$this->httpStatusCode);
+ header($this->request->getServerVariable('SERVER_PROTOCOL').' '.$this->httpStatusCode);
+ }
- header('Content-Type: text/javascript; charset=utf-8');
- echo $data;
+ foreach ($this->httpHeaders as $header => $value) {
+ header($header.': '.$value);
+ }
- exit;
+ if (! empty($this->httpBody)) {
+ echo $this->httpBody;
+ }
}
/**
- * Send a css response
+ * Send a custom HTTP status code
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @param integer $statusCode
*/
- public function css($data, $status_code = 200)
+ public function status($statusCode)
{
- $this->status($status_code);
+ $this->withStatusCode($statusCode);
+ $this->send();
+ }
- header('Content-Type: text/css; charset=utf-8');
- echo $data;
+ /**
+ * Redirect to another URL
+ *
+ * @access public
+ * @param string $url Redirection URL
+ * @param boolean $self If Ajax request and true: refresh the current page
+ */
+ public function redirect($url, $self = false)
+ {
+ if ($this->request->isAjax()) {
+ $this->withHeader('X-Ajax-Redirect', $self ? 'self' : $url);
+ } else {
+ $this->withHeader('Location', $url);
+ }
- exit;
+ $this->send();
}
/**
- * Send a binary response
+ * Send a HTML response
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @param string $data
+ * @param integer $statusCode
*/
- public function binary($data, $status_code = 200)
+ public function html($data, $statusCode = 200)
{
- $this->status($status_code);
- $this->nocache();
- header('Content-Transfer-Encoding: binary');
- header('Content-Type: application/octet-stream');
- echo $data;
- exit;
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/html; charset=utf-8');
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send a iCal response
+ * Send a text response
*
* @access public
- * @param string $data Raw data
- * @param integer $status_code HTTP status code
+ * @param string $data
+ * @param integer $statusCode
*/
- public function ical($data, $status_code = 200)
+ public function text($data, $statusCode = 200)
{
- $this->status($status_code);
- $this->contentType('text/calendar; charset=utf-8');
- echo $data;
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/plain; charset=utf-8');
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send the security header: Content-Security-Policy
+ * Send a CSV response
*
* @access public
- * @param array $policies CSP rules
+ * @param array $data Data to serialize in csv
*/
- public function csp(array $policies = array())
+ public function csv(array $data)
{
- $values = '';
+ $this->withoutCache();
+ $this->withContentType('text/csv; charset=utf-8');
+ $this->send();
+ Csv::output($data);
+ }
- foreach ($policies as $policy => $acl) {
- $values .= $policy.' '.trim($acl).'; ';
- }
+ /**
+ * Send a Json response
+ *
+ * @access public
+ * @param array $data Data to serialize in json
+ * @param integer $statusCode HTTP status code
+ */
+ public function json(array $data, $statusCode = 200)
+ {
+ $this->withStatusCode($statusCode);
+ $this->withContentType('application/json');
+ $this->withoutCache();
+ $this->withBody(json_encode($data));
+ $this->send();
+ }
- header('Content-Security-Policy: '.$values);
+ /**
+ * Send a XML response
+ *
+ * @access public
+ * @param string $data
+ * @param integer $statusCode
+ */
+ public function xml($data, $statusCode = 200)
+ {
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/xml; charset=utf-8');
+ $this->withoutCache();
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send the security header: X-Content-Type-Options
+ * Send a javascript response
*
* @access public
+ * @param string $data
+ * @param integer $statusCode
*/
- public function nosniff()
+ public function js($data, $statusCode = 200)
{
- header('X-Content-Type-Options: nosniff');
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/javascript; charset=utf-8');
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send the security header: X-XSS-Protection
+ * Send a css response
*
* @access public
+ * @param string $data
+ * @param integer $statusCode
*/
- public function xss()
+ public function css($data, $statusCode = 200)
{
- header('X-XSS-Protection: 1; mode=block');
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/css; charset=utf-8');
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send the security header: Strict-Transport-Security (only if we use HTTPS)
+ * Send a binary response
*
* @access public
+ * @param string $data
+ * @param integer $statusCode
*/
- public function hsts()
+ public function binary($data, $statusCode = 200)
{
- if ($this->request->isHTTPS()) {
- header('Strict-Transport-Security: max-age=31536000');
- }
+ $this->withStatusCode($statusCode);
+ $this->withoutCache();
+ $this->withHeader('Content-Transfer-Encoding', 'binary');
+ $this->withContentType('application/octet-stream');
+ $this->withBody($data);
+ $this->send();
}
/**
- * Send the security header: X-Frame-Options (deny by default)
+ * Send a iCal response
*
* @access public
- * @param string $mode Frame option mode
- * @param array $urls Allowed urls for the given mode
+ * @param string $data
+ * @param integer $statusCode
*/
- public function xframe($mode = 'DENY', array $urls = array())
+ public function ical($data, $statusCode = 200)
{
- header('X-Frame-Options: '.$mode.' '.implode(' ', $urls));
+ $this->withStatusCode($statusCode);
+ $this->withContentType('text/calendar; charset=utf-8');
+ $this->withBody($data);
+ $this->send();
}
}
diff --git a/app/Core/Http/Route.php b/app/Core/Http/Route.php
index 7836146d..9b45b725 100644
--- a/app/Core/Http/Route.php
+++ b/app/Core/Http/Route.php
@@ -119,8 +119,8 @@ class Route extends Base
}
return array(
- 'controller' => 'app',
- 'action' => 'index',
+ 'controller' => 'DashboardController',
+ 'action' => 'show',
'plugin' => '',
);
}
diff --git a/app/Core/Http/Router.php b/app/Core/Http/Router.php
index 0fe80ecc..4de276a0 100644
--- a/app/Core/Http/Router.php
+++ b/app/Core/Http/Router.php
@@ -2,7 +2,6 @@
namespace Kanboard\Core\Http;
-use RuntimeException;
use Kanboard\Core\Base;
/**
@@ -13,13 +12,16 @@ use Kanboard\Core\Base;
*/
class Router extends Base
{
+ const DEFAULT_CONTROLLER = 'DashboardController';
+ const DEFAULT_METHOD = 'show';
+
/**
* Plugin name
*
* @access private
* @var string
*/
- private $plugin = '';
+ private $currentPluginName = '';
/**
* Controller
@@ -27,7 +29,7 @@ class Router extends Base
* @access private
* @var string
*/
- private $controller = '';
+ private $currentControllerName = '';
/**
* Action
@@ -35,7 +37,7 @@ class Router extends Base
* @access private
* @var string
*/
- private $action = '';
+ private $currentActionName = '';
/**
* Get plugin name
@@ -45,7 +47,7 @@ class Router extends Base
*/
public function getPlugin()
{
- return $this->plugin;
+ return $this->currentPluginName;
}
/**
@@ -56,7 +58,7 @@ class Router extends Base
*/
public function getController()
{
- return $this->controller;
+ return $this->currentControllerName;
}
/**
@@ -67,7 +69,7 @@ class Router extends Base
*/
public function getAction()
{
- return $this->action;
+ return $this->currentActionName;
}
/**
@@ -109,11 +111,9 @@ class Router extends Base
$plugin = $route['plugin'];
}
- $this->controller = ucfirst($this->sanitize($controller, 'app'));
- $this->action = $this->sanitize($action, 'index');
- $this->plugin = ucfirst($this->sanitize($plugin));
-
- return $this->executeAction();
+ $this->currentControllerName = ucfirst($this->sanitize($controller, self::DEFAULT_CONTROLLER));
+ $this->currentActionName = $this->sanitize($action, self::DEFAULT_METHOD);
+ $this->currentPluginName = ucfirst($this->sanitize($plugin));
}
/**
@@ -128,42 +128,4 @@ class Router extends Base
{
return preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $value : $default;
}
-
- /**
- * Execute controller action
- *
- * @access private
- */
- private function executeAction()
- {
- $class = $this->getControllerClassName();
-
- if (! class_exists($class)) {
- throw new RuntimeException('Controller not found');
- }
-
- if (! method_exists($class, $this->action)) {
- throw new RuntimeException('Action not implemented');
- }
-
- $instance = new $class($this->container);
- $instance->beforeAction();
- $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;
- }
}
diff --git a/app/Helper/LayoutHelper.php b/app/Helper/LayoutHelper.php
index cbda85a3..39defc88 100644
--- a/app/Helper/LayoutHelper.php
+++ b/app/Helper/LayoutHelper.php
@@ -130,7 +130,7 @@ class LayoutHelper extends Base
*/
public function dashboard($template, array $params)
{
- return $this->subLayout('app/layout', 'app/sidebar', $template, $params);
+ return $this->subLayout('dashboard/layout', 'dashboard/sidebar', $template, $params);
}
/**
diff --git a/app/Middleware/ApplicationAuthorizationMiddleware.php b/app/Middleware/ApplicationAuthorizationMiddleware.php
new file mode 100644
index 00000000..faca2d6a
--- /dev/null
+++ b/app/Middleware/ApplicationAuthorizationMiddleware.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Kanboard\Middleware;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\BaseMiddleware;
+
+/**
+ * Class ApplicationAuthorizationMiddleware
+ *
+ * @package Kanboard\Middleware
+ * @author Frederic Guillot
+ */
+class ApplicationAuthorizationMiddleware extends BaseMiddleware
+{
+ /**
+ * Execute middleware
+ */
+ public function execute()
+ {
+ if (! $this->helper->user->hasAccess($this->router->getController(), $this->router->getAction())) {
+ throw new AccessForbiddenException();
+ }
+
+ $this->next();
+ }
+}
diff --git a/app/Middleware/AuthenticationMiddleware.php b/app/Middleware/AuthenticationMiddleware.php
new file mode 100644
index 00000000..a31198a5
--- /dev/null
+++ b/app/Middleware/AuthenticationMiddleware.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Kanboard\Middleware;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\BaseMiddleware;
+use Kanboard\Core\Security\Role;
+
+/**
+ * Class AuthenticationMiddleware
+ *
+ * @package Kanboard\Middleware
+ * @author Frederic Guillot
+ */
+class AuthenticationMiddleware extends BaseMiddleware
+{
+ /**
+ * Execute middleware
+ */
+ public function execute()
+ {
+ if (! $this->authenticationManager->checkCurrentSession()) {
+ throw AccessForbiddenException::getInstance()->withoutLayout();
+ }
+
+ if (! $this->isPublicAccess()) {
+ $this->handleAuthentication();
+ }
+
+ $this->next();
+ }
+
+ protected function handleAuthentication()
+ {
+ if (! $this->userSession->isLogged() && ! $this->authenticationManager->preAuthentication()) {
+ $this->setNextMiddleware(null);
+
+ if ($this->request->isAjax()) {
+ $this->response->text('Not Authorized', 401);
+ } else {
+ $this->sessionStorage->redirectAfterLogin = $this->request->getUri();
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ }
+ }
+ }
+
+ private function isPublicAccess()
+ {
+ if ($this->applicationAuthorization->isAllowed($this->router->getController(), $this->router->getAction(), Role::APP_PUBLIC)) {
+ $this->setNextMiddleware(null);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/app/Middleware/BootstrapMiddleware.php b/app/Middleware/BootstrapMiddleware.php
new file mode 100644
index 00000000..c9de1de9
--- /dev/null
+++ b/app/Middleware/BootstrapMiddleware.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Kanboard\Middleware;
+
+use Kanboard\Core\Controller\BaseMiddleware;
+
+/**
+ * Class BootstrapMiddleware
+ *
+ * @package Kanboard\Middleware
+ * @author Frederic Guillot
+ */
+class BootstrapMiddleware extends BaseMiddleware
+{
+ /**
+ * Execute middleware
+ */
+ public function execute()
+ {
+ $this->sessionManager->open();
+ $this->dispatcher->dispatch('app.bootstrap');
+ $this->sendHeaders();
+ $this->next();
+ }
+
+ /**
+ * Send HTTP headers
+ *
+ * @access private
+ */
+ private function sendHeaders()
+ {
+ $this->response->withContentSecurityPolicy($this->container['cspRules']);
+ $this->response->withSecurityHeaders();
+
+ if (ENABLE_XFRAME && $this->router->getAction() !== 'readonly') {
+ $this->response->withXframe();
+ }
+
+ if (ENABLE_HSTS) {
+ $this->response->withStrictTransportSecurity();
+ }
+ }
+}
diff --git a/app/Middleware/PostAuthenticationMiddleware.php b/app/Middleware/PostAuthenticationMiddleware.php
new file mode 100644
index 00000000..8287c10e
--- /dev/null
+++ b/app/Middleware/PostAuthenticationMiddleware.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Kanboard\Middleware;
+
+use Kanboard\Core\Controller\BaseMiddleware;
+
+/**
+ * Class PostAuthenticationMiddleware
+ *
+ * @package Kanboard\Middleware
+ * @author Frederic Guillot
+ */
+class PostAuthenticationMiddleware extends BaseMiddleware
+{
+ /**
+ * Execute middleware
+ */
+ public function execute()
+ {
+ $controller = strtolower($this->router->getController());
+ $action = strtolower($this->router->getAction());
+ $ignore = ($controller === 'twofactor' && in_array($action, array('code', 'check'))) || ($controller === 'auth' && $action === 'logout');
+
+ if ($ignore === false && $this->userSession->hasPostAuthentication() && ! $this->userSession->isPostAuthenticationValidated()) {
+ $this->setNextMiddleware(null);
+
+ if ($this->request->isAjax()) {
+ $this->response->text('Not Authorized', 401);
+ }
+
+ $this->response->redirect($this->helper->url->to('twofactor', 'code'));
+ }
+
+ $this->next();
+ }
+}
diff --git a/app/Middleware/ProjectAuthorizationMiddleware.php b/app/Middleware/ProjectAuthorizationMiddleware.php
new file mode 100644
index 00000000..6000ee0e
--- /dev/null
+++ b/app/Middleware/ProjectAuthorizationMiddleware.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Kanboard\Middleware;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\Controller\BaseMiddleware;
+
+/**
+ * Class ProjectAuthorizationMiddleware
+ *
+ * @package Kanboard\Middleware
+ * @author Frederic Guillot
+ */
+class ProjectAuthorizationMiddleware extends BaseMiddleware
+{
+ /**
+ * Execute middleware
+ */
+ public function execute()
+ {
+ $project_id = $this->request->getIntegerParam('project_id');
+ $task_id = $this->request->getIntegerParam('task_id');
+
+ if ($task_id > 0 && $project_id === 0) {
+ $project_id = $this->taskFinder->getProjectId($task_id);
+ }
+
+ if ($project_id > 0 && ! $this->helper->user->hasProjectAccess($this->router->getController(), $this->router->getAction(), $project_id)) {
+ throw new AccessForbiddenException();
+ }
+
+ $this->next();
+ }
+}
diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php
index 30e7b648..2788e3a7 100644
--- a/app/ServiceProvider/RouteProvider.php
+++ b/app/ServiceProvider/RouteProvider.php
@@ -31,14 +31,14 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->enable();
// Dashboard
- $container['route']->addRoute('dashboard', 'app', 'index');
- $container['route']->addRoute('dashboard/:user_id', 'app', 'index');
- $container['route']->addRoute('dashboard/:user_id/projects', 'app', 'projects');
- $container['route']->addRoute('dashboard/:user_id/tasks', 'app', 'tasks');
- $container['route']->addRoute('dashboard/:user_id/subtasks', 'app', 'subtasks');
- $container['route']->addRoute('dashboard/:user_id/calendar', 'app', 'calendar');
- $container['route']->addRoute('dashboard/:user_id/activity', 'app', 'activity');
- $container['route']->addRoute('dashboard/:user_id/notifications', 'app', 'notifications');
+ $container['route']->addRoute('dashboard', 'DashboardController', 'show');
+ $container['route']->addRoute('dashboard/:user_id', 'DashboardController', 'show');
+ $container['route']->addRoute('dashboard/:user_id/projects', 'DashboardController', 'projects');
+ $container['route']->addRoute('dashboard/:user_id/tasks', 'DashboardController', 'tasks');
+ $container['route']->addRoute('dashboard/:user_id/subtasks', 'DashboardController', 'subtasks');
+ $container['route']->addRoute('dashboard/:user_id/calendar', 'DashboardController', 'calendar');
+ $container['route']->addRoute('dashboard/:user_id/activity', 'DashboardController', 'activity');
+ $container['route']->addRoute('dashboard/:user_id/notifications', 'DashboardController', 'notifications');
// Search routes
$container['route']->addRoute('search', 'search', 'index');
diff --git a/app/Subscriber/BootstrapSubscriber.php b/app/Subscriber/BootstrapSubscriber.php
index a376a935..b82405f8 100644
--- a/app/Subscriber/BootstrapSubscriber.php
+++ b/app/Subscriber/BootstrapSubscriber.php
@@ -29,7 +29,7 @@ class BootstrapSubscriber extends BaseSubscriber implements EventSubscriberInter
{
if (DEBUG) {
foreach ($this->db->getLogMessages() as $message) {
- $this->logger->debug($message);
+ $this->logger->debug('SQL: ' . $message);
}
$this->logger->debug('nb_queries={nb}', array('nb' => $this->db->getStatementHandler()->getNbQueries()));
diff --git a/app/Template/app/sidebar.php b/app/Template/app/sidebar.php
deleted file mode 100644
index 66d15b14..00000000
--- a/app/Template/app/sidebar.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<div class="sidebar">
- <h2><?= $this->text->e($user['name'] ?: $user['username']) ?></h2>
- <ul>
- <li <?= $this->app->checkMenuSelection('app', 'index') ?>>
- <?= $this->url->link(t('Overview'), 'app', 'index', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'projects') ?>>
- <?= $this->url->link(t('My projects'), 'app', 'projects', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'tasks') ?>>
- <?= $this->url->link(t('My tasks'), 'app', 'tasks', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'subtasks') ?>>
- <?= $this->url->link(t('My subtasks'), 'app', 'subtasks', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'calendar') ?>>
- <?= $this->url->link(t('My calendar'), 'app', 'calendar', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'activity') ?>>
- <?= $this->url->link(t('My activity stream'), 'app', 'activity', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('app', 'notifications') ?>>
- <?= $this->url->link(t('My notifications'), 'app', 'notifications', array('user_id' => $user['id'])) ?>
- </li>
- <?= $this->hook->render('template:dashboard:sidebar') ?>
- </ul>
-</div> \ No newline at end of file
diff --git a/app/Template/config/application.php b/app/Template/config/application.php
index 259756bc..ee0e147b 100644
--- a/app/Template/config/application.php
+++ b/app/Template/config/application.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Application settings') ?></h2>
</div>
-<form method="post" action="<?= $this->url->href('config', 'application') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'application')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
diff --git a/app/Template/config/board.php b/app/Template/config/board.php
index ba1bab59..75cd40ef 100644
--- a/app/Template/config/board.php
+++ b/app/Template/config/board.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Board settings') ?></h2>
</div>
-<form method="post" action="<?= $this->url->href('config', 'board') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'board')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
diff --git a/app/Template/config/calendar.php b/app/Template/config/calendar.php
index b7b230df..37084a8b 100644
--- a/app/Template/config/calendar.php
+++ b/app/Template/config/calendar.php
@@ -2,7 +2,7 @@
<h2><?= t('Calendar settings') ?></h2>
</div>
<section>
-<form method="post" action="<?= $this->url->href('config', 'calendar') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'calendar')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
@@ -31,4 +31,4 @@
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div>
</form>
-</section> \ No newline at end of file
+</section>
diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php
index e404c52e..2a29b358 100644
--- a/app/Template/config/integrations.php
+++ b/app/Template/config/integrations.php
@@ -2,7 +2,7 @@
<h2><?= t('Integration with third-party services') ?></h2>
</div>
-<form method="post" action="<?= $this->url->href('config', 'integrations') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'integrations')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->hook->render('template:config:integrations', array('values' => $values)) ?>
@@ -14,4 +14,4 @@
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/config/layout.php b/app/Template/config/layout.php
index f34caaab..6eafa593 100644
--- a/app/Template/config/layout.php
+++ b/app/Template/config/layout.php
@@ -1,10 +1,9 @@
<section id="main">
<section class="sidebar-container" id="config-section">
-
<?= $this->render($sidebar_template) ?>
<div class="sidebar-content">
<?= $content_for_sublayout ?>
</div>
</section>
-</section> \ No newline at end of file
+</section>
diff --git a/app/Template/config/project.php b/app/Template/config/project.php
index b6b7ec25..b0112773 100644
--- a/app/Template/config/project.php
+++ b/app/Template/config/project.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Project settings') ?></h2>
</div>
-<form method="post" action="<?= $this->url->href('config', 'project') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'project')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
diff --git a/app/Template/config/webhook.php b/app/Template/config/webhook.php
index b96979a0..5db1fa6e 100644
--- a/app/Template/config/webhook.php
+++ b/app/Template/config/webhook.php
@@ -2,7 +2,7 @@
<h2><?= t('Webhook settings') ?></h2>
</div>
<section>
-<form method="post" action="<?= $this->url->href('config', 'webhook') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('config', 'save', array('redirect' => 'webhook')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
@@ -32,4 +32,4 @@
<?= $this->url->link(t('Reset token'), 'config', 'token', array('type' => 'webhook'), true) ?>
</li>
</ul>
-</section> \ No newline at end of file
+</section>
diff --git a/app/Template/app/activity.php b/app/Template/dashboard/activity.php
index 71a67fb2..71a67fb2 100644
--- a/app/Template/app/activity.php
+++ b/app/Template/dashboard/activity.php
diff --git a/app/Template/app/calendar.php b/app/Template/dashboard/calendar.php
index a154203b..a154203b 100644
--- a/app/Template/app/calendar.php
+++ b/app/Template/dashboard/calendar.php
diff --git a/app/Template/app/layout.php b/app/Template/dashboard/layout.php
index 2a32ac02..2a32ac02 100644
--- a/app/Template/app/layout.php
+++ b/app/Template/dashboard/layout.php
diff --git a/app/Template/app/notifications.php b/app/Template/dashboard/notifications.php
index b64eb0b7..b64eb0b7 100644
--- a/app/Template/app/notifications.php
+++ b/app/Template/dashboard/notifications.php
diff --git a/app/Template/app/projects.php b/app/Template/dashboard/projects.php
index c0110b07..cdf19bdf 100644
--- a/app/Template/app/projects.php
+++ b/app/Template/dashboard/projects.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= $this->url->link(t('My projects'), 'app', 'projects', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
+ <h2><?= $this->url->link(t('My projects'), 'DashboardController', 'projects', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('Your are not member of any project.') ?></p>
diff --git a/app/Template/app/overview.php b/app/Template/dashboard/show.php
index 0b354791..bc4e2952 100644
--- a/app/Template/app/overview.php
+++ b/app/Template/dashboard/show.php
@@ -7,6 +7,6 @@
</form>
</div>
-<?= $this->render('app/projects', array('paginator' => $project_paginator, 'user' => $user)) ?>
-<?= $this->render('app/tasks', array('paginator' => $task_paginator, 'user' => $user)) ?>
-<?= $this->render('app/subtasks', array('paginator' => $subtask_paginator, 'user' => $user)) ?> \ No newline at end of file
+<?= $this->render('dashboard/projects', array('paginator' => $project_paginator, 'user' => $user)) ?>
+<?= $this->render('dashboard/tasks', array('paginator' => $task_paginator, 'user' => $user)) ?>
+<?= $this->render('dashboard/subtasks', array('paginator' => $subtask_paginator, 'user' => $user)) ?>
diff --git a/app/Template/dashboard/sidebar.php b/app/Template/dashboard/sidebar.php
new file mode 100644
index 00000000..86cc20f8
--- /dev/null
+++ b/app/Template/dashboard/sidebar.php
@@ -0,0 +1,27 @@
+<div class="sidebar">
+ <h2><?= $this->text->e($user['name'] ?: $user['username']) ?></h2>
+ <ul>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'show') ?>>
+ <?= $this->url->link(t('Overview'), 'DashboardController', 'show', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'projects') ?>>
+ <?= $this->url->link(t('My projects'), 'DashboardController', 'projects', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'tasks') ?>>
+ <?= $this->url->link(t('My tasks'), 'DashboardController', 'tasks', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'subtasks') ?>>
+ <?= $this->url->link(t('My subtasks'), 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'calendar') ?>>
+ <?= $this->url->link(t('My calendar'), 'DashboardController', 'calendar', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'activity') ?>>
+ <?= $this->url->link(t('My activity stream'), 'DashboardController', 'activity', array('user_id' => $user['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('DashboardController', 'notifications') ?>>
+ <?= $this->url->link(t('My notifications'), 'DashboardController', 'notifications', array('user_id' => $user['id'])) ?>
+ </li>
+ <?= $this->hook->render('template:dashboard:sidebar') ?>
+ </ul>
+</div>
diff --git a/app/Template/app/subtasks.php b/app/Template/dashboard/subtasks.php
index cca09481..ee6caf02 100644
--- a/app/Template/app/subtasks.php
+++ b/app/Template/dashboard/subtasks.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= $this->url->link(t('My subtasks'), 'app', 'subtasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
+ <h2><?= $this->url->link(t('My subtasks'), 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
@@ -40,4 +40,4 @@
</table>
<?= $paginator ?>
-<?php endif ?> \ No newline at end of file
+<?php endif ?>
diff --git a/app/Template/app/tasks.php b/app/Template/dashboard/tasks.php
index f0ed61e0..71b62572 100644
--- a/app/Template/app/tasks.php
+++ b/app/Template/dashboard/tasks.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= $this->url->link(t('My tasks'), 'app', 'tasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
+ <h2><?= $this->url->link(t('My tasks'), 'DashboardController', 'tasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
diff --git a/app/Template/header.php b/app/Template/header.php
index 3885e671..f47b270f 100644
--- a/app/Template/header.php
+++ b/app/Template/header.php
@@ -2,7 +2,7 @@
<nav>
<h1>
<span class="logo">
- <?= $this->url->link('K<span>B</span>', 'app', 'index', array(), false, '', t('Dashboard')) ?>
+ <?= $this->url->link('K<span>B</span>', 'DashboardController', 'show', array(), false, '', t('Dashboard')) ?>
</span>
<span class="title">
<?= $this->text->e($title) ?>
@@ -34,7 +34,7 @@
<li class="user-links">
<?php if ($this->user->hasNotifications()): ?>
<span class="notification">
- <?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'app', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?>
+ <?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'DashboardController', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?>
</span>
<?php endif ?>
@@ -63,7 +63,7 @@
<li class="no-hover"><strong><?= $this->text->e($this->user->getFullname()) ?></strong></li>
<li>
<i class="fa fa-tachometer fa-fw"></i>
- <?= $this->url->link(t('My dashboard'), 'app', 'index', array('user_id' => $this->user->getId())) ?>
+ <?= $this->url->link(t('My dashboard'), 'DashboardController', 'show', array('user_id' => $this->user->getId())) ?>
</li>
<li>
<i class="fa fa-home fa-fw"></i>
diff --git a/app/Template/layout.php b/app/Template/layout.php
index a80fc288..701b297d 100644
--- a/app/Template/layout.php
+++ b/app/Template/layout.php
@@ -44,7 +44,7 @@
<?= $this->hook->render('template:layout:head') ?>
</head>
- <body data-status-url="<?= $this->url->href('app', 'status') ?>"
+ <body data-status-url="<?= $this->url->href('UserHelper', 'status') ?>"
data-login-url="<?= $this->url->href('auth', 'login') ?>"
data-keyboard-shortcut-url="<?= $this->url->href('Doc', 'shortcuts') ?>"
data-timezone="<?= $this->app->getTimezone() ?>"
diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php
index 5ea2e355..9da43ced 100644
--- a/app/Template/user/sidebar.php
+++ b/app/Template/user/sidebar.php
@@ -8,7 +8,7 @@
<?php endif ?>
<?php if ($this->user->isAdmin()): ?>
<li>
- <?= $this->url->link(t('User dashboard'), 'app', 'index', array('user_id' => $user['id'])) ?>
+ <?= $this->url->link(t('User dashboard'), 'DashboardController', 'show', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>
@@ -80,4 +80,4 @@
<?= $this->hook->render('template:user:sidebar:actions', array('user' => $user)) ?>
</ul>
-</div> \ No newline at end of file
+</div>