diff --git a/install/data/defaults.json b/install/data/defaults.json index e9dca26896..194283b400 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -21,6 +21,7 @@ "chatMessageDelay": 200, "newbiePostDelayThreshold": 3, "postQueueReputationThreshold": 0, + "groupsExemptFromPostQueue": ["administrators", "Global Moderators"], "minimumPostLength": 8, "maximumPostLength": 32767, "minimumTagsPerTopic": 0, diff --git a/public/language/en-GB/admin/settings/post.json b/public/language/en-GB/admin/settings/post.json index b10ebe2c35..096b69026f 100644 --- a/public/language/en-GB/admin/settings/post.json +++ b/public/language/en-GB/admin/settings/post.json @@ -12,6 +12,7 @@ "restrictions-new": "New User Restrictions", "restrictions.post-queue": "Enable post queue", "restrictions.post-queue-rep-threshold": "Reputation required to bypass post queue", + "restrictions.groups-exempt-from-post-queue": "Select groups that should be exempt from the post queue", "restrictions-new.post-queue": "Enable new user restrictions", "restrictions.post-queue-help": "Enabling post queue will put the posts of new users in a queue for approval", "restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users", diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index fee6c423c7..461ef014f7 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -3,6 +3,7 @@ const meta = require('../../meta'); const emailer = require('../../emailer'); const notifications = require('../../notifications'); +const groups = require('../../groups'); const settingsController = module.exports; @@ -13,6 +14,8 @@ settingsController.get = async function (req, res, next) { await renderEmail(req, res, next); } else if (term === 'user') { await renderUser(req, res, next); + } else if (term === 'post') { + await renderPost(req, res, next); } else { res.render('admin/settings/' + term); } @@ -41,3 +44,10 @@ async function renderUser(req, res) { notificationSettings: notificationSettings, }); } + +async function renderPost(req, res) { + const groupData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1); + res.render('admin/settings/post', { + groupsExemptFromPostQueue: groupData, + }); +} diff --git a/src/groups/update.js b/src/groups/update.js index 2853ba705c..cc30cb8775 100644 --- a/src/groups/update.js +++ b/src/groups/update.js @@ -7,6 +7,7 @@ const utils = require('../utils'); const db = require('../database'); const user = require('../user'); const batch = require('../batch'); +const meta = require('../meta'); module.exports = function (Groups) { @@ -164,6 +165,7 @@ module.exports = function (Groups) { await updateMemberGroupTitles(oldName, newName); await updateNavigationItems(oldName, newName); await updateWidgets(oldName, newName); + await updateConfig(oldName, newName); await db.setObject('group:' + oldName, { name: newName, slug: utils.slugify(newName) }); await db.deleteObjectField('groupslug:groupname', group.slug); await db.setObjectField('groupslug:groupname', utils.slugify(newName), newName); @@ -245,4 +247,11 @@ module.exports = function (Groups) { } } } + + async function updateConfig(oldName, newName) { + if (meta.config.groupsExemptFromPostQueue.includes(oldName)) { + meta.config.groupsExemptFromPostQueue.splice(meta.config.groupsExemptFromPostQueue.indexOf(oldName), 1, newName); + await meta.configs.set('groupsExemptFromPostQueue', meta.config.groupsExemptFromPostQueue); + } + } }; diff --git a/src/meta/configs.js b/src/meta/configs.js index 9a61488760..0973501ca9 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -16,6 +16,7 @@ const Configs = module.exports; Meta.config = {}; +// called after data is loaded from db function deserialize(config) { const deserialized = {}; Object.keys(config).forEach(function (key) { @@ -39,6 +40,13 @@ function deserialize(config) { deserialized[key] = defaults[key]; } else if (defaultType === 'undefined' && !isNaN(number) && isFinite(config[key])) { deserialized[key] = number; + } else if (Array.isArray(defaults[key]) && !Array.isArray(config[key])) { + try { + deserialized[key] = JSON.parse(config[key] || '[]'); + } catch (err) { + winston.error(err); + deserialized[key] = defaults[key]; + } } else { deserialized[key] = config[key]; } @@ -46,7 +54,37 @@ function deserialize(config) { return deserialized; } +// called before data is saved to db +function serialize(config) { + const serialized = {}; + Object.keys(config).forEach(function (key) { + const defaultType = typeof defaults[key]; + const type = typeof config[key]; + const number = parseFloat(config[key]); + + if (defaultType === 'string' && type === 'number') { + serialized[key] = String(config[key]); + } else if (defaultType === 'number' && type === 'string') { + if (!isNaN(number) && isFinite(config[key])) { + serialized[key] = number; + } else { + serialized[key] = defaults[key]; + } + } else if (config[key] === null) { + serialized[key] = defaults[key]; + } else if (defaultType === 'undefined' && !isNaN(number) && isFinite(config[key])) { + serialized[key] = number; + } else if (Array.isArray(defaults[key]) && Array.isArray(config[key])) { + serialized[key] = JSON.stringify(config[key]); + } else { + serialized[key] = config[key]; + } + }); + return serialized; +} + Configs.deserialize = deserialize; +Configs.serialize = serialize; Configs.init = async function () { const config = await Configs.list(); @@ -92,15 +130,15 @@ Configs.set = async function (field, value) { }; Configs.setMultiple = async function (data) { - data = deserialize(data); await processConfig(data); + data = serialize(data); await db.setObject('config', data); - updateConfig(data); + updateConfig(deserialize(data)); }; Configs.setOnEmpty = async function (values) { const data = await db.getObject('config'); - const config = { ...values, ...(data ? deserialize(data) : {}) }; + const config = { ...values, ...(data ? serialize(data) : {}) }; await db.setObject('config', config); }; diff --git a/src/posts/queue.js b/src/posts/queue.js index e48ea5d5c9..04753b04e3 100644 --- a/src/posts/queue.js +++ b/src/posts/queue.js @@ -16,7 +16,8 @@ const socketHelpers = require('../socket.io/helpers'); module.exports = function (Posts) { Posts.shouldQueue = async function (uid, data) { const userData = await user.getUserFields(uid, ['uid', 'reputation', 'postcount']); - const shouldQueue = meta.config.postQueue && (!userData.uid || userData.reputation < meta.config.postQueueReputationThreshold || userData.postcount <= 0); + const isMemberOfExempt = await groups.isMemberOfAny(userData.uid, meta.config.groupsExemptFromPostQueue); + const shouldQueue = meta.config.postQueue && !isMemberOfExempt && (!userData.uid || userData.reputation < meta.config.postQueueReputationThreshold || userData.postcount <= 0); const result = await plugins.fireHook('filter:post.shouldQueue', { shouldQueue: !!shouldQueue, uid: uid, diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index f433dd8809..15791388ea 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -165,10 +165,11 @@ SocketAdmin.config.setMultiple = async function (socket, data) { } const changes = {}; - data = meta.configs.deserialize(data); - Object.keys(data).forEach(function (key) { - if (data[key] !== meta.config[key]) { - changes[key] = data[key]; + const newData = meta.configs.serialize(data); + const oldData = meta.configs.serialize(meta.config); + Object.keys(newData).forEach(function (key) { + if (newData[key] !== oldData[key]) { + changes[key] = newData[key]; changes[key + '_old'] = meta.config[key]; } }); diff --git a/src/views/admin/settings/post.tpl b/src/views/admin/settings/post.tpl index e558141158..4a1657b096 100644 --- a/src/views/admin/settings/post.tpl +++ b/src/views/admin/settings/post.tpl @@ -138,6 +138,16 @@ +
+
+ + +
+
diff --git a/test/posts.js b/test/posts.js index 546c6b8741..48e9df4200 100644 --- a/test/posts.js +++ b/test/posts.js @@ -964,6 +964,7 @@ describe('Post\'s', function () { after(function (done) { meta.config.postQueue = 0; + meta.config.groupsExemptFromPostQueue = []; done(); }); @@ -1057,6 +1058,15 @@ describe('Post\'s', function () { done(); }); }); + + it('should bypass post queue if user is in exempt group', function (done) { + meta.config.groupsExemptFromPostQueue = ['registered-users']; + socketTopics.post({ uid: uid, emit: () => {} }, { title: 'should not be queued', content: 'topic content', cid: cid }, function (err, result) { + assert.ifError(err); + assert.strictEqual(result.title, 'should not be queued'); + done(); + }); + }); }); describe('upload methods', function () {