From 902aed727882eb4418af094a2935d9b63e96151d Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 1 May 2014 16:25:10 +0200 Subject: [PATCH] Introducing API Right now there's a lot of messy code in controllers. Furthermore, there is no way to interact with szurubooru via vanilla HTTP, since API is next to non-existent. So, basing upon my experiences from another project, I plan to: - Create actual API. It is going to consist of well-defined "jobs" that do things currently done by controllers. Benefits of such approach are as follows: - defining them in their own classes allows to clean up code a lot, - it allows to abstract from input method (POST data, part of URL, whatever), and leave processing of these to controllers, - it allows to make proxy controller, whose purpose would be to let users interact with API (jobs) directly in well-documented and consistent way. - Make controllers responsible only for mediating between views and API. Behavior of these may remain inconsistent, since views they're talking to are also messy to begin with. Such controllers might be removed altogether in the future in favor of making views talk to API directly through previously mentioned ApiController. - Organize all sorts of privilege checking and possibly other stuff into methods within jobs. - Actually distinguish POST from GET requests. - Leave POST-only controller methods as Actions, but rename GET-only methods to Views. Example: editAction for editing comments, but listView for showing comment list. The choice of these suffixes might be subject to changes in future. - Get rid of ?json and $context->transport. They now look like disease to me. This commit introduces job system and converts CommentController to use the new API. --- public_html/dispatch.php | 14 +++-- src/Api.php | 33 +++++++++++ src/Auth.php | 1 + src/Controllers/CommentController.php | 80 +++++++++++---------------- src/Helpers/LogHelper.php | 16 ++++-- src/Jobs/AbstractJob.php | 9 +++ src/Jobs/AddCommentJob.php | 38 +++++++++++++ src/Jobs/EditCommentJob.php | 35 ++++++++++++ src/Jobs/PreviewCommentJob.php | 31 +++++++++++ src/Views/comment-add.phtml | 5 +- src/Views/comment-small.phtml | 6 +- src/Views/top-navigation.phtml | 2 +- 12 files changed, 203 insertions(+), 67 deletions(-) create mode 100644 src/Api.php create mode 100644 src/Jobs/AbstractJob.php create mode 100644 src/Jobs/AddCommentJob.php create mode 100644 src/Jobs/EditCommentJob.php create mode 100644 src/Jobs/PreviewCommentJob.php diff --git a/public_html/dispatch.php b/public_html/dispatch.php index 182d6b54..18b24d3f 100644 --- a/public_html/dispatch.php +++ b/public_html/dispatch.php @@ -35,7 +35,7 @@ $context->simpleActionName = null; TextCaseConverter::SPINAL_CASE); $context->simpleActionName = TextCaseConverter::convert( - str_replace('Action', '', $methodName), + preg_replace('/Action|View/', '', $methodName), TextCaseConverter::CAMEL_CASE, TextCaseConverter::SPINAL_CASE); @@ -45,6 +45,13 @@ $context->simpleActionName = null; $context->simpleActionName); }); +\Chibi\Router::register(['CommentController', 'listView'], 'GET', '/comments'); +\Chibi\Router::register(['CommentController', 'listView'], 'GET', '/comments/{page}', ['page' => '\d+']); +\Chibi\Router::register(['CommentController', 'addAction'], 'POST', '/comment/add'); +\Chibi\Router::register(['CommentController', 'deleteAction'], 'POST', '/comment/{id}/delete', ['id' => '\d+']); +\Chibi\Router::register(['CommentController', 'editView'], 'GET', '/comment/{id}/edit', ['id' => '\d+']); +\Chibi\Router::register(['CommentController', 'editAction'], 'POST', '/comment/{id}/edit', ['id' => '\d+']); + foreach (['GET', 'POST'] as $method) { \Chibi\Router::register(['IndexController', 'indexAction'], $method, ''); @@ -59,11 +66,6 @@ foreach (['GET', 'POST'] as $method) \Chibi\Router::register(['AuthController', 'logoutAction'], $method, '/auth/logout'); \Chibi\Router::register(['AuthController', 'loginAction'], 'POST', '/auth/login'); \Chibi\Router::register(['AuthController', 'logoutAction'], 'POST', '/auth/logout'); - \Chibi\Router::register(['CommentController', 'listAction'], $method, '/comments'); - \Chibi\Router::register(['CommentController', 'listAction'], $method, '/comments/{page}', ['page' => '\d+']); - \Chibi\Router::register(['CommentController', 'addAction'], $method, '/post/{postId}/add-comment', ['postId' => '\d+']); - \Chibi\Router::register(['CommentController', 'deleteAction'], $method, '/comment/{id}/delete', ['id' => '\d+']); - \Chibi\Router::register(['CommentController', 'editAction'], $method, '/comment/{id}/edit', ['id' => '\d+']); $postValidation = [ diff --git a/src/Api.php b/src/Api.php new file mode 100644 index 00000000..64099943 --- /dev/null +++ b/src/Api.php @@ -0,0 +1,33 @@ +requiresAuthentication()) + Access::assertAuthentication(); + + if ($job->requiresConfirmedEmail()) + Access::assertEmailConfirmation(); + + return $job->execute($jobArgs); + }); + } + + public static function runMultiple($jobs) + { + $statuses = []; + \Chibi\Database::transaction(function() use ($jobs, &$statuses) + { + foreach ($jobs as $jobItem) + { + list ($job, $jobArgs) = $jobItem; + $statuses []= self::run($job, $jobArgs); + } + }); + return $statuses; + } +} diff --git a/src/Auth.php b/src/Auth.php index a2c90fd2..e3e91c09 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -85,6 +85,7 @@ class Auth private static function getAnonymousUser() { $dummy = UserModel::spawn(); + $dummy->id = null; $dummy->name = UserModel::getAnonymousName(); $dummy->accessRank = AccessRank::Anonymous; return $dummy; diff --git a/src/Controllers/CommentController.php b/src/Controllers/CommentController.php index 05cceb5e..5c4a6ea5 100644 --- a/src/Controllers/CommentController.php +++ b/src/Controllers/CommentController.php @@ -1,7 +1,7 @@ transport->paginator->params = func_get_args(); } - public function addAction($postId) + public function previewAction() { - $context = getContext(); - Access::assert(Privilege::AddComment); - if (getConfig()->registration->needEmailForCommenting) - Access::assertEmailConfirmation(); + $comment = Api::run( + new PreviewCommentJob(), + [ + 'text' => InputHelper::get('text') + ]); - $post = PostModel::findById($postId); - $context->transport->post = $post; + getContext()->transport->textPreview = $comment->getText(); + } - if (!InputHelper::get('submit')) - return; + public function addAction() + { + if (InputHelper::get('sender') == 'preview') + return $this->previewAction(); - $text = InputHelper::get('text'); - $text = CommentModel::validateText($text); + $comment = Api::run( + new AddCommentJob(), + [ + 'post-id' => InputHelper::get('post-id'), + 'text' => InputHelper::get('text') + ]); + } - $comment = CommentModel::spawn(); - $comment->setPost($post); - if (Auth::isLoggedIn()) - $comment->setCommenter(Auth::getCurrentUser()); - else - $comment->setCommenter(null); - $comment->commentDate = time(); - $comment->text = $text; - - if (InputHelper::get('sender') != 'preview') - { - CommentModel::save($comment); - LogHelper::log('{user} commented on {post}', ['post' => TextHelper::reprPost($post->id)]); - } - $context->transport->textPreview = $comment->getText(); + public function editView($id) + { + getContext()->transport->comment = CommentModel::findById($id); } public function editAction($id) { - $context = getContext(); - $comment = CommentModel::findById($id); - $context->transport->comment = $comment; + if (InputHelper::get('sender') == 'preview') + return $this->previewAction(); - Access::assert( - Privilege::EditComment, - Access::getIdentity($comment->getCommenter())); - - if (!InputHelper::get('submit')) - return; - - $text = InputHelper::get('text'); - $text = CommentModel::validateText($text); - - $comment->text = $text; - - if (InputHelper::get('sender') != 'preview') - { - CommentModel::save($comment); - LogHelper::log('{user} edited comment in {post}', [ - 'post' => TextHelper::reprPost($comment->getPost())]); - } - $context->transport->textPreview = $comment->getText(); + $comment = Api::run( + new EditCommentJob(), + [ + 'comment-id' => $id, + 'text' => InputHelper::get('text') + ]); } public function deleteAction($id) diff --git a/src/Helpers/LogHelper.php b/src/Helpers/LogHelper.php index 2a1bf35d..f573358d 100644 --- a/src/Helpers/LogHelper.php +++ b/src/Helpers/LogHelper.php @@ -71,11 +71,17 @@ class LogEvent $this->text = $text; $this->ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'; - $tokens['anon'] = UserModel::getAnonymousName(); - if (Auth::isLoggedIn()) - $tokens['user'] = TextHelper::reprUser(Auth::getCurrentUser()->name); - else - $tokens['user'] = $tokens['anon']; + //todo: deprecated + if (!isset($tokens['anon'])) + $tokens['anon'] = UserModel::getAnonymousName(); + if (!isset($tokens['user'])) + { + if (Auth::isLoggedIn()) + $tokens['user'] = TextHelper::reprUser(Auth::getCurrentUser()->name); + else + $tokens['user'] = $tokens['anon']; + } + $this->tokens = $tokens; } diff --git a/src/Jobs/AbstractJob.php b/src/Jobs/AbstractJob.php new file mode 100644 index 00000000..a12400e4 --- /dev/null +++ b/src/Jobs/AbstractJob.php @@ -0,0 +1,9 @@ +setCommenter($user); + $comment->setPost($post); + $comment->commentDate = time(); + $comment->text = $text; + + CommentModel::save($comment); + LogHelper::log('{user} commented on {post}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($comment->getPost()->id)]); + + return $comment; + } + + public function requiresPrivilege() + { + return Privilege::AddComment; + } + + public function requiresAuthentication() + { + return true; + } + + public function requiresConfirmedEmail() + { + return getConfig()->registration->needEmailForCommenting; + } +} diff --git a/src/Jobs/EditCommentJob.php b/src/Jobs/EditCommentJob.php new file mode 100644 index 00000000..1c7d6589 --- /dev/null +++ b/src/Jobs/EditCommentJob.php @@ -0,0 +1,35 @@ +commentDate = time(); + $comment->text = $text; + + CommentModel::save($comment); + LogHelper::log('{user} edited comment in {post}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($comment->getPost())]); + + return $comment; + } + + public function requiresPrivilege() + { + return Privilege::EditComment; + } + + public function requiresAuthentication() + { + return true; + } + + public function requiresConfirmedEmail() + { + return getConfig()->registration->needEmailForCommenting; + } +} diff --git a/src/Jobs/PreviewCommentJob.php b/src/Jobs/PreviewCommentJob.php new file mode 100644 index 00000000..b3f3c35f --- /dev/null +++ b/src/Jobs/PreviewCommentJob.php @@ -0,0 +1,31 @@ +setCommenter($user); + $comment->commentDate = time(); + $comment->text = $text; + + return $comment; + } + + public function requiresPrivilege() + { + return Privilege::AddComment; + } + + public function requiresAuthentication() + { + return true; + } + + public function requiresConfirmedEmail() + { + return getConfig()->registration->needEmailForCommenting; + } +} diff --git a/src/Views/comment-add.phtml b/src/Views/comment-add.phtml index 8733249d..96213d13 100644 --- a/src/Views/comment-add.phtml +++ b/src/Views/comment-add.phtml @@ -5,9 +5,7 @@ Assets::addScript('comment-edit.js');

add comment

@@ -18,6 +16,7 @@ Assets::addScript('comment-edit.js');
+
diff --git a/src/Views/comment-small.phtml b/src/Views/comment-small.phtml index 09d77879..46092298 100644 --- a/src/Views/comment-small.phtml +++ b/src/Views/comment-small.phtml @@ -41,15 +41,15 @@ Assets::addScript('comment-edit.js'); Access::getIdentity($commenter))): ?> edit -