From 6ae4cea8bb7ef31e6311a5d5ad313cba2b089c86 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Sat, 3 May 2014 14:20:48 +0200 Subject: [PATCH] Moved post upload to API --- public_html/dispatch.php | 4 +- public_html/media/js/post-upload.js | 2 + src/Api.php | 8 +++ src/ApiMissingArgumentException.php | 8 +++ src/Controllers/PostController.php | 65 +++++++------------- src/Jobs/Abstraction/AbstractJob.php | 15 ++++- src/Jobs/AddPostJob.php | 89 ++++++++++++++++++++++++++++ src/Jobs/EditPostContentJob.php | 38 ++++++++++++ src/Jobs/EditPostRelationsJob.php | 54 +++++++++++++++++ src/Jobs/EditPostSafetyJob.php | 45 ++++++++++++++ src/Jobs/EditPostSourceJob.php | 45 ++++++++++++++ src/Jobs/EditPostTagsJob.php | 52 ++++++++++++++++ src/Jobs/EditPostUrlJob.php | 40 +++++++++++++ src/Models/AbstractCrudModel.php | 8 +++ src/Models/PostModel.php | 4 ++ src/Models/TokenModel.php | 1 - src/Views/top-navigation.phtml | 2 +- 17 files changed, 433 insertions(+), 47 deletions(-) create mode 100644 src/ApiMissingArgumentException.php create mode 100644 src/Jobs/AddPostJob.php create mode 100644 src/Jobs/EditPostContentJob.php create mode 100644 src/Jobs/EditPostRelationsJob.php create mode 100644 src/Jobs/EditPostSafetyJob.php create mode 100644 src/Jobs/EditPostSourceJob.php create mode 100644 src/Jobs/EditPostTagsJob.php create mode 100644 src/Jobs/EditPostUrlJob.php diff --git a/public_html/dispatch.php b/public_html/dispatch.php index 990df736..2c3d1d74 100644 --- a/public_html/dispatch.php +++ b/public_html/dispatch.php @@ -70,6 +70,9 @@ $postValidation = 'score' => '-1|0|1', ]; +\Chibi\Router::register(['PostController', 'uploadView'], 'GET', '/posts/upload', $postValidation); +\Chibi\Router::register(['PostController', 'uploadAction'], 'POST', '/posts/upload', $postValidation); + \Chibi\Router::register(['PostController', 'listView'], 'GET', '/{source}', $postValidation); \Chibi\Router::register(['PostController', 'listView'], 'GET', '/{source}/{query}', $postValidation); \Chibi\Router::register(['PostController', 'listView'], 'GET', '/{source}/{query}/{page}', $postValidation); @@ -92,7 +95,6 @@ $postValidation = foreach (['GET', 'POST'] as $method) { - \Chibi\Router::register(['PostController', 'uploadAction'], $method, '/posts/upload', $postValidation); \Chibi\Router::register(['PostController', 'viewAction'], $method, '/post/{id}', $postValidation); \Chibi\Router::register(['PostController', 'retrieveAction'], $method, '/post/{name}/retrieve', $postValidation); \Chibi\Router::register(['PostController', 'thumbAction'], $method, '/post/{name}/thumb', $postValidation); diff --git a/public_html/media/js/post-upload.js b/public_html/media/js/post-upload.js index 40d18c93..370e5eb3 100644 --- a/public_html/media/js/post-upload.js +++ b/public_html/media/js/post-upload.js @@ -166,6 +166,7 @@ $(function() { handleInputs(files, function(postDom, file) { + postDom.data('url', ''); postDom.data('file', file); $('.file-name strong', postDom).text(file.name); @@ -198,6 +199,7 @@ $(function() handleInputs(urls, function(postDom, url) { postDom.data('url', url); + postDom.data('file', ''); postDom.find('[name=source]').val(url); if (matches = url.match(/watch.*?=([a-zA-Z0-9_-]+)/)) { diff --git a/src/Api.php b/src/Api.php index 2b372a46..0d3fa212 100644 --- a/src/Api.php +++ b/src/Api.php @@ -40,4 +40,12 @@ class Api }); return $statuses; } + + public static function serializeFile($filePath, $fileName) + { + $x = new StdClass; + $x->filePath = $filePath; + $x->fileName = $fileName; + return $x; + } } diff --git a/src/ApiMissingArgumentException.php b/src/ApiMissingArgumentException.php new file mode 100644 index 00000000..83c32c95 --- /dev/null +++ b/src/ApiMissingArgumentException.php @@ -0,0 +1,8 @@ +registration->needEmailForUploading) - Access::assertEmailConfirmation(); + $jobArgs = + [ + AddPostJob::ANONYMOUS => InputHelper::get('anonymous'), + EditPostSafetyJob::SAFETY => InputHelper::get('safety'), + EditPostTagsJob::TAG_NAMES => InputHelper::get('tags'), + EditPostSourceJob::SOURCE => InputHelper::get('source'), + ]; - if (!InputHelper::get('submit')) - return; - - \Chibi\Database::transaction(function() use ($context) + if (!empty(InputHelper::get('url'))) { - $post = PostModel::spawn(); - LogHelper::bufferChanges(); + $jobArgs[EditPostUrlJob::CONTENT_URL] = InputHelper::get('url'); + } + elseif (!empty($_FILES['file']['name'])) + { + $file = $_FILES['file']; + TransferHelper::handleUploadErrors($file); - //basic stuff - $anonymous = InputHelper::get('anonymous'); - if (Auth::isLoggedIn() and !$anonymous) - $post->setUploader(Auth::getCurrentUser()); + $jobArgs[EditPostContentJob::POST_CONTENT] = Api::serializeFile( + $file['tmp_name'], + $file['name']); + } - //store the post to get the ID in the logs - PostModel::forgeId($post); - - //do the edits - $this->doEdit($post, true); - - //this basically means that user didn't specify file nor url - if (empty($post->type)) - throw new SimpleException('No post type detected; upload faled'); - - //clean edit log - LogHelper::setBuffer([]); - - //log - $fmt = ($anonymous and !getConfig()->misc->logAnonymousUploads) - ? '{anon}' - : '{user}'; - $fmt .= ' added {post} (tags: {tags}, safety: {safety}, source: {source})'; - LogHelper::log($fmt, [ - 'post' => TextHelper::reprPost($post), - 'tags' => TextHelper::reprTags($post->getTags()), - 'safety' => PostSafety::toString($post->safety), - 'source' => $post->source]); - - //finish - LogHelper::flush(); - PostModel::save($post); - }); + Api::run(new AddPostJob(), $jobArgs); } public function editAction($id) diff --git a/src/Jobs/Abstraction/AbstractJob.php b/src/Jobs/Abstraction/AbstractJob.php index 64f099b9..a17a7768 100644 --- a/src/Jobs/Abstraction/AbstractJob.php +++ b/src/Jobs/Abstraction/AbstractJob.php @@ -4,6 +4,7 @@ abstract class AbstractJob const COMMENT_ID = 'comment-id'; const POST_ID = 'post-id'; const TAG_NAME = 'tag-name'; + const TAG_NAMES = 'tags'; const TEXT = 'text'; const PAGE_NUMBER = 'page-number'; const QUERY = 'query'; @@ -24,12 +25,22 @@ abstract class AbstractJob public function getArgument($key) { - if (!isset($this->arguments[$key])) - throw new SimpleException('Expected argument "' . $key . '" was not specified'); + if (!$this->hasArgument($key)) + throw new ApiMissingArgumentException($key); return $this->arguments[$key]; } + public function getArguments() + { + return $this->arguments; + } + + public function hasArgument($key) + { + return isset($this->arguments[$key]); + } + public function setArguments($arguments) { $this->arguments = $arguments; diff --git a/src/Jobs/AddPostJob.php b/src/Jobs/AddPostJob.php new file mode 100644 index 00000000..f082811b --- /dev/null +++ b/src/Jobs/AddPostJob.php @@ -0,0 +1,89 @@ +getArgument(self::ANONYMOUS); + if (Auth::isLoggedIn() and !$anonymous) + $post->setUploader(Auth::getCurrentUser()); + + //store the post to get the ID in the logs + PostModel::forgeId($post); + + //do the edits + //warning: each handler runs uses the same privileges as post editing + $subJobs = + [ + new EditPostSafetyJob(), + new EditPostTagsJob(), + new EditPostSourceJob(), + new EditPostRelationsJob(), + new EditPostContentJob(), + new EditPostUrlJob(), + ]; + + foreach ($subJobs as $subJob) + { + $args = $this->getArguments(); + $args[self::POST_ID] = $post->id; + try + { + Api::run($subJob, $args); + } + catch (ApiMissingArgumentException $e) + { + } + } + + //load the post after edits + $post = PostModel::findById($post->id); + + // basically means that user didn't specify file nor url + //todo: + //- move this to PostEntity::isValid() + //- create IValidatable interface + //- enforce entity validity upon calling save() in models + if (empty($post->type)) + throw new SimpleException('No post type detected; upload faled'); + + //clean edit log + LogHelper::setBuffer([]); + + //log + LogHelper::log('{user} added {post} (tags: {tags}, safety: {safety}, source: {source})', [ + 'user' => ($anonymous and !getConfig()->misc->logAnonymousUploads) + ? TextHelper::reprUser(UserModel::getAnonymousName()) + : TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'tags' => TextHelper::reprTags($post->getTags()), + 'safety' => PostSafety::toString($post->safety), + 'source' => $post->source]); + + //finish + LogHelper::flush(); + PostModel::save($post); + + return $post; + } + + public function requiresPrivilege() + { + return Privilege::UploadPost; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return getConfig()->registration->needEmailForUploading; + } +} diff --git a/src/Jobs/EditPostContentJob.php b/src/Jobs/EditPostContentJob.php new file mode 100644 index 00000000..26ab0762 --- /dev/null +++ b/src/Jobs/EditPostContentJob.php @@ -0,0 +1,38 @@ +getArgument(self::CONTENT); + + $this->post->setContentFromPath($file->filePath, $file->fileName); + + PostModel::save($this->post); + LogHelper::log('{user} changed contents of {post}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($this->post)]); + + return $this->post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostFile, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Jobs/EditPostRelationsJob.php b/src/Jobs/EditPostRelationsJob.php new file mode 100644 index 00000000..055d53dd --- /dev/null +++ b/src/Jobs/EditPostRelationsJob.php @@ -0,0 +1,54 @@ +post; + $relations = $this->getArgument(self::RELATED_POST_IDS); + + $oldRelatedIds = array_map(function($post) { return $post->id; }, $post->getRelations()); + $post->setRelationsFromText($relations); + $newRelatedIds = array_map(function($post) { return $post->id; }, $post->getRelations()); + + PostModel::save($post); + + foreach (array_diff($oldRelatedIds, $newRelatedIds) as $post2id) + { + LogHelper::log('{user} removed relation between {post} and {post2}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'post2' => TextHelper::reprPost($post2id)]); + } + + foreach (array_diff($newRelatedIds, $oldRelatedIds) as $post2id) + { + LogHelper::log('{user} added relation between {post} and {post2}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'post2' => TextHelper::reprPost($post2id)]); + } + + return $post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostRelations, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Jobs/EditPostSafetyJob.php b/src/Jobs/EditPostSafetyJob.php new file mode 100644 index 00000000..77621fe4 --- /dev/null +++ b/src/Jobs/EditPostSafetyJob.php @@ -0,0 +1,45 @@ +post; + $newSafety = $this->getArgument(self::SAFETY); + + $oldSafety = $post->safety; + $post->setSafety($newSafety); + + PostModel::save($post); + + if ($oldSafety != $newSafety) + { + LogHelper::log('{user} changed safety of {post} to {safety}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'safety' => PostSafety::toString($post->safety)]); + } + + return $post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostSafety, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Jobs/EditPostSourceJob.php b/src/Jobs/EditPostSourceJob.php new file mode 100644 index 00000000..a2f5b6bc --- /dev/null +++ b/src/Jobs/EditPostSourceJob.php @@ -0,0 +1,45 @@ +post; + $newSource = $this->getArgument(self::SOURCE); + + $oldSource = $post->source; + $post->setSource($newSource); + + PostModel::save($post); + + if ($oldSource != $newSource) + { + LogHelper::log('{user} changed source of {post} to {source}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'source' => $post->source]); + } + + return $post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostSource, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Jobs/EditPostTagsJob.php b/src/Jobs/EditPostTagsJob.php new file mode 100644 index 00000000..a0c65aad --- /dev/null +++ b/src/Jobs/EditPostTagsJob.php @@ -0,0 +1,52 @@ +post; + $tags = $this->getArgument(self::TAG_NAMES); + + $oldTags = array_map(function($tag) { return $tag->name; }, $post->getTags()); + $post->setTagsFromText($tags); + $newTags = array_map(function($tag) { return $tag->name; }, $post->getTags()); + + PostModel::save($post); + + foreach (array_diff($oldTags, $newTags) as $tag) + { + LogHelper::log('{user} untagged {post} with {tag}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'tag' => TextHelper::reprTag($tag)]); + } + + foreach (array_diff($newTags, $oldTags) as $tag) + { + LogHelper::log('{user} tagged {post} with {tag}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post), + 'tag' => TextHelper::reprTag($tag)]); + } + + return $post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostTags, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Jobs/EditPostUrlJob.php b/src/Jobs/EditPostUrlJob.php new file mode 100644 index 00000000..c945abb9 --- /dev/null +++ b/src/Jobs/EditPostUrlJob.php @@ -0,0 +1,40 @@ +post; + $url = $this->getArgument(self::CONTENT_URL); + + $post->setContentFromUrl($url); + + PostModel::save($post); + + LogHelper::log('{user} changed contents of {post}', [ + 'user' => TextHelper::reprUser(Auth::getCurrentUser()), + 'post' => TextHelper::reprPost($post)]); + + return $post; + } + + public function requiresPrivilege() + { + return + [ + Privilege::EditPostFile, + Access::getIdentity($this->post->getUploader()) + ]; + } + + public function requiresAuthentication() + { + return false; + } + + public function requiresConfirmedEmail() + { + return false; + } +} diff --git a/src/Models/AbstractCrudModel.php b/src/Models/AbstractCrudModel.php index 9c796228..434fdf32 100644 --- a/src/Models/AbstractCrudModel.php +++ b/src/Models/AbstractCrudModel.php @@ -120,6 +120,14 @@ abstract class AbstractCrudModel implements IModel { $stmt = new Sql\InsertStatement(); $stmt->setTable($table); + foreach ($entity as $key => $val) + { + $key = TextCaseConverter::convert($key, + TextCaseConverter::LOWER_CAMEL_CASE, + TextCaseConverter::SNAKE_CASE); + + $stmt->setColumn($key, new Sql\Binding($val)); + } Database::exec($stmt); $entity->id = Database::lastInsertId(); } diff --git a/src/Models/PostModel.php b/src/Models/PostModel.php index b616fd0b..80b236df 100644 --- a/src/Models/PostModel.php +++ b/src/Models/PostModel.php @@ -19,6 +19,10 @@ class PostModel extends AbstractCrudModel public static function spawn() { $post = new PostEntity; + $post->score = 0; + $post->favCount = 0; + $post->commentCount = 0; + $post->safety = PostSafety::Safe; $post->hidden = false; $post->uploadDate = time(); do diff --git a/src/Models/TokenModel.php b/src/Models/TokenModel.php index fe4167ec..b9336cd5 100644 --- a/src/Models/TokenModel.php +++ b/src/Models/TokenModel.php @@ -30,7 +30,6 @@ class TokenModel extends AbstractCrudModel $stmt->setColumn($key, new Sql\Binding($val)); Database::exec($stmt); - }); } diff --git a/src/Views/top-navigation.phtml b/src/Views/top-navigation.phtml index a9653fd8..2af70ccc 100644 --- a/src/Views/top-navigation.phtml +++ b/src/Views/top-navigation.phtml @@ -30,7 +30,7 @@ { $registerNavItem( 'Upload', - \Chibi\Router::linkTo(['PostController', 'uploadAction']), + \Chibi\Router::linkTo(['PostController', 'uploadView']), $activeController == 'post' and $activeAction == 'upload'); }