From e80400665e52a5e6e219eee7e6c99bb7fdd54a10 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 13 Oct 2016 13:12:38 -0400 Subject: [PATCH] closes #5111 --- src/controllers/admin/flags.js | 17 +++++++----- src/controllers/globalmods.js | 11 -------- src/controllers/index.js | 1 + src/controllers/mods.js | 27 +++++++++++++++++++ src/posts/category.js | 32 +++++++++++++++++------ src/routes/index.js | 6 ++++- src/socket.io/posts/flag.js | 11 +++++--- src/user.js | 47 ++++++++++++++++++++++++++++++++++ 8 files changed, 123 insertions(+), 29 deletions(-) create mode 100644 src/controllers/mods.js diff --git a/src/controllers/admin/flags.js b/src/controllers/admin/flags.js index b5edc7b5be..4334aef0c3 100644 --- a/src/controllers/admin/flags.js +++ b/src/controllers/admin/flags.js @@ -24,14 +24,12 @@ flagsController.get = function(req, res, next) { categories.buildForSelect(req.uid, next); }, flagData: function(next) { - getFlagData(req, next); + getFlagData(req, res, next); }, analytics: function(next) { analytics.getDailyStatsForSet('analytics:flags', Date.now(), 30, next); }, - assignees: function(next) { - user.getAdminsandGlobalMods(next); - } + assignees: async.apply(user.getAdminsandGlobalModsandModerators) }, function (err, results) { if (err) { return next(err); @@ -45,6 +43,13 @@ flagsController.get = function(req, res, next) { }; }); + // If res.locals.cids is populated, then slim down the categories list + if (res.locals.cids) { + results.categories = results.categories.filter(function(category) { + return res.locals.cids.indexOf(String(category.cid)) !== -1; + }); + } + var pageCount = Math.max(1, Math.ceil(results.flagData.count / itemsPerPage)); results.categories.forEach(function(category) { @@ -66,10 +71,10 @@ flagsController.get = function(req, res, next) { }); }; -function getFlagData(req, callback) { +function getFlagData(req, res, callback) { var sortBy = req.query.sortBy || 'count'; var byUsername = req.query.byUsername || ''; - var cid = req.query.cid || 0; + var cid = req.query.cid || res.locals.cids || 0; var page = parseInt(req.query.page, 10) || 1; var start = (page - 1) * itemsPerPage; var stop = start + itemsPerPage - 1; diff --git a/src/controllers/globalmods.js b/src/controllers/globalmods.js index 3275c7929e..a5628f5a4d 100644 --- a/src/controllers/globalmods.js +++ b/src/controllers/globalmods.js @@ -1,21 +1,10 @@ "use strict"; var user = require('../user'); -var adminFlagsController = require('./admin/flags'); var adminBlacklistController = require('./admin/blacklist'); var globalModsController = {}; -globalModsController.flagged = function(req, res, next) { - user.isAdminOrGlobalMod(req.uid, function(err, isAdminOrGlobalMod) { - if (err || !isAdminOrGlobalMod) { - return next(err); - } - - adminFlagsController.get(req, res, next); - }); -}; - globalModsController.ipBlacklist = function(req, res, next) { user.isAdminOrGlobalMod(req.uid, function(err, isAdminOrGlobalMod) { if (err || !isAdminOrGlobalMod) { diff --git a/src/controllers/index.js b/src/controllers/index.js index 103dbf70f3..6e18283d5e 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -27,6 +27,7 @@ var Controllers = { api: require('./api'), admin: require('./admin'), globalMods: require('./globalmods'), + mods: require('./mods'), sitemap: require('./sitemap') }; diff --git a/src/controllers/mods.js b/src/controllers/mods.js new file mode 100644 index 0000000000..e319c90086 --- /dev/null +++ b/src/controllers/mods.js @@ -0,0 +1,27 @@ +"use strict"; + +var async = require('async'); + +var user = require('../user'); +var adminFlagsController = require('./admin/flags'); + +var modsController = {}; + +modsController.flagged = function(req, res, next) { + async.parallel([ + async.apply(user.isAdminOrGlobalMod, req.uid), + async.apply(user.isModeratorOfAnyCategory, req.uid) + ], function(err, results) { + if (err || !(results[0] || results[1])) { + return next(err); + } + + if (!results[0] && results[1]) { + res.locals.cids = results[1]; + } + + adminFlagsController.get(req, res, next); + }); +}; + +module.exports = modsController; diff --git a/src/posts/category.js b/src/posts/category.js index 0f9ee7fd67..64d3615805 100644 --- a/src/posts/category.js +++ b/src/posts/category.js @@ -2,6 +2,7 @@ 'use strict'; var async = require('async'); +var _ = require('underscore'); var db = require('../database'); var topics = require('../topics'); @@ -56,14 +57,29 @@ module.exports = function(Posts) { if (!cid) { return callback(null, pids); } - db.isSortedSetMembers('cid:' + cid + ':pids', pids, function(err, isMembers) { - if (err) { - return callback(err); - } - pids = pids.filter(function(pid, index) { - return pid && isMembers[index]; + + if (!Array.isArray(cid) || cid.length === 1) { + // Single cid + db.isSortedSetMembers('cid:' + parseInt(cid, 10) + ':pids', pids, function(err, isMembers) { + if (err) { + return callback(err); + } + pids = pids.filter(function(pid, index) { + return pid && isMembers[index]; + }); + callback(null, pids); }); - callback(null, pids); - }); + } else { + // Multiple cids + async.map(cid, function(cid, next) { + Posts.filterPidsByCid(pids, cid, next); + }, function(err, pidsArr) { + if (err) { + return callback(err); + } + + callback(null, _.union.apply(_, pidsArr)); + }); + } }; }; \ No newline at end of file diff --git a/src/routes/index.js b/src/routes/index.js index 659f4132ae..6325ec322c 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -38,9 +38,12 @@ function mainRoutes(app, middleware, controllers) { app.get('/sping', controllers.ping); } +function modRoutes(app, middleware, controllers) { + setupPageRoute(app, '/posts/flags', middleware, [], controllers.mods.flagged); +} + function globalModRoutes(app, middleware, controllers) { setupPageRoute(app, '/ip-blacklist', middleware, [], controllers.globalMods.ipBlacklist); - setupPageRoute(app, '/posts/flags', middleware, [], controllers.globalMods.flagged); } function topicRoutes(app, middleware, controllers) { @@ -123,6 +126,7 @@ module.exports = function(app, middleware, hotswapIds) { mainRoutes(router, middleware, controllers); topicRoutes(router, middleware, controllers); postRoutes(router, middleware, controllers); + modRoutes(router, middleware, controllers); globalModRoutes(router, middleware, controllers); tagRoutes(router, middleware, controllers); categoryRoutes(router, middleware, controllers); diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js index 583de733fb..8315782ee8 100644 --- a/src/socket.io/posts/flag.js +++ b/src/socket.io/posts/flag.js @@ -145,10 +145,15 @@ module.exports = function(SocketPosts) { async.waterfall([ function (next) { - user.isAdminOrGlobalMod(socket.uid, next); + async.parallel([ + async.apply(user.isAdminOrGlobalMod, socket.uid), + async.apply(user.isModeratorOfAnyCategory, socket.uid) + ], function(err, results) { + next(err, results[0] || results[1]); + }); }, - function (isAdminOrGlobalModerator, next) { - if (!isAdminOrGlobalModerator) { + function (allowed, next) { + if (!allowed) { return next(new Error('[[no-privileges]]')); } diff --git a/src/user.js b/src/user.js index 40aefeaaf2..1c7b2f772a 100644 --- a/src/user.js +++ b/src/user.js @@ -1,6 +1,7 @@ 'use strict'; var async = require('async'); +var _ = require('underscore'); var groups = require('./groups'); var plugins = require('./plugins'); @@ -233,6 +234,19 @@ var meta = require('./meta'); privileges.users.isModerator(uid, cid, callback); }; + User.isModeratorOfAnyCategory = function(uid, callback) { + // Checks all active categories and determines whether passed-in uid is a mod of any of them + db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { + async.filter(cids, function(cid, next) { + User.isModerator(uid, cid, function(err, isMod) { + next(!!isMod); + }); + }, function(result) { + callback(err, result); + }); + }); + }; + User.isAdministrator = function(uid, callback) { privileges.users.isAdministrator(uid, callback); }; @@ -277,6 +291,39 @@ var meta = require('./meta'); }); }; + User.getAdminsandGlobalModsandModerators = function(callback) { + async.parallel([ + async.apply(groups.getMembers, 'administrators', 0, -1), + async.apply(groups.getMembers, 'Global Moderators', 0, -1), + async.apply(User.getModeratorUids) + ], function(err, results) { + if (err) { + return callback(err); + } + + User.getUsersData(_.union.apply(_, results), callback); + }); + }; + + User.getModeratorUids = function(callback) { + async.waterfall([ + async.apply(db.getSortedSetRange, 'categories:cid', 0, -1), + function(cids, next) { + var groupNames = cids.map(function(cid) { + return 'cid:' + cid + ':privileges:mods'; + }); + + groups.getMembersOfGroups(groupNames, function(err, memberSets) { + if (err) { + return next(err); + } + + next(null, _.union.apply(_, memberSets)); + }); + } + ], callback); + }; + User.addInterstitials = function(callback) { plugins.registerHook('core', { hook: 'filter:register.interstitial',