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.
This commit is contained in:
parent
feec48ed83
commit
902aed7278
12 changed files with 203 additions and 67 deletions
|
@ -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 =
|
||||
[
|
||||
|
|
33
src/Api.php
Normal file
33
src/Api.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
class Api
|
||||
{
|
||||
public static function run($job, $jobArgs)
|
||||
{
|
||||
$user = Auth::getCurrentUser();
|
||||
|
||||
return \Chibi\Database::transaction(function() use ($job, $jobArgs)
|
||||
{
|
||||
if ($job->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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
class CommentController
|
||||
{
|
||||
public function listAction($page)
|
||||
public function listView($page)
|
||||
{
|
||||
Access::assert(Privilege::ListComments);
|
||||
|
||||
|
@ -30,64 +30,46 @@ class CommentController
|
|||
$context->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;
|
||||
|
||||
if (!InputHelper::get('submit'))
|
||||
return;
|
||||
|
||||
$text = InputHelper::get('text');
|
||||
$text = CommentModel::validateText($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)]);
|
||||
getContext()->transport->textPreview = $comment->getText();
|
||||
}
|
||||
$context->transport->textPreview = $comment->getText();
|
||||
|
||||
public function addAction()
|
||||
{
|
||||
if (InputHelper::get('sender') == 'preview')
|
||||
return $this->previewAction();
|
||||
|
||||
$comment = Api::run(
|
||||
new AddCommentJob(),
|
||||
[
|
||||
'post-id' => InputHelper::get('post-id'),
|
||||
'text' => InputHelper::get('text')
|
||||
]);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -71,11 +71,17 @@ class LogEvent
|
|||
$this->text = $text;
|
||||
$this->ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
|
9
src/Jobs/AbstractJob.php
Normal file
9
src/Jobs/AbstractJob.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
abstract class AbstractJob
|
||||
{
|
||||
public abstract function execute($arguments);
|
||||
|
||||
public abstract function requiresAuthentication();
|
||||
public abstract function requiresConfirmedEmail();
|
||||
public abstract function requiresPrivilege();
|
||||
}
|
38
src/Jobs/AddCommentJob.php
Normal file
38
src/Jobs/AddCommentJob.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
class AddCommentJob extends AbstractJob
|
||||
{
|
||||
public function execute($arguments)
|
||||
{
|
||||
$post = PostModel::findById($arguments['post-id']);
|
||||
$user = Auth::getCurrentUser();
|
||||
$text = CommentModel::validateText($arguments['text']);
|
||||
|
||||
$comment = CommentModel::spawn();
|
||||
$comment->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;
|
||||
}
|
||||
}
|
35
src/Jobs/EditCommentJob.php
Normal file
35
src/Jobs/EditCommentJob.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
class EditCommentJob extends AbstractJob
|
||||
{
|
||||
public function execute($arguments)
|
||||
{
|
||||
$user = Auth::getCurrentUser();
|
||||
$comment = CommentModel::findById($arguments['comment-id']);
|
||||
$text = CommentModel::validateText($arguments['text']);
|
||||
|
||||
$comment->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;
|
||||
}
|
||||
}
|
31
src/Jobs/PreviewCommentJob.php
Normal file
31
src/Jobs/PreviewCommentJob.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
class PreviewCommentJob extends AbstractJob
|
||||
{
|
||||
public function execute($arguments)
|
||||
{
|
||||
$user = Auth::getCurrentUser();
|
||||
$text = CommentModel::validateText($arguments['text']);
|
||||
|
||||
$comment = CommentModel::spawn();
|
||||
$comment->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;
|
||||
}
|
||||
}
|
|
@ -5,9 +5,7 @@ Assets::addScript('comment-edit.js');
|
|||
|
||||
<form
|
||||
method="post"
|
||||
action="<?= \Chibi\Router::linkTo(
|
||||
['CommentController', 'addAction'],
|
||||
['postId' => $this->context->transport->post->id]) ?>"
|
||||
action="<?= \Chibi\Router::linkTo(['CommentController', 'addAction']) ?>"
|
||||
class="add-comment">
|
||||
|
||||
<h1>add comment</h1>
|
||||
|
@ -18,6 +16,7 @@ Assets::addScript('comment-edit.js');
|
|||
<div class="input-wrapper"><textarea name="text" cols="50" rows="3"></textarea></div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="post-id" value="<?= $this->context->transport->post->id ?>">
|
||||
<input type="hidden" name="submit" value="1"/>
|
||||
|
||||
<div class="form-row">
|
||||
|
|
|
@ -41,15 +41,15 @@ Assets::addScript('comment-edit.js');
|
|||
Access::getIdentity($commenter))): ?>
|
||||
<span class="edit">
|
||||
<a href="<?= \Chibi\Router::linkTo(
|
||||
['CommentController', 'editAction'],
|
||||
['CommentController', 'editView'],
|
||||
['id' => $this->context->comment->id]) ?>">
|
||||
edit
|
||||
</a>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (
|
||||
Access::check(Privilege::DeleteComment,
|
||||
<?php if (Access::check(
|
||||
Privilege::DeleteComment,
|
||||
Access::getIdentity($commenter))): ?>
|
||||
<span class="delete">
|
||||
<a class="simple-action confirmable"
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
{
|
||||
$registerNavItem(
|
||||
'Comments',
|
||||
\Chibi\Router::linkTo(['CommentController', 'listAction']),
|
||||
\Chibi\Router::linkTo(['CommentController', 'listView']),
|
||||
$activeController == 'comment');
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue