diff --git a/public/src/admin/manage/post-queue.js b/public/src/admin/manage/post-queue.js index 98abbbed32..0478947a0b 100644 --- a/public/src/admin/manage/post-queue.js +++ b/public/src/admin/manage/post-queue.js @@ -1,7 +1,7 @@ 'use strict'; -define('admin/manage/post-queue', function () { +define('admin/manage/post-queue', ['categorySelector'], function (categorySelector) { var PostQueue = {}; PostQueue.init = function () { @@ -24,6 +24,32 @@ define('admin/manage/post-queue', function () { handleContentEdit('.post-content', '.post-content-editable', 'textarea'); handleContentEdit('.topic-title', '.topic-title-editable', 'input'); + + $('.posts-list').on('click', '.topic-category[data-editable]', function () { + var $this = $(this); + var id = $this.parents('[data-id]').attr('data-id'); + categorySelector.modal(ajaxify.data.allCategories, function (cid) { + var category = ajaxify.data.allCategories.find(function (c) { + return parseInt(c.cid, 10) === parseInt(cid, 10); + }); + socket.emit('posts.editQueuedContent', { + id: id, + cid: cid, + }, function (err) { + if (err) { + return app.alertError(err.message); + } + app.parseAndTranslate('admin/manage/post-queue', 'posts', { + posts: [{ + category: category, + }], + }, function (html) { + $this.replaceWith(html.find('.topic-category')); + }); + }); + }); + return false; + }); }; function handleContentEdit(displayClass, editableClass, inputSelector) { diff --git a/src/controllers/mods.js b/src/controllers/mods.js index 9c35a87800..40e99e8d12 100644 --- a/src/controllers/mods.js +++ b/src/controllers/mods.js @@ -181,12 +181,17 @@ modsController.postQueue = async function (req, res, next) { const page = parseInt(req.query.page, 10) || 1; const postsPerPage = 20; - const [ids, isAdminOrGlobalMod, moderatedCids] = await Promise.all([ + const [ids, isAdminOrGlobalMod, moderatedCids, allCategories] = await Promise.all([ db.getSortedSetRange('post:queue', 0, -1), user.isAdminOrGlobalMod(req.uid), user.getModeratedCids(req.uid), + categories.buildForSelect(req.uid, 'find', ['disabled', 'link', 'slug']), ]); + allCategories.forEach((c) => { + c.disabledClass = !isAdminOrGlobalMod && !moderatedCids.includes(String(c.cid)); + }); + let postData = await getQueuedPosts(ids); postData = postData.filter(p => p && (isAdminOrGlobalMod || moderatedCids.includes(String(p.category.cid)))); @@ -198,6 +203,7 @@ modsController.postQueue = async function (req, res, next) { res.render('admin/manage/post-queue', { title: '[[pages:post-queue]]', posts: postData, + allCategories: allCategories, pagination: pagination.create(page, pageCount), breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:post-queue]]' }]), }); diff --git a/src/posts/queue.js b/src/posts/queue.js index 006634f557..8a54f49918 100644 --- a/src/posts/queue.js +++ b/src/posts/queue.js @@ -147,28 +147,31 @@ module.exports = function (Posts) { socketHelpers.notifyNew(data.uid, 'newPost', result); } - Posts.editQueuedContent = async function (uid, id, content, title) { - const canEditQueue = await Posts.canEditQueue(uid, id); + Posts.editQueuedContent = async function (uid, editData) { + const canEditQueue = await Posts.canEditQueue(uid, editData); if (!canEditQueue) { throw new Error('[[error:no-privileges]]'); } - const data = await getParsedObject(id); + const data = await getParsedObject(editData.id); if (!data) { return; } - if (content !== undefined) { - data.data.content = content; + if (editData.content !== undefined) { + data.data.content = editData.content; } - if (title !== undefined) { - data.data.title = title; + if (editData.title !== undefined) { + data.data.title = editData.title; } - await db.setObjectField('post:queue:' + id, 'data', JSON.stringify(data.data)); + if (editData.cid !== undefined) { + data.data.cid = editData.cid; + } + await db.setObjectField('post:queue:' + editData.id, 'data', JSON.stringify(data.data)); }; - Posts.canEditQueue = async function (uid, id) { + Posts.canEditQueue = async function (uid, editData) { const [isAdminOrGlobalMod, data] = await Promise.all([ user.isAdminOrGlobalMod(uid), - getParsedObject(id), + getParsedObject(editData.id), ]); if (!data) { return false; @@ -184,6 +187,11 @@ module.exports = function (Posts) { } else if (data.type === 'reply') { cid = await topics.getTopicField(data.data.tid, 'cid'); } - return await user.isModerator(uid, cid); + const isModerator = await user.isModerator(uid, cid); + let isModeratorOfTargetCid = true; + if (editData.cid) { + isModeratorOfTargetCid = await user.isModerator(uid, editData.cid); + } + return isModerator && isModeratorOfTargetCid; }; }; diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index 987a3586d5..83d0dfe86d 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -166,7 +166,7 @@ SocketPosts.reject = async function (socket, data) { }; async function acceptOrReject(method, socket, data) { - const canEditQueue = await posts.canEditQueue(socket.uid, data.id); + const canEditQueue = await posts.canEditQueue(socket.uid, data); if (!canEditQueue) { throw new Error('[[error:no-privileges]]'); } @@ -174,10 +174,10 @@ async function acceptOrReject(method, socket, data) { } SocketPosts.editQueuedContent = async function (socket, data) { - if (!data || !data.id || (!data.content && !data.title)) { + if (!data || !data.id || (!data.content && !data.title && !data.cid)) { throw new Error('[[error:invalid-data]]'); } - await posts.editQueuedContent(socket.uid, data.id, data.content, data.title); + await posts.editQueuedContent(socket.uid, data); if (data.content) { return await plugins.fireHook('filter:parse.post', { postData: data }); } diff --git a/src/views/admin/manage/post-queue.tpl b/src/views/admin/manage/post-queue.tpl index 086f13a7fb..73e0293e40 100644 --- a/src/views/admin/manage/post-queue.tpl +++ b/src/views/admin/manage/post-queue.tpl @@ -18,7 +18,7 @@ [[admin/manage/post-queue:user]] - [[admin/manage/post-queue:category]] + [[admin/manage/post-queue:category]] [[admin/manage/post-queue:title]] [[admin/manage/post-queue:content]] [[admin/manage/post-queue:posted]] @@ -35,8 +35,8 @@ {posts.user.username} - - {posts.category.name} + + {posts.category.name} diff --git a/src/views/admin/partials/categories/select-category.tpl b/src/views/admin/partials/categories/select-category.tpl index 5c37de80b0..7f8ed6c2b5 100644 --- a/src/views/admin/partials/categories/select-category.tpl +++ b/src/views/admin/partials/categories/select-category.tpl @@ -12,7 +12,7 @@ [[search:no-matches]] - diff --git a/test/posts.js b/test/posts.js index cf94181492..59a932f7fe 100644 --- a/test/posts.js +++ b/test/posts.js @@ -1096,6 +1096,21 @@ describe('Post\'s', function () { }); }); + it('should edit topic category in queue', function (done) { + socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: 2 }, function (err) { + assert.ifError(err); + request(nconf.get('url') + '/api/post-queue', { jar: jar, json: true }, function (err, res, body) { + assert.ifError(err); + assert.equal(body.posts[0].type, 'topic'); + assert.equal(body.posts[0].data.cid, 2); + socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: cid }, function (err) { + assert.ifError(err); + done(); + }); + }); + }); + }); + it('should prevent regular users from approving posts', function (done) { socketPosts.accept({ uid: uid }, { id: queueId }, function (err) { assert.equal(err.message, '[[error:no-privileges]]');