diff --git a/TODO b/TODO index 0a1a3eda..1747b543 100644 --- a/TODO +++ b/TODO @@ -2,10 +2,6 @@ This is transient file that lists functionalities to be implemented before first major release. everything related to posts: - - single post view - - editing - - ability to loop video posts - - ability to paste many urls in post upload - post notes diff --git a/data/config.ini b/data/config.ini index d652ddc2..ba628486 100644 --- a/data/config.ini +++ b/data/config.ini @@ -53,6 +53,7 @@ changePostTags = regularUser, powerUser, moderator, administrator changePostContent = regularUser, powerUser, moderator, administrator changePostThumbnail = powerUser, moderator, administrator changePostRelations = regularUser, powerUser, moderator, administrator +changePostFlags = regularUser, powerUser, moderator, administrator listTags = anonymous, regularUser, powerUser, moderator, administrator diff --git a/public_html/js/Auth.js b/public_html/js/Auth.js index 1ae9f866..993cd6cc 100644 --- a/public_html/js/Auth.js +++ b/public_html/js/Auth.js @@ -32,6 +32,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) { changePostContent: 'changePostContent', changePostThumbnail: 'changePostThumbnail', changePostRelations: 'changePostRelations', + changePostFlags: 'changePostFlags', listComments: 'listComments', addComments: 'addComments', diff --git a/public_html/js/Presenters/PostPresenter.js b/public_html/js/Presenters/PostPresenter.js index 2edef41c..8a3872c7 100644 --- a/public_html/js/Presenters/PostPresenter.js +++ b/public_html/js/Presenters/PostPresenter.js @@ -47,6 +47,7 @@ App.Presenters.PostPresenter = function( editPrivileges.canChangeContent = auth.hasPrivilege(auth.privileges.changePostContent); editPrivileges.canChangeThumbnail = auth.hasPrivilege(auth.privileges.changePostThumbnail); editPrivileges.canChangeRelations = auth.hasPrivilege(auth.privileges.changePostRelations); + editPrivileges.canChangeFlags = auth.hasPrivilege(auth.privileges.changePostFlags); promise.wait( util.promiseTemplate('post'), @@ -162,6 +163,11 @@ App.Presenters.PostPresenter = function( attachLinksToPostsAround(); } + function softRender() { + renderSidebar(); + $el.find('video').prop('loop', post.flags.loop); + } + function renderSidebar() { $el.find('#sidebar').html(jQuery(renderPostTemplate()).find('#sidebar').html()); attachSidebarEvents(); @@ -262,6 +268,7 @@ App.Presenters.PostPresenter = function( var $form = $el.find('form'); var formData = {}; formData.seenEditTime = post.lastEditTime; + formData.flags = {}; if (editPrivileges.canChangeContent && postContent) { formData.content = postContent; @@ -287,6 +294,12 @@ App.Presenters.PostPresenter = function( formData.relations = $form.find('[name=relations]').val(); } + if (editPrivileges.canChangeFlags) { + if (post.contentType === 'video') { + formData.flags.loop = $form.find('[name=loop]').is(':checked') ? 1 : 0; + } + } + if (post.tags.length === 0) { showEditError('No tags set.'); return; @@ -296,7 +309,7 @@ App.Presenters.PostPresenter = function( .then(function(response) { post = response.json; hideEditForm(); - renderSidebar(); + softRender(); }).fail(function(response) { showEditError(response); }); @@ -344,14 +357,14 @@ App.Presenters.PostPresenter = function( function addFavorite() { promise.wait(api.post('/posts/' + post.id + '/favorites')) .then(function(response) { - promise.wait(refreshPost()).then(renderSidebar); + promise.wait(refreshPost()).then(softRender); }).fail(showGenericError); } function deleteFavorite() { promise.wait(api.delete('/posts/' + post.id + '/favorites')) .then(function(response) { - promise.wait(refreshPost()).then(renderSidebar); + promise.wait(refreshPost()).then(softRender); }).fail(showGenericError); } @@ -370,7 +383,7 @@ App.Presenters.PostPresenter = function( function score(scoreValue) { promise.wait(api.post('/posts/' + post.id + '/score', {score: scoreValue})) .then(function() { - promise.wait(refreshPost()).then(renderSidebar); + promise.wait(refreshPost()).then(softRender); }).fail(showGenericError); } diff --git a/public_html/templates/post-content.tpl b/public_html/templates/post-content.tpl index 1b837c2f..cb210a72 100644 --- a/public_html/templates/post-content.tpl +++ b/public_html/templates/post-content.tpl @@ -2,15 +2,15 @@
- <% if (post.contentType == 'image') { %> + <% if (post.contentType === 'image') { %> <%= post.name %> - <% } else if (post.contentType == 'youtube') { %> + <% } else if (post.contentType === 'youtube') { %> - <% } else if (post.contentType == 'flash') { %> + <% } else if (post.contentType === 'flash') { %> - <% } else if (post.contentType == 'video') { %> + <% } else if (post.contentType === 'video') { %> + <% if (post.flags.loop) { %> +
<% } %> + <% if (privileges.canChangeFlags && post.contentType === 'video') { %> +
+ +
+ /> + +
+
+ <% } %> + <% if (privileges.canChangeContent) { %>
diff --git a/src/Controllers/ViewProxies/PostViewProxy.php b/src/Controllers/ViewProxies/PostViewProxy.php index 31e83bd1..96ab15f7 100644 --- a/src/Controllers/ViewProxies/PostViewProxy.php +++ b/src/Controllers/ViewProxies/PostViewProxy.php @@ -8,6 +8,7 @@ use Szurubooru\Services\FavoritesService; use Szurubooru\Services\HistoryService; use Szurubooru\Services\PrivilegeService; use Szurubooru\Services\ScoreService; +use Szurubooru\Entities\Post; class PostViewProxy extends AbstractViewProxy { @@ -72,6 +73,8 @@ class PostViewProxy extends AbstractViewProxy $result->favoriteCount = $post->getFavoriteCount(); $result->score = $post->getScore(); $result->commentCount = $post->getCommentCount(); + $result->flags = new \StdClass; + $result->flags->loop = ($post->getFlags() & Post::FLAG_LOOP); if (!empty($config[self::FETCH_TAGS])) $result->tags = $this->tagViewProxy->fromArray($post->getTags()); diff --git a/src/Dao/EntityConverters/PostEntityConverter.php b/src/Dao/EntityConverters/PostEntityConverter.php index d9d89bdf..897bdb45 100644 --- a/src/Dao/EntityConverters/PostEntityConverter.php +++ b/src/Dao/EntityConverters/PostEntityConverter.php @@ -25,6 +25,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv 'originalFileName' => $entity->getOriginalFileName(), 'featureCount' => $entity->getFeatureCount(), 'lastFeatureTime' => $this->entityTimeToDbTime($entity->getLastFeatureTime()), + 'flags' => $entity->getFlags(), ]; } @@ -46,6 +47,7 @@ class PostEntityConverter extends AbstractEntityConverter implements IEntityConv $entity->setOriginalFileName($array['originalFileName']); $entity->setFeatureCount(intval($array['featureCount'])); $entity->setLastFeatureTime($this->dbTimeToEntityTime($array['lastFeatureTime'])); + $entity->setFlags(intval($array['flags'])); $entity->setMeta(Post::META_TAG_COUNT, intval($array['tagCount'])); $entity->setMeta(Post::META_FAV_COUNT, intval($array['favCount'])); $entity->setMeta(Post::META_COMMENT_COUNT, intval($array['commentCount'])); diff --git a/src/Entities/Post.php b/src/Entities/Post.php index 661cc4a6..bc6d548b 100644 --- a/src/Entities/Post.php +++ b/src/Entities/Post.php @@ -13,6 +13,8 @@ final class Post extends Entity const POST_TYPE_VIDEO = 3; const POST_TYPE_YOUTUBE = 4; + const FLAG_LOOP = 1; + const LAZY_LOADER_USER = 'user'; const LAZY_LOADER_TAGS = 'tags'; const LAZY_LOADER_CONTENT = 'content'; @@ -39,6 +41,7 @@ final class Post extends Entity private $originalFileName; private $featureCount = 0; private $lastFeatureTime; + private $flags = 0; public function getIdMarkdown() { @@ -195,6 +198,16 @@ final class Post extends Entity $this->lastFeatureTime = $lastFeatureTime; } + public function getFlags() + { + return $this->flags; + } + + public function setFlags($flags) + { + $this->flags = $flags; + } + public function getTags() { return $this->lazyLoad(self::LAZY_LOADER_TAGS, []); diff --git a/src/FormData/PostEditFormData.php b/src/FormData/PostEditFormData.php index 9e32775e..63d84de0 100644 --- a/src/FormData/PostEditFormData.php +++ b/src/FormData/PostEditFormData.php @@ -12,6 +12,7 @@ class PostEditFormData implements IValidatable public $source; public $tags; public $relations; + public $flags; public $seenEditTime; @@ -26,6 +27,8 @@ class PostEditFormData implements IValidatable $this->tags = preg_split('/[\s+]/', $inputReader->tags); $this->relations = array_filter(preg_split('/[\s+]/', $inputReader->relations)); $this->seenEditTime = $inputReader->seenEditTime; + $this->flags = new \StdClass; + $this->flags->loop = !empty($inputReader->flags['loop']); } } diff --git a/src/Privilege.php b/src/Privilege.php index 15eda37a..487dc2f7 100644 --- a/src/Privilege.php +++ b/src/Privilege.php @@ -32,6 +32,7 @@ class Privilege const CHANGE_POST_CONTENT = 'changePostContent'; const CHANGE_POST_THUMBNAIL = 'changePostThumbnail'; const CHANGE_POST_RELATIONS = 'changePostRelations'; + const CHANGE_POST_FLAGS = 'changePostFlags'; const LIST_TAGS = 'listTags'; diff --git a/src/Services/HistoryService.php b/src/Services/HistoryService.php index b6511289..3abb5aa8 100644 --- a/src/Services/HistoryService.php +++ b/src/Services/HistoryService.php @@ -105,6 +105,10 @@ class HistoryService $featuredPostParam = $this->globalParamDao->findByKey(GlobalParam::KEY_FEATURED_POST); $isFeatured = ($featuredPostParam and intval($featuredPostParam->getValue()) === $post->getId()); + $flags = []; + if ($post->getFlags() & Post::FLAG_LOOP) + $flags []= 'loop'; + $data = [ 'source' => $post->getSource(), @@ -128,6 +132,7 @@ class HistoryService }, $post->getRelatedPosts()), + 'flags' => $flags, ]; $snapshot = $this->getPostSnapshot($post); diff --git a/src/Services/PostService.php b/src/Services/PostService.php index 549b018e..acdc86d9 100644 --- a/src/Services/PostService.php +++ b/src/Services/PostService.php @@ -188,6 +188,9 @@ class PostService if ($formData->relations !== null) $this->updatePostRelations($post, $formData->relations); + if (count($formData->flags) > 0) + $this->updatePostFlags($post, $formData->flags); + $this->historyService->saveSnapshot($this->historyService->getPostChangeSnapshot($post)); return $this->postDao->save($post); }; @@ -313,6 +316,14 @@ class PostService $post->setRelatedPosts($relatedPosts); } + private function updatePostFlags(Post $post, \StdClass $flags) + { + $value = 0; + if (!empty($flags->loop)) + $value |= Post::FLAG_LOOP; + $post->setFlags($value); + } + public function deletePost(Post $post) { $transactionFunc = function() use ($post) diff --git a/src/Upgrades/Upgrade19.php b/src/Upgrades/Upgrade19.php new file mode 100644 index 00000000..02752275 --- /dev/null +++ b/src/Upgrades/Upgrade19.php @@ -0,0 +1,13 @@ +getPDO(); + + $pdo->exec('ALTER TABLE posts ADD COLUMN flags INT(8) NOT NULL DEFAULT 0'); + } +} diff --git a/src/di.php b/src/di.php index 1eadc921..1b592b80 100644 --- a/src/di.php +++ b/src/di.php @@ -34,6 +34,7 @@ return [ $container->get(\Szurubooru\Upgrades\Upgrade16::class), $container->get(\Szurubooru\Upgrades\Upgrade17::class), $container->get(\Szurubooru\Upgrades\Upgrade18::class), + $container->get(\Szurubooru\Upgrades\Upgrade19::class), ]; }), diff --git a/tests/Services/HistoryServiceTest.php b/tests/Services/HistoryServiceTest.php index 77d02e15..b1c33550 100644 --- a/tests/Services/HistoryServiceTest.php +++ b/tests/Services/HistoryServiceTest.php @@ -135,6 +135,7 @@ final class HistoryServiceTest extends AbstractTestCase $post->setContentChecksum('checksum'); $post->setSafety(Post::POST_SAFETY_SKETCHY); $post->setSource('amazing source'); + $post->setFlags(Post::FLAG_LOOP); $historyService = $this->getHistoryService(); $snapshot = $historyService->getPostChangeSnapshot($post); @@ -145,7 +146,8 @@ final class HistoryServiceTest extends AbstractTestCase 'contentChecksum' => 'checksum', 'featured' => false, 'tags' => ['tag1', 'tag2'], - 'relations' => [1, 2] + 'relations' => [1, 2], + 'flags' => ['loop'], ], $snapshot->getData()); $this->assertEquals(Snapshot::TYPE_POST, $snapshot->getType());