From e95b8d93d8c9e71cd4e865908933154cf9dbf43f Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Fri, 16 May 2014 21:38:33 +0200 Subject: [PATCH] Simplified view management --- lib/chibi-core | 2 +- public_html/dispatch.php | 94 +----- src/Controllers/AbstractController.php | 82 +++++ src/Controllers/AuthController.php | 54 +-- src/Controllers/CommentController.php | 30 +- src/Controllers/ErrorController.php | 30 ++ src/Controllers/LogController.php | 25 +- src/Controllers/PostController.php | 133 +++++--- src/Controllers/StaticPagesController.php | 8 +- src/Controllers/TagController.php | 86 +++-- src/Controllers/UserController.php | 388 ++++++++++++---------- src/Dispatcher.php | 77 +++++ src/Helpers/Assets.php | 38 +-- src/Helpers/SessionHelper.php | 42 ++- src/Messenger.php | 15 +- src/View.php | 22 ++ src/Views/auth-login.phtml | 6 +- src/Views/comment/comment-add.phtml | 4 +- src/Views/comment/comment-edit.phtml | 4 +- src/Views/comment/comment-list.phtml | 16 +- src/Views/comment/comment-small.phtml | 6 +- src/Views/debug.phtml | 5 +- src/Views/layout-normal.phtml | 18 +- src/Views/log/log-view.phtml | 8 +- src/Views/paginator.phtml | 4 +- src/Views/post/post-file-render.phtml | 2 +- src/Views/post/post-list-wrapper.phtml | 10 +- src/Views/post/post-list.phtml | 12 +- src/Views/post/post-small.phtml | 2 +- src/Views/post/post-upload.phtml | 10 +- src/Views/post/post-view.phtml | 24 +- src/Views/static/static-help.phtml | 4 +- src/Views/static/static-main.phtml | 6 +- src/Views/tag/tag-list-wrapper.phtml | 30 +- src/Views/tag/tag-list.phtml | 2 +- src/Views/tag/tag-merge.phtml | 2 +- src/Views/tag/tag-rename.phtml | 2 +- src/Views/top-navigation.phtml | 16 +- src/Views/user/user-delete.phtml | 2 +- src/Views/user/user-edit.phtml | 2 +- src/Views/user/user-list.phtml | 6 +- src/Views/user/user-registration.phtml | 8 +- src/Views/user/user-select.phtml | 6 +- src/Views/user/user-settings.phtml | 2 +- src/Views/user/user-view.phtml | 14 +- 45 files changed, 803 insertions(+), 556 deletions(-) create mode 100644 src/Controllers/AbstractController.php create mode 100644 src/Controllers/ErrorController.php create mode 100644 src/Dispatcher.php create mode 100644 src/View.php diff --git a/lib/chibi-core b/lib/chibi-core index 19bb5ee3..78597486 160000 --- a/lib/chibi-core +++ b/lib/chibi-core @@ -1 +1 @@ -Subproject commit 19bb5ee3901cf6402d7507c30b8fc183c3fc0bcc +Subproject commit 78597486abb1bd6344a64a70c87d9deca14b7ff7 diff --git a/public_html/dispatch.php b/public_html/dispatch.php index 9487a97d..7a8f9dd1 100644 --- a/public_html/dispatch.php +++ b/public_html/dispatch.php @@ -1,95 +1,5 @@ query = $query; - -function renderView() -{ - $context = Core::getContext(); - \Chibi\View::render($context->layoutName, $context); -} - -$context->simpleControllerName = null; -$context->simpleActionName = null; - -\Chibi\Router::setObserver(function($route, $args) -{ - $context = Core::getContext(); - $context->route = $route; - list ($className, $methodName) = $route->destination; - - $context->simpleControllerName = TextCaseConverter::convert( - str_replace('Controller', '', $className), - TextCaseConverter::CAMEL_CASE, - TextCaseConverter::SPINAL_CASE); - - $context->simpleActionName = TextCaseConverter::convert( - preg_replace('/Action|View/', '', $methodName), - TextCaseConverter::CAMEL_CASE, - TextCaseConverter::SPINAL_CASE); - - $context->viewName = sprintf( - '%s-%s', - $context->simpleControllerName, - $context->simpleActionName); -}); - -Assets::setTitle(Core::getConfig()->main->title); - -$context->handleExceptions = false; -$context->layoutName - = isset($_SERVER['HTTP_X_AJAX']) - ? 'layout-json' - : 'layout-normal'; -$context->viewName = ''; -$context->transport = new StdClass; - -SessionHelper::init(); -if (!Auth::isLoggedIn()) - Auth::tryAutoLogin(); - -register_shutdown_function(function() -{ - $error = error_get_last(); - if ($error !== null) - \Chibi\Util\Headers::setCode(400); -}); - -try -{ - try - { - \Chibi\Router::run($query); - renderView(); - AuthController::observeWorkFinish(); - } - catch (\Chibi\UnhandledRouteException $e) - { - throw new SimpleNotFoundException($query . ' not found.'); - } -} -catch (SimpleException $e) -{ - if ($e instanceof SimpleNotFoundException) - \Chibi\Util\Headers::setCode(404); - else - \Chibi\Util\Headers::setCode(400); - Messenger::message($e->getMessage(), false); - if (!$context->handleExceptions) - $context->viewName = 'message'; - renderView(); -} -catch (Exception $e) -{ - \Chibi\Util\Headers::setCode(400); - Messenger::message($e->getMessage(), false); - $context->transport->exception = $e; - $context->transport->queries = \Chibi\Database::getLogs(); - $context->viewName = 'error-exception'; - renderView(); -} +$dispatcher = new Dispatcher(); +$dispatcher->run(); diff --git a/src/Controllers/AbstractController.php b/src/Controllers/AbstractController.php new file mode 100644 index 00000000..8bfed787 --- /dev/null +++ b/src/Controllers/AbstractController.php @@ -0,0 +1,82 @@ +switchLayout('layout-normal'); + + $this->assets = new Assets(); + $this->assets->setTitle(Core::getConfig()->main->title); + } + + public function __destruct() + { + if ($this->isAjax()) + $this->renderAjax(); + } + + public function renderAjax() + { + $this->switchLayout('layout-json'); + $this->renderView(null); + } + + public function renderFile() + { + $this->switchLayout('layout-file'); + $this->renderView(null); + } + + public function renderView($viewName) + { + //no matter which controller runs it (including ErrorController), render only once + if (self::isRendered()) + return; + + self::markAsRendered(); + $context = Core::getContext(); + if ($viewName !== null) + $context->viewName = $viewName; + View::renderTopLevel($this->layoutName, $this->assets); + } + + + protected function redirectToLastVisitedUrl($filter = null) + { + $targetUrl = SessionHelper::getLastVisitedUrl($filter); + if (!$targetUrl) + $targetUrl = \Chibi\Router::linkTo(['StaticPagesController', 'mainPageView']); + $this->redirect($targetUrl); + } + + protected function redirect($url) + { + if (!$this->isAjax()) + \Chibi\Util\Url::forward($url); + } + + + private static function isRendered() + { + return self::$isRendered; + } + + private static function markAsRendered() + { + self::$isRendered = true; + } + + private function switchLayout($layoutName) + { + $this->layoutName = $layoutName; + } +} diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index 35385003..9f215d12 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -1,53 +1,35 @@ redirectToLastVisitedUrl('auth'); + else + $this->renderView('auth-login'); } public function loginAction() { - $context = Core::getContext(); - $context->viewName = 'auth-login'; - $context->handleExceptions = true; + try + { + $suppliedName = InputHelper::get('name'); + $suppliedPassword = InputHelper::get('password'); + $remember = boolval(InputHelper::get('remember')); + Auth::login($suppliedName, $suppliedPassword, $remember); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + $this->renderView('auth-login'); + } - $suppliedName = InputHelper::get('name'); - $suppliedPassword = InputHelper::get('password'); - $remember = boolval(InputHelper::get('remember')); - Auth::login($suppliedName, $suppliedPassword, $remember); - self::redirect(); + $this->redirectToLastVisitedUrl('auth'); } public function logoutAction() { Auth::logout(); - self::redirect(); - } - - public static function observeWorkFinish() - { - if (strpos(\Chibi\Util\Headers::get('Content-Type'), 'text/html') === false) - return; - if (\Chibi\Util\Headers::getCode() != 200) - return; - $context = Core::getContext(); - if ($context->simpleControllerName == 'auth') - return; - $_SESSION['login-redirect-url'] = $context->query; - } - - private static function redirect() - { - if (isset($_SESSION['login-redirect-url'])) - { - \Chibi\Util\Url::forward(\Chibi\Util\Url::makeAbsolute($_SESSION['login-redirect-url'])); - unset($_SESSION['login-redirect-url']); - exit; - } - \Chibi\Util\Url::forward(\Chibi\Router::linkTo(['StaticPagesController', 'mainPageView'])); - exit; + $this->redirectToLastVisitedUrl('auth'); } } diff --git a/src/Controllers/CommentController.php b/src/Controllers/CommentController.php index 829cb8e6..32f88f1f 100644 --- a/src/Controllers/CommentController.php +++ b/src/Controllers/CommentController.php @@ -1,5 +1,5 @@ transport->posts = $ret->entities; $context->transport->paginator = $ret; + $this->renderView('comment-list'); } public function addAction() @@ -26,6 +27,7 @@ class CommentController ]); Core::getContext()->transport->textPreview = $comment->getTextMarkdown(); + $this->renderAjax(); } else { @@ -35,13 +37,18 @@ class CommentController JobArgs::ARG_POST_ID => InputHelper::get('post-id'), JobArgs::ARG_NEW_TEXT => InputHelper::get('text') ]); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToLastVisitedUrl(); } - $this->redirectToLastVisitedUrl(); } public function editView($id) { Core::getContext()->transport->comment = CommentModel::getById($id); + $this->renderView('comment-edit'); } public function editAction($id) @@ -56,6 +63,7 @@ class CommentController ]); Core::getContext()->transport->textPreview = $comment->getTextMarkdown(); + $this->renderAjax(); } else { @@ -65,8 +73,12 @@ class CommentController JobArgs::ARG_COMMENT_ID => $id, JobArgs::ARG_NEW_TEXT => InputHelper::get('text') ]); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToLastVisitedUrl('comment/'); } - $this->redirectToLastVisitedUrl(); } public function deleteAction($id) @@ -76,14 +88,10 @@ class CommentController [ JobArgs::ARG_COMMENT_ID => $id, ]); - $this->redirectToLastVisitedUrl(); - } - private function redirectToLastVisitedUrl() - { - $lastUrl = SessionHelper::getLastVisitedUrl(); - if ($lastUrl) - \Chibi\Util\Url::forward($lastUrl); - exit; + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToLastVisitedUrl('comment/'); } } diff --git a/src/Controllers/ErrorController.php b/src/Controllers/ErrorController.php new file mode 100644 index 00000000..144f9331 --- /dev/null +++ b/src/Controllers/ErrorController.php @@ -0,0 +1,30 @@ +getMessage()); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->renderView('message'); + } + + public function seriousExceptionView(Exception $exception) + { + \Chibi\Util\Headers::setCode(400); + Messenger::fail($exception->getMessage()); + $context->transport->exception = $exception; + $context->transport->queries = \Chibi\Database::getLogs(); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->renderView('error-exception'); + } +} diff --git a/src/Controllers/LogController.php b/src/Controllers/LogController.php index df93ca20..d12b2ce9 100644 --- a/src/Controllers/LogController.php +++ b/src/Controllers/LogController.php @@ -1,30 +1,27 @@ transport->logs = $ret; + $this->renderView('log-list'); } public function logView($name, $page = 1, $filter = '') { - $context = Core::getContext(); - $context->viewName = 'log-view'; - //redirect requests in form of ?query=... to canonical address $formQuery = InputHelper::get('query'); if ($formQuery !== null) { - \Chibi\Util\Url::forward( - \Chibi\Router::linkTo( - ['LogController', 'logView'], - [ - 'name' => $name, - 'filter' => $formQuery, - 'page' => 1 - ])); - exit; + $this->redirect(\Chibi\Router::linkTo( + ['LogController', 'logView'], + [ + 'name' => $name, + 'filter' => $formQuery, + 'page' => 1 + ])); + return; } $ret = Api::run( @@ -46,9 +43,11 @@ class LogController $lines = TextHelper::parseMarkdown($lines, true); $lines = trim($lines); + $context = Core::getContext(); $context->transport->paginator = $ret; $context->transport->lines = $lines; $context->transport->filter = $filter; $context->transport->name = $name; + $this->renderView('log-view'); } } diff --git a/src/Controllers/PostController.php b/src/Controllers/PostController.php index 99dbf96f..70db4d14 100644 --- a/src/Controllers/PostController.php +++ b/src/Controllers/PostController.php @@ -1,68 +1,75 @@ viewName = 'post-list-wrapper'; $context->source = $source; $context->additionalInfo = $additionalInfo; - $context->handleExceptions = true; - //redirect requests in form of /posts/?query=... to canonical address - $formQuery = InputHelper::get('query'); - if ($formQuery !== null) + try { - $context->transport->searchQuery = $formQuery; - $context->transport->lastSearchQuery = $formQuery; - if (strpos($formQuery, '/') !== false) - throw new SimpleException('Search query contains invalid characters'); + //redirect requests in form of /posts/?query=... to canonical address + $formQuery = InputHelper::get('query'); + if ($formQuery !== null) + { + $context->transport->searchQuery = $formQuery; + $context->transport->lastSearchQuery = $formQuery; + if (strpos($formQuery, '/') !== false) + throw new SimpleException('Search query contains invalid characters'); - $url = \Chibi\Router::linkTo(['PostController', 'listView'], [ - 'source' => $source, - 'additionalInfo' => $additionalInfo, - 'query' => $formQuery]); - \Chibi\Util\Url::forward($url); - exit; + $url = \Chibi\Router::linkTo(['PostController', 'listView'], [ + 'source' => $source, + 'additionalInfo' => $additionalInfo, + 'query' => $formQuery]); + $this->redirect($url); + return; + } + + $query = trim($query); + $context->transport->searchQuery = $query; + $context->transport->lastSearchQuery = $query; + if ($source == 'mass-tag') + { + Access::assert(new Privilege(Privilege::MassTag)); + $context->massTagTag = $additionalInfo; + $context->massTagQuery = $query; + + if (!Access::check(new Privilege(Privilege::MassTag, 'all'))) + $query = trim($query . ' submit:' . Auth::getCurrentUser()->getName()); + } + + $ret = Api::run( + new ListPostsJob(), + [ + JobArgs::ARG_PAGE_NUMBER => $page, + JobArgs::ARG_QUERY => $query + ]); + + $context->transport->posts = $ret->entities; + $context->transport->paginator = $ret; + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); } - $query = trim($query); - $context->transport->searchQuery = $query; - $context->transport->lastSearchQuery = $query; - if ($source == 'mass-tag') - { - Access::assert(new Privilege(Privilege::MassTag)); - $context->massTagTag = $additionalInfo; - $context->massTagQuery = $query; - - if (!Access::check(new Privilege(Privilege::MassTag, 'all'))) - $query = trim($query . ' submit:' . Auth::getCurrentUser()->getName()); - } - - $ret = Api::run( - new ListPostsJob(), - [ - JobArgs::ARG_PAGE_NUMBER => $page, - JobArgs::ARG_QUERY => $query - ]); - - $context->transport->posts = $ret->entities; - $context->transport->paginator = $ret; + $this->renderView('post-list-wrapper'); } public function favoritesView($page = 1) { - $this->listView('favmin:1', $page); + $this->listView('favmin:1', $page, 'favorites'); } public function upvotedView($page = 1) { - $this->listView('scoremin:1', $page); + $this->listView('scoremin:1', $page, 'upvoted'); } public function randomView($page = 1) { - $this->listView('order:random', $page); + $this->listView('order:random', $page, 'random'); } public function toggleTagAction($id, $tag, $enable) @@ -79,11 +86,15 @@ class PostController JobArgs::ARG_NEW_STATE => $enable, ]); - $this->redirectToLastVisitedUrl(); + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToLastVisitedUrl(); } public function uploadView() { + $this->renderView('post-upload'); } public function uploadAction() @@ -111,6 +122,11 @@ class PostController } Api::run(new AddPostJob(), $jobArgs); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToPostList(); } public function editView($id) @@ -119,6 +135,7 @@ class PostController JobArgs::ARG_POST_ID => $id]); $context = Core::getContext()->transport->post = $post; + $this->renderView('post-edit'); } public function editAction($id) @@ -163,13 +180,21 @@ class PostController } Api::run(new EditPostJob(), $jobArgs); - $this->redirectToGenericView($id); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToGenericView($id); } public function flagAction($id) { Api::run(new FlagPostJob(), [JobArgs::ARG_POST_ID => $id]); - $this->redirectToGenericView($id); + + if ($this->isAjax()) + $this->renderAjax(); + else + $this->redirectToGenericView($id); } public function hideAction($id) @@ -192,7 +217,7 @@ class PostController { Api::run(new DeletePostJob(), [ JobArgs::ARG_POST_ID => $id]); - $this->redirectToGenericView($id); + $this->redirectToPostList(); } public function addFavoriteAction($id) @@ -229,7 +254,6 @@ class PostController public function genericView($id) { $context = Core::getContext(); - $context->viewName = 'post-view'; $post = Api::run(new GetPostJob(), [ JobArgs::ARG_POST_ID => $id]); @@ -262,6 +286,8 @@ class PostController $context->transport->post = $post; $context->transport->prevPostId = $prevPostId ? $prevPostId : null; $context->transport->nextPostId = $nextPostId ? $nextPostId : null; + + $this->renderView('post-view'); } public function fileView($name) @@ -275,7 +301,7 @@ class PostController $context->transport->fileHash = 'post' . md5(substr($ret->fileContent, 0, 4096)); $context->transport->fileContent = $ret->fileContent; $context->transport->lastModified = $ret->lastModified; - $context->layoutName = 'layout-file'; + $this->renderFile(); } public function thumbView($name) @@ -289,9 +315,10 @@ class PostController $context->transport->fileHash = 'thumb' . md5(substr($ret->fileContent, 0, 4096)); $context->transport->fileContent = $ret->fileContent; $context->transport->lastModified = $ret->lastModified; - $context->layoutName = 'layout-file'; + $this->renderFile(); } + private function splitPostIds($string) { $ids = preg_split('/\D/', trim($string)); @@ -309,19 +336,15 @@ class PostController return $tags; } - private function redirectToLastVisitedUrl() + private function redirectToPostList() { - $lastUrl = SessionHelper::getLastVisitedUrl(); - if ($lastUrl) - \Chibi\Util\Url::forward($lastUrl); - exit; + $this->redirect(\Chibi\Router::linkTo(['PostController', 'listView'])); } private function redirectToGenericView($id) { - \Chibi\Util\Url::forward(\Chibi\Router::linkTo( + $this->redirect(\Chibi\Router::linkTo( ['PostController', 'genericView'], ['id' => $id])); - exit; } } diff --git a/src/Controllers/StaticPagesController.php b/src/Controllers/StaticPagesController.php index 990704e0..c50e6f92 100644 --- a/src/Controllers/StaticPagesController.php +++ b/src/Controllers/StaticPagesController.php @@ -1,11 +1,10 @@ transport->postCount = PostModel::getCount(); - $context->viewName = 'static-main'; $context->transport->postSpaceUsage = PostModel::getSpaceUsage(); PostModel::featureRandomPostIfNecessary(); @@ -17,6 +16,8 @@ class StaticPagesController $context->featuredPostUser = UserModel::tryGetByName( PropertyModel::get(PropertyModel::FeaturedPostUserName)); } + + $this->renderView('static-main'); } public function helpView($tab = null) @@ -31,9 +32,10 @@ class StaticPagesController if (!isset($config->help->paths[$tab])) throw new SimpleException('Invalid tab'); - $context->viewName = 'static-help'; $context->path = TextHelper::absolutePath($config->help->paths[$tab]); $context->tab = $tab; + + $this->renderView('static-help'); } public function fatalErrorView($code = null) diff --git a/src/Controllers/TagController.php b/src/Controllers/TagController.php index 531f3acf..4e278fec 100644 --- a/src/Controllers/TagController.php +++ b/src/Controllers/TagController.php @@ -1,5 +1,5 @@ viewName = 'tag-list-wrapper'; $context->highestUsage = TagSearchService::getMostUsedTag()->getPostCount(); $context->filter = $filter; $context->transport->tags = $ret->entities; $context->transport->paginator = $ret; + $this->renderViewWithSource('tag-list-wrapper', 'list'); } public function autoCompleteView() @@ -42,6 +42,8 @@ class TagController 'count' => $tag->getPostCount(), ]; }, $ret->entities)); + + $this->renderAjax(); } public function relatedView() @@ -67,76 +69,94 @@ class TagController 'count' => $tag->getPostCount(), ]; }, $ret->entities)); + + $this->renderAjax(); } public function mergeView() { - $context = Core::getContext(); - $context->viewName = 'tag-list-wrapper'; + $this->renderViewWithSource('tag-list-wrapper', 'merge'); } public function mergeAction() { - $context = Core::getContext(); - $context->viewName = 'tag-list-wrapper'; - $context->handleExceptions = true; + try + { + Api::run( + new MergeTagsJob(), + [ + JobArgs::ARG_SOURCE_TAG_NAME => InputHelper::get('source-tag'), + JobArgs::ARG_TARGET_TAG_NAME => InputHelper::get('target-tag'), + ]); - Api::run( - new MergeTagsJob(), - [ - JobArgs::ARG_SOURCE_TAG_NAME => InputHelper::get('source-tag'), - JobArgs::ARG_TARGET_TAG_NAME => InputHelper::get('target-tag'), - ]); + Messenger::success('Tags merged successfully.'); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + } - Messenger::message('Tags merged successfully.'); + $this->renderViewWithSource('tag-list-wrapper', 'merge'); } public function renameView() { - $context = Core::getContext(); - $context->viewName = 'tag-list-wrapper'; + $this->renderViewWithSource('tag-list-wrapper', 'rename'); } public function renameAction() { - $context = Core::getContext(); - $context->viewName = 'tag-list-wrapper'; - $context->handleExceptions = true; + try + { + Api::run( + new RenameTagsJob(), + [ + JobArgs::ARG_SOURCE_TAG_NAME => InputHelper::get('source-tag'), + JobArgs::ARG_TARGET_TAG_NAME => InputHelper::get('target-tag'), + ]); - Api::run( - new RenameTagsJob(), - [ - JobArgs::ARG_SOURCE_TAG_NAME => InputHelper::get('source-tag'), - JobArgs::ARG_TARGET_TAG_NAME => InputHelper::get('target-tag'), - ]); + Messenger::success('Tag renamed successfully.'); + } + catch (Exception $e) + { + Messenger::fail($e->getMessage()); + } - Messenger::message('Tag renamed successfully.'); + $this->renderViewWithSource('tag-list-wrapper', 'rename'); } public function massTagRedirectView() { - $context = Core::getContext(); - $context->viewName = 'tag-list-wrapper'; - Access::assert(new Privilege(Privilege::MassTag)); + $this->renderViewWithSource('tag-list-wrapper', 'mass-tag'); } public function massTagRedirectAction() { - $this->massTagRedirectView(); + Access::assert(new Privilege(Privilege::MassTag)); $suppliedOldPage = intval(InputHelper::get('old-page')); $suppliedOldQuery = InputHelper::get('old-query'); $suppliedQuery = InputHelper::get('query'); $suppliedTag = InputHelper::get('tag'); - $params = [ + $params = + [ 'source' => 'mass-tag', 'query' => $suppliedQuery ?: ' ', 'additionalInfo' => $suppliedTag ? $suppliedTag : '', ]; + if ($suppliedOldPage != 0 and $suppliedOldQuery == $suppliedQuery) $params['page'] = $suppliedOldPage; - \Chibi\Util\Url::forward(\Chibi\Router::linkTo(['PostController', 'listView'], $params)); - exit; + + $this->redirect(\Chibi\Router::linkTo(['PostController', 'listView'], $params)); + } + + + private function renderViewWithSource($viewName, $source) + { + $context = Core::getContext(); + $context->source = $source; + $this->renderView($viewName); } } diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index 7c43adcc..943eb4bd 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -1,5 +1,5 @@ filter = $filter; $context->transport->users = $ret->entities; $context->transport->paginator = $ret; + $this->renderView('user-list'); } public function genericView($identifier, $tab = 'favs', $page = 1) { - $user = Api::run( - new GetUserJob(), - $this->appendUserIdentifierArgument([], $identifier)); - - $flagged = in_array(TextHelper::reprUser($user), SessionHelper::get('flagged', [])); - - if ($tab == 'uploads') - $query = 'submit:' . $user->getName(); - elseif ($tab == 'favs') - $query = 'fav:' . $user->getName(); - - elseif ($tab == 'delete') - { - Access::assert(new Privilege( - Privilege::DeleteUser, - Access::getIdentity($user))); - } - elseif ($tab == 'settings') - { - Access::assert(new Privilege( - Privilege::ChangeUserSettings, - Access::getIdentity($user))); - } - elseif ($tab == 'edit' and !(new EditUserJob)->canEditAnything(Auth::getCurrentUser())) - Access::fail(); - - $context = Core::getContext(); - $context->flagged = $flagged; - $context->transport->tab = $tab; - $context->transport->user = $user; - $context->handleExceptions = true; - $context->viewName = 'user-view'; - - if (isset($query)) - { - $ret = Api::run( - new ListPostsJob(), - [ - JobArgs::ARG_PAGE_NUMBER => $page, - JobArgs::ARG_QUERY => $query - ]); - - $context->transport->posts = $ret->entities; - $context->transport->paginator = $ret; - $context->transport->lastSearchQuery = $query; - } + $this->prepareGenericView($identifier, $tab, $page); + $this->renderView('user-view'); } public function settingsAction($identifier) { - $this->genericView($identifier, 'settings'); + $this->prepareGenericView($identifier, 'settings'); - $suppliedSafety = InputHelper::get('safety'); - $desiredSafety = PostSafety::makeFlags($suppliedSafety); + try + { + $suppliedSafety = InputHelper::get('safety'); + $desiredSafety = PostSafety::makeFlags($suppliedSafety); - $user = Api::run( - new EditUserSettingsJob(), - $this->appendUserIdentifierArgument( - [ - JobArgs::ARG_NEW_SETTINGS => + $user = Api::run( + new EditUserSettingsJob(), + $this->appendUserIdentifierArgument( [ - UserSettings::SETTING_SAFETY => $desiredSafety, - UserSettings::SETTING_ENDLESS_SCROLLING => InputHelper::get('endless-scrolling'), - UserSettings::SETTING_POST_TAG_TITLES => InputHelper::get('post-tag-titles'), - UserSettings::SETTING_HIDE_DISLIKED_POSTS => InputHelper::get('hide-disliked-posts'), - ] - ], $identifier)); + JobArgs::ARG_NEW_SETTINGS => + [ + UserSettings::SETTING_SAFETY => $desiredSafety, + UserSettings::SETTING_ENDLESS_SCROLLING => InputHelper::get('endless-scrolling'), + UserSettings::SETTING_POST_TAG_TITLES => InputHelper::get('post-tag-titles'), + UserSettings::SETTING_HIDE_DISLIKED_POSTS => InputHelper::get('hide-disliked-posts'), + ] + ], $identifier)); - Core::getContext()->transport->user = $user; - if ($user->getId() == Auth::getCurrentUser()->getId()) - Auth::setCurrentUser($user); + Core::getContext()->transport->user = $user; + if ($user->getId() == Auth::getCurrentUser()->getId()) + Auth::setCurrentUser($user); - Messenger::message('Browsing settings updated!'); + Messenger::success('Browsing settings updated!'); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + } + + $this->renderView('user-view'); } public function editAction($identifier) { - $this->genericView($identifier, 'edit'); - $this->requirePasswordConfirmation(); + $this->prepareGenericView($identifier, 'edit'); - if (InputHelper::get('password1') != InputHelper::get('password2')) - throw new SimpleException('Specified passwords must be the same'); + try + { + $this->requirePasswordConfirmation(); - $args = - [ - JobArgs::ARG_NEW_USER_NAME => InputHelper::get('name'), - JobArgs::ARG_NEW_PASSWORD => InputHelper::get('password1'), - JobArgs::ARG_NEW_EMAIL => InputHelper::get('email'), - JobArgs::ARG_NEW_ACCESS_RANK => InputHelper::get('access-rank'), - ]; - $args = $this->appendUserIdentifierArgument($args, $identifier); + if (InputHelper::get('password1') != InputHelper::get('password2')) + throw new SimpleException('Specified passwords must be the same'); - $args = array_filter($args); - $user = Api::run(new EditUserJob(), $args); + $args = + [ + JobArgs::ARG_NEW_USER_NAME => InputHelper::get('name'), + JobArgs::ARG_NEW_PASSWORD => InputHelper::get('password1'), + JobArgs::ARG_NEW_EMAIL => InputHelper::get('email'), + JobArgs::ARG_NEW_ACCESS_RANK => InputHelper::get('access-rank'), + ]; + $args = $this->appendUserIdentifierArgument($args, $identifier); - if (Auth::getCurrentUser()->getId() == $user->getId()) - Auth::setCurrentUser($user); + $args = array_filter($args); + $user = Api::run(new EditUserJob(), $args); - $message = 'Account settings updated!'; - if (Mailer::getMailCounter() > 0) - $message .= ' You will be sent an e-mail address confirmation message soon.'; + if (Auth::getCurrentUser()->getId() == $user->getId()) + Auth::setCurrentUser($user); - Messenger::message($message); + $message = 'Account settings updated!'; + if (Mailer::getMailCounter() > 0) + $message .= ' You will be sent an e-mail address confirmation message soon.'; + + Messenger::success($message); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + } + + $this->renderView('user-view'); + } + + public function deleteAction($identifier) + { + $this->prepareGenericView($identifier, 'delete'); + + try + { + $this->requirePasswordConfirmation(); + + Api::run( + new DeleteUserJob(), + $this->appendUserIdentifierArgument([], $identifier)); + + $user = UserModel::tryGetById(Auth::getCurrentUser()->getId()); + if (!$user) + Auth::logOut(); + + $this->redirectToMainPage(); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + $this->renderView('user-view'); + } } public function toggleSafetyAction($safety) @@ -144,23 +144,6 @@ class UserController $this->redirectToLastVisitedUrl(); } - public function deleteAction($identifier) - { - $this->genericView($identifier, 'delete'); - $this->requirePasswordConfirmation(); - - Api::run( - new DeleteUserJob(), - $this->appendUserIdentifierArgument([], $identifier)); - - $user = UserModel::tryGetById(Auth::getCurrentUser()->getId()); - if (!$user) - Auth::logOut(); - - \Chibi\Util\Url::forward(\Chibi\Router::linkTo(['StaticPagesController', 'mainPageView'])); - exit; - } - public function flagAction($identifier) { Api::run( @@ -199,70 +182,70 @@ class UserController public function registrationView() { - $context = Core::getContext(); - $context->handleExceptions = true; - - //check if already logged in if (Auth::isLoggedIn()) - { - \Chibi\Util\Url::forward(\Chibi\Router::linkTo(['StaticPagesController', 'mainPageView'])); - exit; - } + $this->redirectToMainPage(); + $this->renderView('user-registration'); } public function registrationAction() { - $this->registrationView(); - - if (InputHelper::get('password1') != InputHelper::get('password2')) - throw new SimpleException('Specified passwords must be the same'); - - $user = Api::run(new AddUserJob(), - [ - JobArgs::ARG_NEW_USER_NAME => InputHelper::get('name'), - JobArgs::ARG_NEW_PASSWORD => InputHelper::get('password1'), - JobArgs::ARG_NEW_EMAIL => InputHelper::get('email'), - ]); - - if (!Core::getConfig()->registration->needEmailForRegistering and !Core::getConfig()->registration->staffActivation) + try { - Auth::setCurrentUser($user); + if (InputHelper::get('password1') != InputHelper::get('password2')) + throw new SimpleException('Specified passwords must be the same'); + + $user = Api::run(new AddUserJob(), + [ + JobArgs::ARG_NEW_USER_NAME => InputHelper::get('name'), + JobArgs::ARG_NEW_PASSWORD => InputHelper::get('password1'), + JobArgs::ARG_NEW_EMAIL => InputHelper::get('email'), + ]); + + if (!$this->isAnyAccountActivationNeeded()) + { + Auth::setCurrentUser($user); + } + + $message = 'Congratulations, your account was created.'; + if (Mailer::getMailCounter() > 0) + { + $message .= ' Please wait for activation e-mail.'; + if (Core::getConfig()->registration->staffActivation) + $message .= ' After this, your registration must be confirmed by staff.'; + } + elseif (Core::getConfig()->registration->staffActivation) + $message .= ' Your registration must be now confirmed by staff.'; + + Messenger::success($message); + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); } - $message = 'Congratulations, your account was created.'; - if (Mailer::getMailCounter() > 0) - { - $message .= ' Please wait for activation e-mail.'; - if (Core::getConfig()->registration->staffActivation) - $message .= ' After this, your registration must be confirmed by staff.'; - } - elseif (Core::getConfig()->registration->staffActivation) - $message .= ' Your registration must be now confirmed by staff.'; - - Messenger::message($message); + $this->renderView('user-registration'); } public function activationView() { - $context = Core::getContext(); - $context->viewName = 'user-select'; - Assets::setSubTitle('account activation'); + $this->assets->setSubTitle('account activation'); + $this->renderView('user-select'); } public function activationAction($tokenText) { - $context = Core::getContext(); - $context->viewName = 'message'; - Assets::setSubTitle('account activation'); + $this->assets->setSubTitle('account activation'); $identifier = InputHelper::get('identifier'); + try + { if (empty($tokenText)) { Api::run( new ActivateUserEmailJob(), $this->appendUserIdentifierArgument([], $identifier)); - Messenger::message('Activation e-mail resent.'); + Messenger::success('Activation e-mail resent.'); } else { @@ -272,45 +255,115 @@ class UserController $message = 'Activation completed successfully.'; if (Core::getConfig()->registration->staffActivation) $message .= ' However, your account still must be confirmed by staff.'; - Messenger::message($message); + Messenger::success($message); if (!Core::getConfig()->registration->staffActivation) Auth::setCurrentUser($user); } + } + catch (SimpleException $e) + { + Messenger::fail($e->getMessage()); + } + + $this->renderView('message'); } public function passwordResetView() { - $context = Core::getContext(); - $context->viewName = 'user-select'; - Assets::setSubTitle('password reset'); + $this->assets->setSubTitle('password reset'); + $this->renderView('user-select'); } public function passwordResetAction($tokenText) { - $context = Core::getContext(); - $context->viewName = 'message'; - Assets::setSubTitle('password reset'); + $this->assets->setSubTitle('password reset'); $identifier = InputHelper::get('identifier'); - if (empty($tokenText)) + try { - Api::run( - new PasswordResetJob(), - $this->appendUserIdentifierArgument([], $identifier)); + if (empty($tokenText)) + { + Api::run( + new PasswordResetJob(), + $this->appendUserIdentifierArgument([], $identifier)); - Messenger::message('E-mail sent. Follow instructions to reset password.'); + Messenger::success('E-mail sent. Follow instructions to reset password.'); + } + else + { + $ret = Api::run(new PasswordResetJob(), [ JobArgs::ARG_TOKEN => $tokenText ]); + + Messenger::success(sprintf( + 'Password reset successful. Your new password is **%s**.', + $ret->newPassword)); + + Auth::setCurrentUser($ret->user); + } } - else + catch (SimpleException $e) { - $ret = Api::run(new PasswordResetJob(), [ JobArgs::ARG_TOKEN => $tokenText ]); - - Messenger::message(sprintf( - 'Password reset successful. Your new password is **%s**.', - $ret->newPassword)); - - Auth::setCurrentUser($ret->user); + Messenger::fail($e->getMessage()); } + + $this->renderView('message'); + } + + + private function prepareGenericView($identifier, $tab, $page = 1) + { + $user = Api::run( + new GetUserJob(), + $this->appendUserIdentifierArgument([], $identifier)); + + $flagged = in_array(TextHelper::reprUser($user), SessionHelper::get('flagged', [])); + + if ($tab == 'uploads') + $query = 'submit:' . $user->getName(); + elseif ($tab == 'favs') + $query = 'fav:' . $user->getName(); + + elseif ($tab == 'delete') + { + Access::assert(new Privilege( + Privilege::DeleteUser, + Access::getIdentity($user))); + } + elseif ($tab == 'settings') + { + Access::assert(new Privilege( + Privilege::ChangeUserSettings, + Access::getIdentity($user))); + } + elseif ($tab == 'edit' and !(new EditUserJob)->canEditAnything(Auth::getCurrentUser())) + Access::fail(); + + $context = Core::getContext(); + $context->flagged = $flagged; + $context->transport->tab = $tab; + $context->transport->user = $user; + + if (isset($query)) + { + $ret = Api::run( + new ListPostsJob(), + [ + JobArgs::ARG_PAGE_NUMBER => $page, + JobArgs::ARG_QUERY => $query + ]); + + $context->transport->posts = $ret->entities; + $context->transport->paginator = $ret; + $context->transport->lastSearchQuery = $query; + } + } + + + private function isAnyAccountActivationNeeded() + { + $config = Core::getConfig(); + return ($config->registration->needEmailForRegistering + or $config->registration->staffActivation); } private function requirePasswordConfirmation() @@ -334,19 +387,16 @@ class UserController return $arguments; } - private function redirectToLastVisitedUrl() + private function redirectToMainPage() { - $lastUrl = SessionHelper::getLastVisitedUrl(); - if ($lastUrl) - \Chibi\Util\Url::forward($lastUrl); + $this->redirect(\Chibi\Router::linkTo(['StaticPagesController', 'mainPageView'])); exit; } private function redirectToGenericView($identifier) { - \Chibi\Util\Url::forward(\Chibi\Router::linkTo( + $this->redirect(\Chibi\Router::linkTo( ['UserController', 'genericView'], ['identifier' => $identifier])); - exit; } } diff --git a/src/Dispatcher.php b/src/Dispatcher.php new file mode 100644 index 00000000..f557a2d9 --- /dev/null +++ b/src/Dispatcher.php @@ -0,0 +1,77 @@ +retrieveQuery(); + + $context = Core::getContext(); + $context->query = $query; + $context->transport = new StdClass; + + $this->setRouterObserver(); + $this->ensureResponseCodeUponFail(); + + SessionHelper::init(); + if (!Auth::isLoggedIn()) + Auth::tryAutoLogin(); + + $this->routeAndHandleErrors($query); + } + + private function routeAndHandleErrors($query) + { + try + { + \Chibi\Router::run($query); + } + catch (\Chibi\UnhandledRouteException $e) + { + $errorController = new ErrorController; + $errorController->simpleExceptionView(new SimpleNotFoundException($query . ' not found.')); + } + catch (SimpleException $e) + { + $errorController = new ErrorController; + $errorController->simpleExceptionView($e); + } + catch (SimpleException $e) + { + $errorController = new ErrorController; + $errorController->seriousExceptionView($e); + } + } + + private function ensureResponseCodeUponFail() + { + register_shutdown_function(function() + { + $error = error_get_last(); + if ($error !== null) + \Chibi\Util\Headers::setCode(400); + }); + } + + private function retrieveQuery() + { + if (isset($_SERVER['REDIRECT_URL'])) + return $this->parseRawHttpQuery($_SERVER['REDIRECT_URL']); + else + return $this->parseRawHttpQuery($_SERVER['REQUEST_URI']); + } + + private function parseRawHttpQuery($rawHttpQuery) + { + return rtrim($rawHttpQuery, '/'); + } + + private function setRouterObserver() + { + \Chibi\Router::setObserver(function($route, $args) + { + $context = Core::getContext(); + $context->route = $route; + }); + } +} + diff --git a/src/Helpers/Assets.php b/src/Helpers/Assets.php index 50d555ce..678de702 100644 --- a/src/Helpers/Assets.php +++ b/src/Helpers/Assets.php @@ -1,47 +1,41 @@ subTitle = $text; } - public static function setSubTitle($text) + public function setPageThumb($path) { - self::$subTitle = $text; + $this->pageThumb = $path; } - public static function setPageThumb($path) - { - self::$pageThumb = $path; - } - - public static function addStylesheet($path) + public function addStylesheet($path) { return parent::addStylesheet('/media/css/' . $path); } - public static function addScript($path) + public function addScript($path) { return parent::addScript('/media/js/' . $path); } - public static function transformHtml($html) + public function transformHtml($html) { - self::$title = isset(self::$subTitle) - ? sprintf('%s – %s', self::$title, self::$subTitle) - : self::$title; + $this->title = isset($this->subTitle) + ? sprintf('%s – %s', $this->title, $this->subTitle) + : $this->title; $html = parent::transformHtml($html); - $headSnippet = ''; + $headSnippet = ''; $headSnippet .= ''; - if (!empty(self::$pageThumb)) - $headSnippet .= ''; + if (!empty($this->pageThumb)) + $headSnippet .= ''; $bodySnippet = '