mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 19:15:58 +01:00 
			
		
		
		
	feat: #7957, allow post queue based on group
allow multiple select in ACP pages
This commit is contained in:
		| @@ -21,6 +21,7 @@ | |||||||
|     "chatMessageDelay": 200, |     "chatMessageDelay": 200, | ||||||
|     "newbiePostDelayThreshold": 3, |     "newbiePostDelayThreshold": 3, | ||||||
|     "postQueueReputationThreshold": 0, |     "postQueueReputationThreshold": 0, | ||||||
|  |     "groupsExemptFromPostQueue": ["administrators", "Global Moderators"], | ||||||
|     "minimumPostLength": 8, |     "minimumPostLength": 8, | ||||||
|     "maximumPostLength": 32767, |     "maximumPostLength": 32767, | ||||||
|     "minimumTagsPerTopic": 0, |     "minimumTagsPerTopic": 0, | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
| 	"restrictions-new": "New User Restrictions", | 	"restrictions-new": "New User Restrictions", | ||||||
| 	"restrictions.post-queue": "Enable post queue", | 	"restrictions.post-queue": "Enable post queue", | ||||||
| 	"restrictions.post-queue-rep-threshold": "Reputation required to bypass 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-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.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", | 	"restrictions-new.post-queue-help": "Enabling new user restrictions will set restrictions on posts created by new users", | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| const meta = require('../../meta'); | const meta = require('../../meta'); | ||||||
| const emailer = require('../../emailer'); | const emailer = require('../../emailer'); | ||||||
| const notifications = require('../../notifications'); | const notifications = require('../../notifications'); | ||||||
|  | const groups = require('../../groups'); | ||||||
|  |  | ||||||
| const settingsController = module.exports; | const settingsController = module.exports; | ||||||
|  |  | ||||||
| @@ -13,6 +14,8 @@ settingsController.get = async function (req, res, next) { | |||||||
| 		await renderEmail(req, res, next); | 		await renderEmail(req, res, next); | ||||||
| 	} else if (term === 'user') { | 	} else if (term === 'user') { | ||||||
| 		await renderUser(req, res, next); | 		await renderUser(req, res, next); | ||||||
|  | 	} else if (term === 'post') { | ||||||
|  | 		await renderPost(req, res, next); | ||||||
| 	} else { | 	} else { | ||||||
| 		res.render('admin/settings/' + term); | 		res.render('admin/settings/' + term); | ||||||
| 	} | 	} | ||||||
| @@ -41,3 +44,10 @@ async function renderUser(req, res) { | |||||||
| 		notificationSettings: notificationSettings, | 		notificationSettings: notificationSettings, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function renderPost(req, res) { | ||||||
|  | 	const groupData = await groups.getNonPrivilegeGroups('groups:createtime', 0, -1); | ||||||
|  | 	res.render('admin/settings/post', { | ||||||
|  | 		groupsExemptFromPostQueue: groupData, | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ const utils = require('../utils'); | |||||||
| const db = require('../database'); | const db = require('../database'); | ||||||
| const user = require('../user'); | const user = require('../user'); | ||||||
| const batch = require('../batch'); | const batch = require('../batch'); | ||||||
|  | const meta = require('../meta'); | ||||||
|  |  | ||||||
|  |  | ||||||
| module.exports = function (Groups) { | module.exports = function (Groups) { | ||||||
| @@ -164,6 +165,7 @@ module.exports = function (Groups) { | |||||||
| 		await updateMemberGroupTitles(oldName, newName); | 		await updateMemberGroupTitles(oldName, newName); | ||||||
| 		await updateNavigationItems(oldName, newName); | 		await updateNavigationItems(oldName, newName); | ||||||
| 		await updateWidgets(oldName, newName); | 		await updateWidgets(oldName, newName); | ||||||
|  | 		await updateConfig(oldName, newName); | ||||||
| 		await db.setObject('group:' + oldName, { name: newName, slug: utils.slugify(newName) }); | 		await db.setObject('group:' + oldName, { name: newName, slug: utils.slugify(newName) }); | ||||||
| 		await db.deleteObjectField('groupslug:groupname', group.slug); | 		await db.deleteObjectField('groupslug:groupname', group.slug); | ||||||
| 		await db.setObjectField('groupslug:groupname', utils.slugify(newName), newName); | 		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); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ const Configs = module.exports; | |||||||
|  |  | ||||||
| Meta.config = {}; | Meta.config = {}; | ||||||
|  |  | ||||||
|  | // called after data is loaded from db | ||||||
| function deserialize(config) { | function deserialize(config) { | ||||||
| 	const deserialized = {}; | 	const deserialized = {}; | ||||||
| 	Object.keys(config).forEach(function (key) { | 	Object.keys(config).forEach(function (key) { | ||||||
| @@ -39,6 +40,13 @@ function deserialize(config) { | |||||||
| 			deserialized[key] = defaults[key]; | 			deserialized[key] = defaults[key]; | ||||||
| 		} else if (defaultType === 'undefined' && !isNaN(number) && isFinite(config[key])) { | 		} else if (defaultType === 'undefined' && !isNaN(number) && isFinite(config[key])) { | ||||||
| 			deserialized[key] = number; | 			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 { | 		} else { | ||||||
| 			deserialized[key] = config[key]; | 			deserialized[key] = config[key]; | ||||||
| 		} | 		} | ||||||
| @@ -46,7 +54,37 @@ function deserialize(config) { | |||||||
| 	return deserialized; | 	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.deserialize = deserialize; | ||||||
|  | Configs.serialize = serialize; | ||||||
|  |  | ||||||
| Configs.init = async function () { | Configs.init = async function () { | ||||||
| 	const config = await Configs.list(); | 	const config = await Configs.list(); | ||||||
| @@ -92,15 +130,15 @@ Configs.set = async function (field, value) { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| Configs.setMultiple = async function (data) { | Configs.setMultiple = async function (data) { | ||||||
| 	data = deserialize(data); |  | ||||||
| 	await processConfig(data); | 	await processConfig(data); | ||||||
|  | 	data = serialize(data); | ||||||
| 	await db.setObject('config', data); | 	await db.setObject('config', data); | ||||||
| 	updateConfig(data); | 	updateConfig(deserialize(data)); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Configs.setOnEmpty = async function (values) { | Configs.setOnEmpty = async function (values) { | ||||||
| 	const data = await db.getObject('config'); | 	const data = await db.getObject('config'); | ||||||
| 	const config = { ...values, ...(data ? deserialize(data) : {}) }; | 	const config = { ...values, ...(data ? serialize(data) : {}) }; | ||||||
| 	await db.setObject('config', config); | 	await db.setObject('config', config); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,8 @@ const socketHelpers = require('../socket.io/helpers'); | |||||||
| module.exports = function (Posts) { | module.exports = function (Posts) { | ||||||
| 	Posts.shouldQueue = async function (uid, data) { | 	Posts.shouldQueue = async function (uid, data) { | ||||||
| 		const userData = await user.getUserFields(uid, ['uid', 'reputation', 'postcount']); | 		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', { | 		const result = await plugins.fireHook('filter:post.shouldQueue', { | ||||||
| 			shouldQueue: !!shouldQueue, | 			shouldQueue: !!shouldQueue, | ||||||
| 			uid: uid, | 			uid: uid, | ||||||
|   | |||||||
| @@ -165,10 +165,11 @@ SocketAdmin.config.setMultiple = async function (socket, data) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	const changes = {}; | 	const changes = {}; | ||||||
| 	data = meta.configs.deserialize(data); | 	const newData = meta.configs.serialize(data); | ||||||
| 	Object.keys(data).forEach(function (key) { | 	const oldData = meta.configs.serialize(meta.config); | ||||||
| 		if (data[key] !== meta.config[key]) { | 	Object.keys(newData).forEach(function (key) { | ||||||
| 			changes[key] = data[key]; | 		if (newData[key] !== oldData[key]) { | ||||||
|  | 			changes[key] = newData[key]; | ||||||
| 			changes[key + '_old'] = meta.config[key]; | 			changes[key + '_old'] = meta.config[key]; | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
|   | |||||||
| @@ -138,6 +138,16 @@ | |||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
|  | 			<div class="row"> | ||||||
|  | 				<div class="form-group"> | ||||||
|  | 					<label>[[admin/settings/post:restrictions.groups-exempt-from-post-queue]]</label> | ||||||
|  | 					<select class="form-control" multiple data-field="groupsExemptFromPostQueue"> | ||||||
|  | 						<!-- BEGIN groupsExemptFromPostQueue --> | ||||||
|  | 						<option value="{groupsExemptFromPostQueue.displayName}">{groupsExemptFromPostQueue.displayName}</option> | ||||||
|  | 						<!-- END --> | ||||||
|  | 					</select> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
| 		</form> | 		</form> | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -964,6 +964,7 @@ describe('Post\'s', function () { | |||||||
|  |  | ||||||
| 		after(function (done) { | 		after(function (done) { | ||||||
| 			meta.config.postQueue = 0; | 			meta.config.postQueue = 0; | ||||||
|  | 			meta.config.groupsExemptFromPostQueue = []; | ||||||
| 			done(); | 			done(); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| @@ -1057,6 +1058,15 @@ describe('Post\'s', function () { | |||||||
| 				done(); | 				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 () { | 	describe('upload methods', function () { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user