diff --git a/nodebb-theme-slackers/templates/partials/topic/post-menu-list.tpl b/nodebb-theme-slackers/templates/partials/topic/post-menu-list.tpl new file mode 100644 index 0000000000..ceb32ca8a1 --- /dev/null +++ b/nodebb-theme-slackers/templates/partials/topic/post-menu-list.tpl @@ -0,0 +1,146 @@ +{{{ if posts.display_moderator_tools }}} +
  • + + [[topic:edit]] + +
  • +
  • + + [[topic:delete]] + +
  • +
  • + + [[topic:restore]] + +
  • +{{{ if posts.display_purge_tools }}} +
  • + + [[topic:purge]] + +
  • +{{{ end }}} + +{{{ if posts.display_move_tools }}} +
  • + + [[topic:move]] + +
  • +{{{ end }}} + +{{{ if !posts.verify }}} +
  • + + Verify Message + +
  • +{{{ else }}} +
  • + + Un-Verify Message + +
  • +{{{ end }}} + +{{{ if posts.display_change_owner_tools }}} +
  • + + [[topic:change-owner]] + +
  • +{{{ end }}} + +{{{ if posts.ip }}} +
  • + + [[topic:copy-ip]] {posts.ip} + +
  • +{{{ if posts.display_ip_ban }}} +
  • + + [[topic:ban-ip]] {posts.ip} + +
  • +{{{ end }}} +{{{ end }}} +{{{ end }}} + +{{{ each posts.tools }}} +
  • + + {{./html}} + +
  • +{{{ end }}} + +{{{ if !posts.deleted }}} + {{{ if posts.display_history}}} +
  • + + [[topic:view-history]] + +
  • + {{{ end }}} + + {{{ if config.loggedIn }}} +
  • + + + + + + [[topic:bookmark]] + {posts.bookmarks}  + +
  • + {{{ end }}} + +
  • + + [[topic:copy-permalink]] + +
  • + + {{{ if postSharing.length }}} + {{{ if config.loggedIn }}}{{{ end }}} + + {{{ end }}} +
  • + {{{ each postSharing }}} + + {{{ end }}} +
  • +{{{ end }}} + +{{{ if posts.display_flag_tools }}} + + +
  • + [[topic:flag-post]] +
  • +
  • + [[topic:already-flagged]] +
  • + +{{{ if (!posts.selfPost && posts.uid) }}} +
  • + [[topic:flag-user]] +
  • +{{{ end }}} +{{{ end }}} + +{{{ if posts.display_moderator_tools }}} +{{{ if posts.flags.exists }}} +
  • + [[topic:view-flag-report]] +
  • +{{{ if (posts.flags.state == "open") }}} +
  • + [[topic:resolve-flag]] +
  • +{{{ end }}} +{{{ end }}} +{{{ end }}} diff --git a/nodebb-theme-slackers/templates/partials/topic/post.tpl b/nodebb-theme-slackers/templates/partials/topic/post.tpl new file mode 100644 index 0000000000..c8c3309831 --- /dev/null +++ b/nodebb-theme-slackers/templates/partials/topic/post.tpl @@ -0,0 +1,130 @@ +{{{ if (!./index && widgets.mainpost-header.length) }}} +
    + {{{ each widgets.mainpost-header }}} + {widgets.mainpost-header.html} + {{{ end }}} +
    +{{{ end }}} +
    +
    + + {buildAvatar(posts.user, "48px", true, "", "user/picture")} + [[global:{posts.user.status}]] + +
    +
    + + +
    + {posts.content} +
    +
    +
    + +
    + {{{ if posts.user.signature }}} +
    {posts.user.signature}
    + {{{ end }}} + +
    + {{{ if !hideReplies }}} + + + {{{each posts.replies.users}}} + {buildAvatar(posts.replies.users, "20px", true, "avatar-tooltip")} + {{{end}}} + {{{ if posts.replies.hasMore}}} + + {{{ end }}} + + + {posts.replies.text} + + + + + {{{ end }}} +
    + +
    + +
    + + + + + {{{ if !reputation:disabled }}} +
    + + + + + + + {posts.votes} + + {{{ if !downvote:disabled }}} + + + + {{{ end }}} +
    + {{{ end }}} + + +
    +
    +{{{ if (!./index && widgets.mainpost-footer.length) }}} +
    + {{{ each widgets.mainpost-footer }}} + {widgets.mainpost-footer.html} + {{{ end }}} +
    +{{{ end }}} \ No newline at end of file diff --git a/public/openapi/components/schemas/PostObject.yaml b/public/openapi/components/schemas/PostObject.yaml index f49297e938..88b56b1c04 100644 --- a/public/openapi/components/schemas/PostObject.yaml +++ b/public/openapi/components/schemas/PostObject.yaml @@ -16,6 +16,8 @@ PostObject: type: number deleted: type: boolean + verify: + type: number answered: type: number upvotes: @@ -141,4 +143,4 @@ PostObject: isMainPost: type: boolean replies: - type: number \ No newline at end of file + type: number diff --git a/public/openapi/read/topic/topic_id.yaml b/public/openapi/read/topic/topic_id.yaml index 977eef98ab..fd4412dc2a 100644 --- a/public/openapi/read/topic/topic_id.yaml +++ b/public/openapi/read/topic/topic_id.yaml @@ -67,6 +67,10 @@ get: type: number deleted: type: number + verify: + type: number + verifyUid: + type: number answered: type: number upvotes: @@ -447,4 +451,4 @@ get: - tid - $ref: ../../components/schemas/Pagination.yaml#/Pagination - $ref: ../../components/schemas/Breadcrumbs.yaml#/Breadcrumbs - - $ref: ../../components/schemas/CommonProps.yaml#/CommonProps \ No newline at end of file + - $ref: ../../components/schemas/CommonProps.yaml#/CommonProps diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index c59b9bce29..aea404e425 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -186,6 +186,8 @@ paths: $ref: 'write/posts/pid/upvoters.yaml' /posts/{pid}/bookmark: $ref: 'write/posts/pid/bookmark.yaml' + /posts/{pid}/verify: + $ref: 'write/posts/pid/verify.yaml' /posts/{pid}/diffs: $ref: 'write/posts/pid/diffs.yaml' /posts/{pid}/diffs/{since}: @@ -261,4 +263,4 @@ paths: /files/: $ref: 'write/files.yaml' /files/folder: - $ref: 'write/files/folder.yaml' \ No newline at end of file + $ref: 'write/files/folder.yaml' diff --git a/public/openapi/write/posts/pid.yaml b/public/openapi/write/posts/pid.yaml index 8ba44a033a..89df2d6b0d 100644 --- a/public/openapi/write/posts/pid.yaml +++ b/public/openapi/write/posts/pid.yaml @@ -40,6 +40,10 @@ get: type: number deleted: type: number + verify: + type: number + verifyUid: + type: number answered: type: number upvotes: @@ -142,4 +146,4 @@ delete: $ref: ../../components/schemas/Status.yaml#/Status response: type: object - properties: {} \ No newline at end of file + properties: {} diff --git a/public/openapi/write/posts/pid/replies.yaml b/public/openapi/write/posts/pid/replies.yaml index 78042a6afd..99f48aa325 100644 --- a/public/openapi/write/posts/pid/replies.yaml +++ b/public/openapi/write/posts/pid/replies.yaml @@ -45,6 +45,8 @@ get: type: number deleted: type: number + verify: + type: number answered: type: number upvotes: diff --git a/public/openapi/write/posts/pid/verify.yaml b/public/openapi/write/posts/pid/verify.yaml new file mode 100644 index 0000000000..d5f6c8517f --- /dev/null +++ b/public/openapi/write/posts/pid/verify.yaml @@ -0,0 +1,26 @@ +put: + tags: + - posts + summary: verifies a post + description: This operation verifies a post. + parameters: + - in: path + name: pid + schema: + type: string + required: true + description: a valid post id + example: 2 + responses: + '200': + description: Post successfully verified + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../components/schemas/Status.yaml#/Status + response: + type: object + properties: {} diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index 2f52e25446..8894d62358 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -40,6 +40,9 @@ define('forum/topic/events', [ 'posts.bookmark': togglePostBookmark, 'posts.unbookmark': togglePostBookmark, + 'posts.verify': toggleVerifyMessage, + 'posts.unverify': toggleVerifyMessage, + 'posts.answer': toggleAnswered, 'posts.unanswer': toggleAnswered, @@ -68,6 +71,10 @@ define('forum/topic/events', [ } }; + function toggleVerifyMessage(data) { + console.log(data); + } + function onUserStatusChange(data) { app.updateUserStatus($('[data-uid="' + data.uid + '"] [component="user/status"]'), data.status); } diff --git a/public/src/client/topic/postTools.js b/public/src/client/topic/postTools.js index f8d2ca8933..d32ab12fac 100644 --- a/public/src/client/topic/postTools.js +++ b/public/src/client/topic/postTools.js @@ -129,6 +129,10 @@ define('forum/topic/postTools', [ return bookmarkPost($(this), getData($(this), 'data-pid')); }); + postContainer.on('click', '[component="post/verify"]', function () { + return verifyMessage($(this)); + }); + postContainer.on('click', '[component="post/upvote"]', function () { return votes.toggleVote($(this), '.upvoted', 1); }); @@ -404,6 +408,14 @@ define('forum/topic/postTools', [ postAction(action, pid); } + async function verifyMessage(button) { + const pid = getData(button, 'data-pid'); + const method = 'put'; + const action = 'verify'; + console.log('trying to call api'); + api[method](`/posts/${pid}/${action}`).catch(alerts.error); + } + function purgePost(button) { postAction('purge', getData(button, 'data-pid')); } diff --git a/src/api/posts.js b/src/api/posts.js index 80ff4360a8..44c4ca7a03 100644 --- a/src/api/posts.js +++ b/src/api/posts.js @@ -421,6 +421,14 @@ postsAPI.unbookmark = async function (caller, data) { return await apiHelpers.postCommand(caller, 'unbookmark', 'bookmarked', '', data); }; +postsAPI.verify = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'verify', 'verify', '', data); +}; + +postsAPI.unverify = async function (caller, data) { + return await apiHelpers.postCommand(caller, 'unverify', 'unverify', '', data); +}; + async function diffsPrivilegeCheck(pid, uid) { const [deleted, privilegesData] = await Promise.all([ posts.getPostField(pid, 'deleted'), diff --git a/src/controllers/write/posts.js b/src/controllers/write/posts.js index 5e592fdeb8..fc58594ffc 100644 --- a/src/controllers/write/posts.js +++ b/src/controllers/write/posts.js @@ -153,6 +153,18 @@ Posts.unbookmark = async (req, res) => { helpers.formatApiResponse(200, res); }; +Posts.verify = async (req, res) => { + const data = await mock(req); + await api.posts.verify(req, data); + helpers.formatApiResponse(200, res); +}; + +Posts.unverify = async (req, res) => { + const data = await mock(req); + await api.posts.unverify(req, data); + helpers.formatApiResponse(200, res); +}; + Posts.answer = async (req, res) => { const data = await mock(req); await api.posts.answer(req, data); @@ -162,6 +174,7 @@ Posts.answer = async (req, res) => { Posts.unanswer = async (req, res) => { const data = await mock(req); await api.posts.unanswer(req, data); + helpers.formatApiResponse(200, res); }; diff --git a/src/posts/data.js b/src/posts/data.js index b2bcb8555c..1889c5d2d5 100644 --- a/src/posts/data.js +++ b/src/posts/data.js @@ -7,7 +7,8 @@ const utils = require('../utils'); const intFields = [ 'uid', 'pid', 'tid', 'deleted', 'timestamp', 'upvotes', 'downvotes', 'deleterUid', 'edited', - 'replies', 'bookmarks', 'answered', 'answererUid', + 'replies', 'bookmarks', 'verify', 'verifyUid', + 'answered', 'answererUid', ]; module.exports = function (Posts) { diff --git a/src/posts/index.js b/src/posts/index.js index 9a055ead2c..3dc9e4ddd2 100644 --- a/src/posts/index.js +++ b/src/posts/index.js @@ -25,6 +25,7 @@ require('./votes')(Posts); require('./bookmarks')(Posts); require('./answer')(Posts); require('./queue')(Posts); +require('./verify')(Posts); require('./diffs')(Posts); require('./uploads')(Posts); diff --git a/src/posts/summary.js b/src/posts/summary.js index f27c8c56b1..927f071ee8 100644 --- a/src/posts/summary.js +++ b/src/posts/summary.js @@ -20,7 +20,8 @@ module.exports = function (Posts) { options.parse = options.hasOwnProperty('parse') ? options.parse : true; options.extraFields = options.hasOwnProperty('extraFields') ? options.extraFields : []; - const fields = ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle', 'answered'].concat(options.extraFields); + + const fields = ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle', 'answered', 'verify'].concat(options.extraFields); let posts = await Posts.getPostsFields(pids, fields); posts = posts.filter(Boolean); diff --git a/src/posts/verify.js b/src/posts/verify.js new file mode 100644 index 0000000000..08f1ab621f --- /dev/null +++ b/src/posts/verify.js @@ -0,0 +1,27 @@ +'use strict'; + +const plugins = require('../plugins'); + +module.exports = function (Posts) { + Posts.verify = async function (pid, uid) { + return await toggleVerify('verify', pid, uid); + }; + + Posts.unverify = async function (pid, uid) { + return await toggleVerify('unverify', pid, uid); + }; + + async function toggleVerify(type, pid, uid) { + const isVerifying = type === 'verify'; + await plugins.hooks.fire(`filter:post.${type}`, { pid: pid, uid: uid }); + + await Posts.setPostFields(pid, { + verify: isVerifying ? 1 : 0, + verifyUid: isVerifying ? uid : 0, + }); + + const postData = await Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp']); + + return postData; + } +}; diff --git a/src/routes/write/posts.js b/src/routes/write/posts.js index e573bbb9b0..29d1520ef7 100644 --- a/src/routes/write/posts.js +++ b/src/routes/write/posts.js @@ -32,6 +32,9 @@ module.exports = function () { setupApiRoute(router, 'put', '/:pid/bookmark', middlewares, controllers.write.posts.bookmark); setupApiRoute(router, 'delete', '/:pid/bookmark', middlewares, controllers.write.posts.unbookmark); + setupApiRoute(router, 'put', '/:pid/verify', middlewares, controllers.write.posts.verify); + setupApiRoute(router, 'put', '/:pid/verify', middlewares, controllers.write.posts.unverify); + setupApiRoute(router, 'get', '/:pid/diffs', [middleware.assert.post], controllers.write.posts.getDiffs); setupApiRoute(router, 'get', '/:pid/diffs/:since', [middleware.assert.post], controllers.write.posts.loadDiff); setupApiRoute(router, 'put', '/:pid/diffs/:since', middlewares, controllers.write.posts.restoreDiff); diff --git a/test/posts.js b/test/posts.js index 4b78fa378f..063c341b96 100644 --- a/test/posts.js +++ b/test/posts.js @@ -281,10 +281,19 @@ describe('Post\'s', () => { }); }); + describe('verifying', async () => { + it('should mark post verified', async () => { + await apiPosts.verify({ uid: voterUid }, { pid: postData.pid, room_id: `topic_${postData.tid}` }); + const isVerified = await posts.getPostField(postData.pid, 'verify'); + assert.strictEqual(isVerified, 1); + }); + }); + describe('answering', async () => { it('should mark post answered', async () => { const data = await apiPosts.answer({ uid: voterUid }, { pid: postData.pid, room_id: `topic_${postData.tid}` }); const isAnswered = await posts.getPostField(postData.pid, 'answered'); + assert.strictEqual(isAnswered, 1); }); });