From 97ca08cf443ae839d1e7c89c4c4ce36ef0a49f64 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 24 Sep 2014 23:24:51 +0200 Subject: [PATCH] Added post featuring --- TODO | 12 +--- data/config.ini | 1 + public_html/css/home.css | 29 +++++++++ public_html/index.html | 1 + public_html/js/Auth.js | 1 + public_html/js/Presenters/HomePresenter.js | 40 ++++++++++-- public_html/js/Presenters/PostPresenter.js | 36 +++++++++-- .../js/Presenters/TopNavigationPresenter.js | 5 ++ public_html/templates/home.tpl | 30 +++++++++ public_html/templates/post-list-item.tpl | 2 +- public_html/templates/post.tpl | 17 +++++ src/Controllers/PostController.php | 15 +++++ src/Controllers/ViewProxies/PostViewProxy.php | 3 + .../GlobalParamEntityConverter.php | 23 +++++++ .../EntityConverters/PostEntityConverter.php | 4 ++ src/Dao/GlobalParamDao.php | 34 ++++++++++ src/Entities/GlobalParam.php | 30 +++++++++ src/Entities/Post.php | 22 +++++++ src/Privilege.php | 1 + src/Services/PostService.php | 30 +++++++++ src/Upgrades/Upgrade07.php | 20 ++++++ src/di.php | 1 + tests/Dao/GlobalParamDaoTest.php | 64 +++++++++++++++++++ tests/Services/PostServiceTest.php | 3 + 24 files changed, 402 insertions(+), 22 deletions(-) create mode 100644 public_html/css/home.css create mode 100644 public_html/templates/home.tpl create mode 100644 src/Dao/EntityConverters/GlobalParamEntityConverter.php create mode 100644 src/Dao/GlobalParamDao.php create mode 100644 src/Entities/GlobalParam.php create mode 100644 src/Upgrades/Upgrade07.php create mode 100644 tests/Dao/GlobalParamDaoTest.php diff --git a/TODO b/TODO index 0b81f952..eec316a8 100644 --- a/TODO +++ b/TODO @@ -5,9 +5,6 @@ everything related to posts: - single post view - basic information - time of last edit - - time of last feature - - how many times the post was featured - - feature - fav - score (see notes about scoring) - editing @@ -30,11 +27,9 @@ everything related to posts: - hiding posts - link to iqdb and/or tineye - - featured posts - - ability to feature the post - - show the post on main page - - tag list - - uploader + - auto feature + - number of posts + - total size of posts - random post - regard safety settings @@ -115,7 +110,6 @@ everything related to comments: - score (see notes about scoring) refactors: - - centralize markdown prefix decorators - add enum validation in IValidatables (needs refactors of enums and possible disposal of EnumHelper in favor of something more subtle) - (idea) keep denormalized data in separate tables, i.e. tag usages in diff --git a/data/config.ini b/data/config.ini index c6fb52b9..d1551c18 100644 --- a/data/config.ini +++ b/data/config.ini @@ -42,6 +42,7 @@ listUnsafePosts = anonymous, regularUser, powerUser, moderator, administ uploadPosts = regularUser, powerUser, moderator, administrator uploadPostsAnonymously = regularUser, powerUser, moderator, administrator deletePosts = moderator, administrator +featurePosts = moderator, administrator listTags = anonymous, regularUser, powerUser, moderator, administrator diff --git a/public_html/css/home.css b/public_html/css/home.css new file mode 100644 index 00000000..cf52b012 --- /dev/null +++ b/public_html/css/home.css @@ -0,0 +1,29 @@ +#home { + text-align: center; +} +#home h1 { + margin-top: 0; +} + +#home .post { + text-align: left; + margin: 0 auto; + max-width: 60%; +} +#home .post .left { + display: inline-block; + float: left; +} +#home .post .right { + display: inline-block; + float: right; +} + +#home .post-footer, +#home .post-footer img { + vertical-align: middle; +} + +#home .post-footer img { + margin-left: 0.5em; +} diff --git a/public_html/index.html b/public_html/index.html index da2cef63..86a5b4cd 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -31,6 +31,7 @@ + diff --git a/public_html/js/Auth.js b/public_html/js/Auth.js index a10598cd..bc0ca76b 100644 --- a/public_html/js/Auth.js +++ b/public_html/js/Auth.js @@ -24,6 +24,7 @@ App.Auth = function(_, jQuery, util, api, appState, promise) { uploadPosts: 'uploadPosts', uploadPostsAnonymously: 'uploadPostsAnonymously', deletePosts: 'deletePosts', + featurePosts: 'featurePosts', listTags: 'listTags', }; diff --git a/public_html/js/Presenters/HomePresenter.js b/public_html/js/Presenters/HomePresenter.js index 6f2b1254..ebb1e38e 100644 --- a/public_html/js/Presenters/HomePresenter.js +++ b/public_html/js/Presenters/HomePresenter.js @@ -2,20 +2,50 @@ var App = App || {}; App.Presenters = App.Presenters || {}; App.Presenters.HomePresenter = function( + _, jQuery, - topNavigationPresenter) { + util, + promise, + api, + topNavigationPresenter, + messagePresenter) { var $el = jQuery('#content'); + var homeTemplate; + var postContentTemplate; + var post; function init(args, loaded) { topNavigationPresenter.select('home'); topNavigationPresenter.changeTitle('Home'); - render(); - loaded(); + + promise.waitAll( + util.promiseTemplate('home'), + util.promiseTemplate('post-content'), + api.get('/posts/featured')) + .then(function( + homeTemplateHtml, + postContentTemplateHtml, + response) { + homeTemplate = _.template(homeTemplateHtml); + postContentTemplate = _.template(postContentTemplateHtml); + + post = response.json; + render(); + loaded(); + + }).fail(function(response) { + messagePresenter.showError($el, response.json && response.json.error || response); + }); } function render() { - $el.html('Home placeholder'); + $el.html(homeTemplate({ + post: post, + postContentTemplate: postContentTemplate, + title: topNavigationPresenter.getBaseTitle(), + formatRelativeTime: util.formatRelativeTime, + })); } return { @@ -25,4 +55,4 @@ App.Presenters.HomePresenter = function( }; -App.DI.register('homePresenter', ['jQuery', 'topNavigationPresenter'], App.Presenters.HomePresenter); +App.DI.register('homePresenter', ['_', 'jQuery', 'util', 'promise', 'api', 'topNavigationPresenter', 'messagePresenter'], App.Presenters.HomePresenter); diff --git a/public_html/js/Presenters/PostPresenter.js b/public_html/js/Presenters/PostPresenter.js index a042b55c..0efa840a 100644 --- a/public_html/js/Presenters/PostPresenter.js +++ b/public_html/js/Presenters/PostPresenter.js @@ -25,6 +25,7 @@ App.Presenters.PostPresenter = function( topNavigationPresenter.select('posts'); privileges.canDeletePosts = auth.hasPrivilege(auth.privileges.deletePosts); + privileges.canFeaturePosts = auth.hasPrivilege(auth.privileges.featurePosts); promise.waitAll( util.promiseTemplate('post'), @@ -34,7 +35,6 @@ App.Presenters.PostPresenter = function( postTemplateHtml, postContentTemplateHtml, response) { - $messages = $el.find('.messages'); postTemplate = _.template(postTemplateHtml); postContentTemplate = _.template(postContentTemplateHtml); @@ -45,7 +45,7 @@ App.Presenters.PostPresenter = function( }).fail(function(response) { $el.empty(); - messagePresenter.showError($messages, response.json && response.json.error || response); + showGenericError(response); }); } @@ -57,23 +57,45 @@ App.Presenters.PostPresenter = function( postContentTemplate: postContentTemplate, privileges: privileges, })); + $messages = $el.find('.messages'); $el.find('.delete').click(deleteButtonClicked); + $el.find('.feature').click(featureButtonClicked); } function deleteButtonClicked(e) { e.preventDefault(); + messagePresenter.hideMessages($messages); if (window.confirm('Do you really want to delete this post?')) { deletePost(); } } function deletePost() { - api.delete('/posts/' + post.id).then(function(response) { - router.navigate('#/posts'); - }).fail(function(response) { - messagePresenter.showError($messages, response.json && response.json.error || response); - }); + api.delete('/posts/' + post.id) + .then(function(response) { + router.navigate('#/posts'); + }).fail(showGenericError); + } + + function featureButtonClicked(e) { + e.preventDefault(); + messagePresenter.hideMessages($messages); + if (window.confirm('Do you want to feature this post on fron page?')) { + featurePost(); + } + } + + function featurePost() { + api.post('/posts/' + post.id + '/feature') + .then(function(response) { + router.navigate('#/home'); + }) + .fail(showGenericError); + } + + function showGenericError(response) { + messagePresenter.showError($messages, response.json && response.json.error || response); } return { diff --git a/public_html/js/Presenters/TopNavigationPresenter.js b/public_html/js/Presenters/TopNavigationPresenter.js index 44d3b719..5b99f970 100644 --- a/public_html/js/Presenters/TopNavigationPresenter.js +++ b/public_html/js/Presenters/TopNavigationPresenter.js @@ -47,6 +47,10 @@ App.Presenters.TopNavigationPresenter = function( $el.find('li.' + selectedElement).find('a').addClass('active'); } + function getBaseTitle() { + return baseTitle; + } + function changeTitle(subTitle) { var newTitle = baseTitle; if (subTitle) { @@ -59,6 +63,7 @@ App.Presenters.TopNavigationPresenter = function( init: init, render: render, select: select, + getBaseTitle: getBaseTitle, changeTitle: changeTitle, }; diff --git a/public_html/templates/home.tpl b/public_html/templates/home.tpl new file mode 100644 index 00000000..4806bcad --- /dev/null +++ b/public_html/templates/home.tpl @@ -0,0 +1,30 @@ +
+

<%= title %>

+ + <% if (post) { %> +
+ <%= postContentTemplate({post: post}) %> + +
+ <% } %> +
diff --git a/public_html/templates/post-list-item.tpl b/public_html/templates/post-list-item.tpl index 24c48cfe..48d405c5 100644 --- a/public_html/templates/post-list-item.tpl +++ b/public_html/templates/post-list-item.tpl @@ -3,6 +3,6 @@ title="<%= _.map(post.tags, function(tag) { return '#' + tag.name; }).join(', ') %>" href="#/post/<%= post.id %>"> - @<%= post.id %> + <%= post.idMarkdown %> diff --git a/public_html/templates/post.tpl b/public_html/templates/post.tpl index 4a6cba7f..be6b0bfb 100644 --- a/public_html/templates/post.tpl +++ b/public_html/templates/post.tpl @@ -69,6 +69,13 @@ <% } %> + <% if (post.featureCount > 0) { %> +
  • + Featured: <%= post.featureCount %> <%= post.featureCount < 2 ? 'time' : 'times' %> + (<%= formatRelativeTime(post.lastFeatureTime) %>) +
  • + <% } %> + <% if (post.source) { %>
  • Source: