From bc98748c0d5b1e1c4d08f16b58db8aea9b5fb5ce Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sun, 12 Jul 2015 10:38:42 -0400 Subject: Start to implement url rewrite (nice urls) --- .htaccess | 9 ++ app/Api/Project.php | 6 +- app/Api/Task.php | 2 +- app/Auth/RememberMe.php | 4 +- app/Controller/Action.php | 8 +- app/Controller/Base.php | 19 +--- app/Controller/Category.php | 8 +- app/Controller/Comment.php | 8 +- app/Controller/Config.php | 6 +- app/Controller/Project.php | 25 ++--- app/Controller/Search.php | 2 +- app/Controller/Subtask.php | 12 +-- app/Controller/Swimlane.php | 18 ++-- app/Controller/Task.php | 30 +++--- app/Controller/User.php | 22 ++-- app/Core/Router.php | 195 ++++++++++++++++++++++++++-------- app/Core/Session.php | 2 - app/Helper/Asset.php | 4 +- app/Helper/Url.php | 96 ++++++++++++----- app/ServiceProvider/ClassProvider.php | 1 + app/Template/app/overview.php | 4 +- app/Template/config/integrations.php | 18 ++-- app/Template/config/webhook.php | 2 +- app/Template/feed/project.php | 6 +- app/Template/feed/user.php | 6 +- app/Template/layout.php | 10 +- app/Template/project/filters.php | 4 +- app/Template/project/integrations.php | 12 +-- app/Template/search/index.php | 2 +- app/common.php | 92 +++++++++++++++- app/constants.php | 6 +- config.default.php | 4 + index.php | 7 +- tests/units/RouterTest.php | 79 ++++++++++++++ tests/units/UrlHelperTest.php | 18 ++++ 35 files changed, 543 insertions(+), 204 deletions(-) create mode 100644 .htaccess create mode 100644 tests/units/RouterTest.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..0d873f58 --- /dev/null +++ b/.htaccess @@ -0,0 +1,9 @@ + + Options -MultiViews + + SetEnv HTTP_MOD_REWRITE On + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [QSA,L] + diff --git a/app/Api/Project.php b/app/Api/Project.php index faf2a3da..4e4e10b8 100644 --- a/app/Api/Project.php +++ b/app/Api/Project.php @@ -87,9 +87,9 @@ class Project extends Base { if (! empty($project)) { $project['url'] = array( - 'board' => $this->helper->url->base().$this->helper->url->to('board', 'show', array('project_id' => $project['id'])), - 'calendar' => $this->helper->url->base().$this->helper->url->to('calendar', 'show', array('project_id' => $project['id'])), - 'list' => $this->helper->url->base().$this->helper->url->to('listing', 'show', array('project_id' => $project['id'])), + 'board' => $this->helper->url->to('board', 'show', array('project_id' => $project['id']), '', true), + 'calendar' => $this->helper->url->to('calendar', 'show', array('project_id' => $project['id']), '', true), + 'list' => $this->helper->url->to('listing', 'show', array('project_id' => $project['id']), '', true), ); } diff --git a/app/Api/Task.php b/app/Api/Task.php index ade49a6d..3b8c1ec8 100644 --- a/app/Api/Task.php +++ b/app/Api/Task.php @@ -119,7 +119,7 @@ class Task extends Base private function formatTask($task) { if (! empty($task)) { - $task['url'] = $this->helper->url->base().$this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])); + $task['url'] = $this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), '', true); } return $task; diff --git a/app/Auth/RememberMe.php b/app/Auth/RememberMe.php index eebf4f4b..54e60422 100644 --- a/app/Auth/RememberMe.php +++ b/app/Auth/RememberMe.php @@ -282,7 +282,7 @@ class RememberMe extends Base self::COOKIE_NAME, $this->encodeCookie($token, $sequence), $expiration, - BASE_URL_DIRECTORY, + $this->helper->url->dir(), null, Request::isHTTPS(), true @@ -315,7 +315,7 @@ class RememberMe extends Base self::COOKIE_NAME, '', time() - 3600, - BASE_URL_DIRECTORY, + $this->helper->url->dir(), null, Request::isHTTPS(), true diff --git a/app/Controller/Action.php b/app/Controller/Action.php index cd24453a..74a5326d 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -46,7 +46,7 @@ class Action extends Base $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id'])) { - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('action/event', array( @@ -68,7 +68,7 @@ class Action extends Base $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) { - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } $action = $this->action->load($values['action_name'], $values['project_id'], $values['event_name']); @@ -125,7 +125,7 @@ class Action extends Base } } - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } /** @@ -163,6 +163,6 @@ class Action extends Base $this->session->flashError(t('Unable to remove this action.')); } - $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 9f5d6dc6..18187162 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -101,7 +101,7 @@ abstract class Base extends \Core\Base public function beforeAction($controller, $action) { // Start the session - $this->session->open(BASE_URL_DIRECTORY); + $this->session->open($this->helper->url->dir()); $this->sendHeaders($action); $this->container['dispatcher']->dispatch('session.bootstrap', new Event); @@ -222,17 +222,6 @@ abstract class Base extends \Core\Base } } - /** - * Redirection when there is no project in the database - * - * @access protected - */ - protected function redirectNoProject() - { - $this->session->flash(t('There is no active project, the first step is to create a new project.')); - $this->response->redirect('?controller=project&action=create'); - } - /** * Common layout for task views * @@ -301,7 +290,7 @@ abstract class Base extends \Core\Base if (empty($project)) { $this->session->flashError(t('Project not found.')); - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } return $project; @@ -344,10 +333,10 @@ abstract class Base extends \Core\Base 'controller' => $controller, 'action' => $action, 'project_id' => $project['id'], - 'search' => $search, + 'search' => urldecode($search), ); - $this->userSession->setFilters($project['id'], $search); + $this->userSession->setFilters($project['id'], $filters['search']); return array( 'project' => $project, diff --git a/app/Controller/Category.php b/app/Controller/Category.php index 515cc9c8..e8d83f2d 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -23,7 +23,7 @@ class Category extends Base if (empty($category)) { $this->session->flashError(t('Category not found.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project_id))); } return $category; @@ -63,7 +63,7 @@ class Category extends Base if ($this->category->create($values)) { $this->session->flash(t('Your category have been created successfully.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to create your category.')); @@ -107,7 +107,7 @@ class Category extends Base if ($this->category->update($values)) { $this->session->flash(t('Your category have been updated successfully.')); - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update your category.')); @@ -151,6 +151,6 @@ class Category extends Base $this->session->flashError(t('Unable to remove this category.')); } - $this->response->redirect('?controller=category&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php index a5f6b1f8..ca701a88 100644 --- a/app/Controller/Comment.php +++ b/app/Controller/Comment.php @@ -90,10 +90,10 @@ class Comment extends Base } if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comments'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), 'comments'); } $this->create($values, $errors); @@ -140,7 +140,7 @@ class Comment extends Base $this->session->flashError(t('Unable to update your comment.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comment-'.$comment['id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), 'comment-'.$comment['id']); } $this->edit($values, $errors); @@ -181,6 +181,6 @@ class Comment extends Base $this->session->flashError(t('Unable to remove this comment.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#comments'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), 'comments'); } } diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 19bc2767..1e6b3dc8 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -60,7 +60,7 @@ class Config extends Base $this->session->flashError(t('Unable to save your settings.')); } - $this->response->redirect('?controller=config&action='.$redirect); + $this->response->redirect($this->helper->url->to('config', $redirect)); } } @@ -199,7 +199,7 @@ class Config extends Base $this->checkCSRFParam(); $this->config->optimizeDatabase(); $this->session->flash(t('Database optimization done.')); - $this->response->redirect('?controller=config'); + $this->response->redirect($this->helper->url->to('config', 'index')); } /** @@ -215,6 +215,6 @@ class Config extends Base $this->config->regenerateToken($type.'_token'); $this->session->flash(t('Token regenerated.')); - $this->response->redirect('?controller=config&action='.$type); + $this->response->redirect($this->helper->url->to('config', $type)); } } diff --git a/app/Controller/Project.php b/app/Controller/Project.php index faebac38..45bc2a46 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -73,11 +73,12 @@ class Project extends Base if ($this->project->{$switch.'PublicAccess'}($project['id'])) { $this->session->flash(t('Project updated successfully.')); - } else { + } + else { $this->session->flashError(t('Unable to update this project.')); } - $this->response->redirect('?controller=project&action=share&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/share', array( @@ -150,7 +151,7 @@ class Project extends Base if ($this->project->update($values)) { $this->session->flash(t('Project updated successfully.')); - $this->response->redirect('?controller=project&action=edit&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'edit', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this project.')); @@ -197,7 +198,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $project['id']))); } /** @@ -220,7 +221,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -250,7 +251,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -279,7 +280,7 @@ class Project extends Base } } - $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); } /** @@ -301,7 +302,7 @@ class Project extends Base $this->session->flashError(t('Unable to remove this project.')); } - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } $this->response->html($this->projectLayout('project/remove', array( @@ -329,7 +330,7 @@ class Project extends Base $this->session->flashError(t('Unable to clone this project.')); } - $this->response->redirect('?controller=project'); + $this->response->redirect($this->helper->url->to('project', 'index')); } $this->response->html($this->projectLayout('project/duplicate', array( @@ -357,7 +358,7 @@ class Project extends Base $this->session->flashError(t('Unable to disable this project.')); } - $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/disable', array( @@ -385,7 +386,7 @@ class Project extends Base $this->session->flashError(t('Unable to activate this project.')); } - $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id']))); } $this->response->html($this->projectLayout('project/enable', array( @@ -428,7 +429,7 @@ class Project extends Base if ($project_id > 0) { $this->session->flash(t('Your project have been created successfully.')); - $this->response->redirect('?controller=project&action=show&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id))); } $this->session->flashError(t('Unable to create your project.')); diff --git a/app/Controller/Search.php b/app/Controller/Search.php index 519f9ce4..f6dc7a32 100644 --- a/app/Controller/Search.php +++ b/app/Controller/Search.php @@ -13,7 +13,7 @@ class Search extends Base public function index() { $projects = $this->projectPermission->getAllowedProjects($this->userSession->getId()); - $search = $this->request->getStringParam('search'); + $search = urldecode($this->request->getStringParam('search')); $nb_tasks = 0; $paginator = $this->paginator diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index 6ee94333..87f3fcb4 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -75,10 +75,10 @@ class Subtask extends Base } if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { - $this->response->redirect('?controller=subtask&action=create&task_id='.$task['id'].'&another_subtask=1&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('subtask', 'create', array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1))); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } $this->create($values, $errors); @@ -126,7 +126,7 @@ class Subtask extends Base $this->session->flashError(t('Unable to update your sub-task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } $this->edit($values, $errors); @@ -166,7 +166,7 @@ class Subtask extends Base $this->session->flashError(t('Unable to remove this sub-task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks')); } /** @@ -256,7 +256,7 @@ class Subtask extends Base case 'dashboard': $this->response->redirect($this->helper->url->to('app', 'index')); default: - $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'subtasks')); } } @@ -275,6 +275,6 @@ class Subtask extends Base $method = $direction === 'up' ? 'moveUp' : 'moveDown'; $this->subtask->$method($task_id, $subtask_id); - $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks'); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks')); } } diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php index c6862d47..054fa4ba 100644 --- a/app/Controller/Swimlane.php +++ b/app/Controller/Swimlane.php @@ -25,7 +25,7 @@ class Swimlane extends Base if (empty($swimlane)) { $this->session->flashError(t('Swimlane not found.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project_id); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project_id))); } return $swimlane; @@ -67,7 +67,7 @@ class Swimlane extends Base if ($this->swimlane->create($project['id'], $values['name'])) { $this->session->flash(t('Your swimlane have been created successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to create your swimlane.')); @@ -93,7 +93,7 @@ class Swimlane extends Base if ($this->swimlane->updateDefault($values)) { $this->session->flash(t('The default swimlane have been updated successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this swimlane.')); @@ -137,7 +137,7 @@ class Swimlane extends Base if ($this->swimlane->rename($values['id'], $values['name'])) { $this->session->flash(t('Swimlane updated successfully.')); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } else { $this->session->flashError(t('Unable to update this swimlane.')); @@ -181,7 +181,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to remove this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -201,7 +201,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to update this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -221,7 +221,7 @@ class Swimlane extends Base $this->session->flashError(t('Unable to update this swimlane.')); } - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -236,7 +236,7 @@ class Swimlane extends Base $swimlane_id = $this->request->getIntegerParam('swimlane_id'); $this->swimlane->moveUp($project['id'], $swimlane_id); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } /** @@ -251,6 +251,6 @@ class Swimlane extends Base $swimlane_id = $this->request->getIntegerParam('swimlane_id'); $this->swimlane->moveDown($project['id'], $swimlane_id); - $this->response->redirect('?controller=swimlane&action=index&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id']))); } } diff --git a/app/Controller/Task.php b/app/Controller/Task.php index 0d85f411..16bb408f 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -163,10 +163,10 @@ class Task extends Base if (isset($values['another_task']) && $values['another_task'] == 1) { unset($values['title']); unset($values['description']); - $this->response->redirect('?controller=task&action=create&'.http_build_query($values)); + $this->response->redirect($this->helper->url->to('task', 'create', $values)); } else { - $this->response->redirect('?controller=board&action=show&project_id='.$project['id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id']))); } } else { @@ -231,10 +231,10 @@ class Task extends Base $this->session->flash(t('Task updated successfully.')); if ($this->request->getIntegerParam('ajax')) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } } else { @@ -264,7 +264,7 @@ class Task extends Base $this->session->flashError(t('Unable to update your task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } /** @@ -326,7 +326,7 @@ class Task extends Base $this->session->flashError(t('Unable to open this task.')); } - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } $this->response->html($this->taskLayout('task/open', array( @@ -357,7 +357,7 @@ class Task extends Base $this->session->flashError(t('Unable to remove this task.')); } - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } $this->response->html($this->taskLayout('task/remove', array( @@ -381,10 +381,10 @@ class Task extends Base if ($task_id) { $this->session->flash(t('Task created successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task_id.'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } else { $this->session->flashError(t('Unable to create this task.')); - $this->response->redirect('?controller=task&action=duplicate&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } } @@ -419,10 +419,10 @@ class Task extends Base } if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } } } @@ -472,10 +472,10 @@ class Task extends Base } if ($ajax) { - $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id']))); } else { - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } } } @@ -526,7 +526,7 @@ class Task extends Base if ($this->taskDuplication->moveToProject($task['id'], $values['project_id'])) { $this->session->flash(t('Task updated successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } else { $this->session->flashError(t('Unable to update your task.')); @@ -565,7 +565,7 @@ class Task extends Base $task_id = $this->taskDuplication->duplicateToProject($task['id'], $values['project_id']); if ($task_id) { $this->session->flash(t('Task created successfully.')); - $this->response->redirect('?controller=task&action=show&task_id='.$task_id.'&project_id='.$values['project_id']); + $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } else { $this->session->flashError(t('Unable to create your task.')); diff --git a/app/Controller/User.php b/app/Controller/User.php index 119041e5..edebf5ba 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -178,7 +178,7 @@ class User extends Base $this->checkCSRFParam(); $user = $this->getUser(); $this->authentication->backend('rememberMe')->remove($this->request->getIntegerParam('id')); - $this->response->redirect('?controller=user&action=sessions&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'session', array('user_id' => $user['id']))); } /** @@ -194,7 +194,7 @@ class User extends Base $values = $this->request->getValues(); $this->notification->saveSettings($user['id'], $values); $this->session->flash(t('User updated successfully.')); - $this->response->redirect('?controller=user&action=notifications&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id']))); } $this->response->html($this->layout('user/notifications', array( @@ -272,7 +272,7 @@ class User extends Base $this->session->flashError(t('Unable to change the password.')); } - $this->response->redirect('?controller=user&action=show&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id']))); } } @@ -321,7 +321,7 @@ class User extends Base $this->session->flashError(t('Unable to update your user.')); } - $this->response->redirect('?controller=user&action=show&user_id='.$user['id']); + $this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id']))); } } @@ -353,7 +353,7 @@ class User extends Base $this->session->flashError(t('Unable to remove this user.')); } - $this->response->redirect('?controller=user'); + $this->response->redirect($this->helper->url->to('user', 'index')); } $this->response->html($this->layout('user/remove', array( @@ -386,10 +386,10 @@ class User extends Base $this->session->flashError(t('Unable to link your Google Account.')); } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); } else if ($this->authentication->backend('google')->authenticate($profile['id'])) { - $this->response->redirect('?controller=app'); + $this->response->redirect($this->helper->url->to('app', 'index')); } else { $this->response->html($this->template->layout('auth/index', array( @@ -421,7 +421,7 @@ class User extends Base $this->session->flashError(t('Unable to unlink your Google Account.')); } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); } /** @@ -448,10 +448,10 @@ class User extends Base $this->session->flashError(t('Unable to link your GitHub Account.')); } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); } else if ($this->authentication->backend('gitHub')->authenticate($profile['id'])) { - $this->response->redirect('?controller=app'); + $this->response->redirect($this->helper->url->to('app', 'index')); } else { $this->response->html($this->template->layout('auth/index', array( @@ -486,6 +486,6 @@ class User extends Base $this->session->flashError(t('Unable to unlink your GitHub Account.')); } - $this->response->redirect('?controller=user&action=external&user_id='.$this->userSession->getId()); + $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); } } diff --git a/app/Core/Router.php b/app/Core/Router.php index 36c11a0a..ae989de5 100644 --- a/app/Core/Router.php +++ b/app/Core/Router.php @@ -2,53 +2,151 @@ namespace Core; -use Pimple\Container; - /** * Router class * * @package core * @author Frederic Guillot */ -class Router +class Router extends Base { /** - * Controller name + * Store routes for path lookup * * @access private - * @var string + * @var array */ - private $controller = ''; + private $paths = array(); /** - * Action name + * Store routes for url lookup * * @access private - * @var string + * @var array */ - private $action = ''; + private $urls = array(); /** - * Container instance + * Get the path to compare patterns * - * @access private - * @var \Pimple\Container + * @access public + * @param string $uri + * @param string $query_string + * @return string */ - private $container; + public function getPath($uri, $query_string = '') + { + $path = substr($uri, strlen($this->helper->url->dir())); + + if (! empty($query_string)) { + $path = substr($path, 0, - strlen($query_string) - 1); + } + + if ($path{0} === '/') { + $path = substr($path, 1); + } + + return $path; + } + + /** + * Add route + * + * @access public + * @param string $path + * @param string $controller + * @param string $action + * @param array $params + */ + public function addRoute($path, $controller, $action, array $params = array()) + { + $pattern = explode('/', $path); + + $this->paths[] = array( + 'pattern' => $pattern, + 'count' => count($pattern), + 'controller' => $controller, + 'action' => $action, + ); + + $this->urls[$controller][$action][] = array( + 'path' => $path, + 'params' => array_flip($params), + 'count' => count($params), + ); + } + + /** + * Find a route according to the given path + * + * @access public + * @param string $path + * @return array + */ + public function findRoute($path) + { + $parts = explode('/', $path); + $count = count($parts); + + foreach ($this->paths as $route) { + + if ($count === $route['count']) { + + $params = array(); + + for ($i = 0; $i < $count; $i++) { + + if ($route['pattern'][$i]{0} === ':') { + $params[substr($route['pattern'][$i], 1)] = $parts[$i]; + } + else if ($route['pattern'][$i] !== $parts[$i]) { + break; + } + } + + if ($i === $count) { + $_GET = array_merge($_GET, $params); + return array($route['controller'], $route['action']); + } + } + } + + return array('app', 'index'); + } /** - * Constructor + * Find route url * * @access public - * @param \Pimple\Container $container Container instance - * @param string $controller Controller name - * @param string $action Action name + * @param string $controller + * @param string $action + * @param array $params + * @return string */ - public function __construct(Container $container, $controller = '', $action = '') + public function findUrl($controller, $action, array $params = array()) { - $this->container = $container; - $this->controller = empty($_GET['controller']) ? $controller : $_GET['controller']; - $this->action = empty($_GET['action']) ? $action : $_GET['action']; + if (! isset($this->urls[$controller][$action])) { + return ''; + } + + foreach ($this->urls[$controller][$action] as $pattern) { + + if (array_diff_key($params, $pattern['params']) === array()) { + $url = $pattern['path']; + $i = 0; + + foreach ($params as $variable => $value) { + $url = str_replace(':'.$variable, $value, $url); + $i++; + } + + if ($i === $pattern['count']) { + return $url; + } + } + } + + return ''; } /** @@ -65,15 +163,42 @@ class Router } /** - * Load a controller and execute the action + * Find controller/action from the route table or from get arguments * * @access public - * @param string $filename Controller filename - * @param string $class Class name - * @param string $method Method name + * @param string $uri + * @param string $query_string + * @return boolean + */ + public function dispatch($uri, $query_string = '') + { + if (! empty($_GET['controller']) && ! empty($_GET['action'])) { + $controller = $this->sanitize($_GET['controller'], 'app'); + $action = $this->sanitize($_GET['action'], 'index'); + } + else { + list($controller, $action) = $this->findRoute($this->getPath($uri, $query_string)); + } + + return $this->load( + __DIR__.'/../Controller/'.ucfirst($controller).'.php', + $controller, + '\Controller\\'.ucfirst($controller), + $action + ); + } + + /** + * Load a controller and execute the action + * + * @access private + * @param string $filename + * @param string $controller + * @param string $class + * @param string $method * @return bool */ - public function load($filename, $class, $method) + private function load($filename, $controller, $class, $method) { if (file_exists($filename)) { @@ -84,7 +209,7 @@ class Router } $instance = new $class($this->container); - $instance->beforeAction($this->controller, $this->action); + $instance->beforeAction($controller, $method); $instance->$method(); return true; @@ -92,20 +217,4 @@ class Router return false; } - - /** - * Find a route - * - * @access public - */ - public function execute() - { - $this->controller = $this->sanitize($this->controller, 'app'); - $this->action = $this->sanitize($this->action, 'index'); - $filename = __DIR__.'/../Controller/'.ucfirst($this->controller).'.php'; - - if (! $this->load($filename, '\Controller\\'.$this->controller, $this->action)) { - die('Page not found!'); - } - } } diff --git a/app/Core/Session.php b/app/Core/Session.php index c35014cd..0e5f7426 100644 --- a/app/Core/Session.php +++ b/app/Core/Session.php @@ -41,8 +41,6 @@ class Session implements ArrayAccess */ public function open($base_path = '/') { - $base_path = str_replace('\\', '/', $base_path); - // HttpOnly and secure flags for session cookie session_set_cookie_params( self::SESSION_LIFETIME, diff --git a/app/Helper/Asset.php b/app/Helper/Asset.php index 1b1e47c5..fd555e07 100644 --- a/app/Helper/Asset.php +++ b/app/Helper/Asset.php @@ -18,7 +18,7 @@ class Asset extends \Core\Base */ public function js($filename, $async = false) { - return ''; + return ''; } /** @@ -31,7 +31,7 @@ class Asset extends \Core\Base */ public function css($filename, $is_file = true, $media = 'screen') { - return ''; + return ''; } /** diff --git a/app/Helper/Url.php b/app/Helper/Url.php index 8de63f8d..964e0762 100644 --- a/app/Helper/Url.php +++ b/app/Helper/Url.php @@ -13,6 +13,9 @@ use Core\Security; */ class Url extends \Core\Base { + private $base = ''; + private $directory = ''; + /** * HTML Link tag * @@ -33,7 +36,7 @@ class Url extends \Core\Base } /** - * Hyperlink + * HTML Hyperlink * * @access public * @param string $controller Controller name @@ -41,22 +44,12 @@ class Url extends \Core\Base * @param array $params Url parameters * @param boolean $csrf Add a CSRF token * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link * @return string */ - public function href($controller, $action, array $params = array(), $csrf = false, $anchor = '') + public function href($controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) { - $values = array( - 'controller' => $controller, - 'action' => $action, - ); - - if ($csrf) { - $params['csrf_token'] = Security::getCSRFToken(); - } - - $values += $params; - - return '?'.http_build_query($values, '', '&').(empty($anchor) ? '' : '#'.$anchor); + return $this->build('&', $controller, $action, $params, $csrf, $anchor, $absolute); } /** @@ -66,18 +59,13 @@ class Url extends \Core\Base * @param string $controller Controller name * @param string $action Action name * @param array $params Url parameters + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link * @return string */ - public function to($controller, $action, array $params = array()) + public function to($controller, $action, array $params = array(), $anchor = '', $absolute = false) { - $values = array( - 'controller' => $controller, - 'action' => $action, - ); - - $values += $params; - - return '?'.http_build_query($values, '', '&'); + return $this->build('&', $controller, $action, $params, false, $anchor, $absolute); } /** @@ -88,7 +76,28 @@ class Url extends \Core\Base */ public function base() { - return $this->config->get('application_url') ?: $this->server(); + if (empty($this->base)) { + $this->base = $this->config->get('application_url') ?: $this->server(); + } + + return $this->base; + } + + /** + * Get application base directory + * + * @access public + * @return string + */ + public function dir() + { + if (empty($this->directory) && isset($_SERVER['REQUEST_METHOD'])) { + $this->directory = str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])); + $this->directory = $this->directory !== '/' ? $this->directory.'/' : '/'; + $this->directory = str_replace('//', '/', $this->directory); + } + + return $this->directory; } /** @@ -103,13 +112,46 @@ class Url extends \Core\Base return 'http://localhost/'; } - $self = str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])); - $url = Request::isHTTPS() ? 'https://' : 'http://'; $url .= $_SERVER['SERVER_NAME']; $url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 ? '' : ':'.$_SERVER['SERVER_PORT']; - $url .= $self !== '/' ? $self.'/' : '/'; + $url .= $this->dir() ?: '/'; return $url; } + + /** + * Build relative url + * + * @access private + * @param string $separator Querystring argument separator + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link + * @return string + */ + private function build($separator, $controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) + { + $path = $this->router->findUrl($controller, $action, $params); + $qs = array(); + + if (empty($path)) { + $qs['controller'] = $controller; + $qs['action'] = $action; + $qs += $params; + } + + if ($csrf) { + $qs['csrf_token'] = Security::getCSRFToken(); + } + + if (! empty($qs)) { + $path .= '?'.http_build_query($qs, '', $separator); + } + + return ($absolute ? $this->base() : $this->dir()).$path.(empty($anchor) ? '' : '#'.$anchor); + } } diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index 8c55457d..ef772f58 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -72,6 +72,7 @@ class ClassProvider implements ServiceProviderInterface 'Lexer', 'MemoryCache', 'Request', + 'Router', 'Session', 'Template', ), diff --git a/app/Template/app/overview.php b/app/Template/app/overview.php index fa7866af..1b160496 100644 --- a/app/Template/app/overview.php +++ b/app/Template/app/overview.php @@ -1,7 +1,7 @@