diff --git a/src/privileges/categories.js b/src/privileges/categories.js index feb3c53bc6..4ae73274bc 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -183,6 +183,17 @@ module.exports = function(privileges) { }); }; + privileges.categories.isAdminOrMod = function(cid, uid, callback) { + helpers.some([ + function (next) { + user.isModerator(uid, cid, next); + }, + function (next) { + user.isAdministrator(uid, next); + } + ], callback); + }; + privileges.categories.can = function(privilege, cid, uid, callback) { if (!cid) { return callback(null, false); diff --git a/src/privileges/topics.js b/src/privileges/topics.js index c61764fe8e..da5a61271b 100644 --- a/src/privileges/topics.js +++ b/src/privileges/topics.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'), + winston = require('winston'), db = require('../database'), topics = require('../topics'), @@ -170,21 +171,27 @@ module.exports = function(privileges) { }; privileges.topics.canEdit = function(tid, uid, callback) { + winston.warn('[deprecated] please use privileges.topics.isOwnerOrAdminOrMod'); + privileges.topics.isOwnerOrAdminOrMod(tid, uid, callback); + }; + + privileges.topics.isOwnerOrAdminOrMod = function(tid, uid, callback) { helpers.some([ function(next) { topics.isOwner(tid, uid, next); }, function(next) { - isAdminOrMod(tid, uid, next); + privileges.topics.isAdminOrMod(tid, uid, next); } ], callback); }; privileges.topics.canMove = function(tid, uid, callback) { - isAdminOrMod(tid, uid, callback); + winston.warn('[deprecated] please use privileges.topics.isAdminOrMod'); + privileges.topics.isAdminOrMod(tid, uid, callback); }; - function isAdminOrMod(tid, uid, callback) { + privileges.topics.isAdminOrMod = function(tid, uid, callback) { helpers.some([ function(next) { topics.getTopicField(tid, 'cid', function(err, cid) { @@ -198,5 +205,5 @@ module.exports = function(privileges) { user.isAdministrator(uid, next); } ], callback); - } + }; }; diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index 1137422913..5d9fbed84f 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -234,42 +234,33 @@ SocketTopics.doTopicAction = function(action, event, socket, data, callback) { if (!socket.uid) { return; } - if(!data || !Array.isArray(data.tids) || !data.cid) { + + if (!data || !Array.isArray(data.tids) || !data.cid) { return callback(new Error('[[error:invalid-tid]]')); } + if (typeof threadTools[action] !== 'function') { + return callback(); + } + async.each(data.tids, function(tid, next) { - privileges.topics.canEdit(tid, socket.uid, function(err, canEdit) { + threadTools[action](tid, socket.uid, function(err, data) { if (err) { return next(err); } - if (!canEdit) { - return next(new Error('[[error:no-privileges]]')); + emitToTopicAndCategory(event, data); + + if (action === 'delete' || action === 'restore' || action === 'purge') { + events.log({ + type: 'topic-' + action, + uid: socket.uid, + ip: socket.ip, + tid: tid + }); } - if (typeof threadTools[action] !== 'function') { - return next(); - } - - threadTools[action](tid, socket.uid, function(err, data) { - if (err) { - return next(err); - } - - emitToTopicAndCategory(event, data); - - if (action === 'delete' || action === 'restore' || action === 'purge') { - events.log({ - type: 'topic-' + action, - uid: socket.uid, - ip: socket.ip, - tid: tid - }); - } - - next(); - }); + next(); }); }, callback); }; @@ -325,7 +316,7 @@ SocketTopics.move = function(socket, data, callback) { var topicData; async.waterfall([ function(next) { - privileges.topics.canMove(tid, socket.uid, next); + privileges.topics.isAdminOrMod(tid, socket.uid, next); }, function(canMove, next) { if (!canMove) { diff --git a/src/threadTools.js b/src/threadTools.js index 9e3bb2e53d..bbf78e1054 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -7,7 +7,8 @@ var async = require('async'), categories = require('./categories'), posts = require('./posts'), plugins = require('./plugins'), - batch = require('./batch'); + batch = require('./batch'), + privileges = require('./privileges'); (function(ThreadTools) { @@ -21,21 +22,29 @@ var async = require('async'), }; function toggleDelete(tid, uid, isDelete, callback) { - topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], function(err, topicData) { - if (err) { - return callback(err); - } - - if (parseInt(topicData.deleted, 10) === 1 && isDelete) { - return callback(new Error('[[error:topic-already-deleted]]')); - } else if(parseInt(topicData.deleted, 10) !== 1 && !isDelete) { - return callback(new Error('[[error:topic-already-restored]]')); - } - - topics[isDelete ? 'delete' : 'restore'](tid, function(err) { - if (err) { - return callback(err); + var topicData; + async.waterfall([ + function (next) { + privileges.topics.isOwnerOrAdminOrMod(tid, uid, next); + }, + function (isOwnerOrAdminOrMod, next) { + if (!isOwnerOrAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); } + topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], next); + }, + function (_topicData, next) { + topicData = _topicData; + + if (parseInt(topicData.deleted, 10) === 1 && isDelete) { + return callback(new Error('[[error:topic-already-deleted]]')); + } else if(parseInt(topicData.deleted, 10) !== 1 && !isDelete) { + return callback(new Error('[[error:topic-already-restored]]')); + } + + topics[isDelete ? 'delete' : 'restore'](tid, next); + }, + function (next) { topicData.deleted = isDelete ? 1 : 0; if (isDelete) { @@ -52,8 +61,8 @@ var async = require('async'), }; callback(null, data); - }); - }); + } + ], callback); } ThreadTools.purge = function(tid, uid, callback) { @@ -66,21 +75,29 @@ var async = require('async'), if (!exists) { return callback(); } + privileges.topics.isOwnerOrAdminOrMod(tid, uid, next); + }, + function (isOwnerOrAdminOrMod, next) { + if (!isOwnerOrAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } + + topics.getTopicFields(tid, ['mainPid', 'cid'], next); + }, + function (_topic, next) { + topic = _topic; + batch.processSortedSet('tid:' + tid + ':posts', function(pids, next) { async.eachLimit(pids, 10, posts.purge, next); }, {alwaysStartAt: 0}, next); }, - function(next) { - topics.getTopicFields(tid, ['mainPid', 'cid'], next); - }, - function(_topic, next) { - topic = _topic; + function (next) { posts.purge(topic.mainPid, next); }, - function(next) { + function (next) { topics.purge(tid, next); }, - function(next) { + function (next) { next(null, {tid: tid, cid: topic.cid, uid: uid}); } ], callback); @@ -96,24 +113,40 @@ var async = require('async'), function toggleLock(tid, uid, lock, callback) { callback = callback || function() {}; - topics.getTopicField(tid, 'cid', function(err, cid) { - if (err) { - return callback(err); + + var cid; + + async.waterfall([ + function (next) { + topics.getTopicField(tid, 'cid', next); + }, + function (_cid, next) { + cid = _cid; + if (!cid) { + return next(new Error('[[error:no-topic]]')); + } + privileges.categories.isAdminOrMod(cid, uid, next); + }, + function (isAdminOrMod, next) { + if (!isAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } + + topics.setTopicField(tid, 'locked', lock ? 1 : 0, next); + }, + function (next) { + var data = { + tid: tid, + isLocked: lock, + uid: uid, + cid: cid + }; + + plugins.fireHook('action:topic.lock', data); + + next(null, data); } - - topics.setTopicField(tid, 'locked', lock ? 1 : 0); - - var data = { - tid: tid, - isLocked: lock, - uid: uid, - cid: cid - }; - - plugins.fireHook('action:topic.lock', data); - - callback(null, data); - }); + ], callback); } ThreadTools.pin = function(tid, uid, callback) { @@ -127,11 +160,23 @@ var async = require('async'), function togglePin(tid, uid, pin, callback) { var topicData; async.waterfall([ - function(next) { + function (next) { + topics.exists(tid, next); + }, + function (exists, next) { + if (!exists) { + return callback(); + } topics.getTopicFields(tid, ['cid', 'lastposttime'], next); }, - function(_topicData, next) { + function (_topicData, next) { topicData = _topicData; + privileges.categories.isAdminOrMod(_topicData.cid, uid, next); + }, + function(isAdminOrMod, next) { + if (!isAdminOrMod) { + return next(new Error('[[error:no-privileges]]')); + } async.parallel([ async.apply(topics.setTopicField, tid, 'pinned', pin ? 1 : 0), async.apply(db.sortedSetAdd, 'cid:' + topicData.cid + ':tids', pin ? Math.pow(2, 53) : topicData.lastposttime, tid)