mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +01:00 
			
		
		
		
	| @@ -38,5 +38,5 @@ | |||||||
|     "bookmarkThreshold": 5, |     "bookmarkThreshold": 5, | ||||||
|     "topicsPerList": 20, |     "topicsPerList": 20, | ||||||
|     "autoDetectLang": 1, |     "autoDetectLang": 1, | ||||||
|     "privileges:flag": 0 |     "min:rep:flag": 0 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -69,9 +69,9 @@ | |||||||
|         "nodebb-plugin-spam-be-gone": "0.5.1", |         "nodebb-plugin-spam-be-gone": "0.5.1", | ||||||
|         "nodebb-rewards-essentials": "0.0.9", |         "nodebb-rewards-essentials": "0.0.9", | ||||||
|         "nodebb-theme-lavender": "5.0.0", |         "nodebb-theme-lavender": "5.0.0", | ||||||
|         "nodebb-theme-persona": "7.2.11", |         "nodebb-theme-persona": "7.2.12", | ||||||
|         "nodebb-theme-slick": "1.1.2", |         "nodebb-theme-slick": "1.1.2", | ||||||
|         "nodebb-theme-vanilla": "8.1.5", |         "nodebb-theme-vanilla": "8.1.6", | ||||||
|         "nodebb-widget-essentials": "4.0.1", |         "nodebb-widget-essentials": "4.0.1", | ||||||
|         "nodemailer": "4.4.1", |         "nodemailer": "4.4.1", | ||||||
|         "passport": "^0.4.0", |         "passport": "^0.4.0", | ||||||
|   | |||||||
| @@ -5,5 +5,8 @@ | |||||||
| 	"votes-are-public": "All Votes Are Public", | 	"votes-are-public": "All Votes Are Public", | ||||||
| 	"thresholds": "Activity Thresholds", | 	"thresholds": "Activity Thresholds", | ||||||
| 	"min-rep-downvote": "Minimum reputation to downvote posts", | 	"min-rep-downvote": "Minimum reputation to downvote posts", | ||||||
| 	"min-rep-flag": "Minimum reputation to flag posts" | 	"min-rep-flag": "Minimum reputation to flag posts", | ||||||
|  | 	"min-rep-website": "Minimum reputation to add \"Website\" to user profile", | ||||||
|  | 	"min-rep-aboutme": "Minimum reputation to add \"About me\" to user profile", | ||||||
|  | 	"min-rep-signature": "Minimum reputation to add \"Signature\" to user profile" | ||||||
| } | } | ||||||
| @@ -146,6 +146,9 @@ | |||||||
| 	"downvoting-disabled": "Downvoting is disabled", | 	"downvoting-disabled": "Downvoting is disabled", | ||||||
| 	"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", | 	"not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post", | ||||||
| 	"not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", | 	"not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", | ||||||
|  | 	"not-enough-reputation-min-rep-website": "You do not have enough reputation to add a website", | ||||||
|  | 	"not-enough-reputation-min-rep-aboutme": "You do not have enough reputation to add an about me", | ||||||
|  | 	"not-enough-reputation-min-rep-signature": "You do not have enough reputation to add a signature", | ||||||
| 	"already-flagged": "You have already flagged this post", | 	"already-flagged": "You have already flagged this post", | ||||||
| 	"self-vote": "You cannot vote on your own post", | 	"self-vote": "You cannot vote on your own post", | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,6 +28,9 @@ editController.get = function (req, res, callback) { | |||||||
| 			userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10); | 			userData.maximumProfileImageSize = parseInt(meta.config.maximumProfileImageSize, 10); | ||||||
| 			userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads, 10) === 1; | 			userData.allowProfileImageUploads = parseInt(meta.config.allowProfileImageUploads, 10) === 1; | ||||||
| 			userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; | 			userData.allowAccountDelete = parseInt(meta.config.allowAccountDelete, 10) === 1; | ||||||
|  | 			userData.allowWebsite = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:website'], 10) || 0); | ||||||
|  | 			userData.allowAboutMe = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:aboutme'], 10) || 0); | ||||||
|  | 			userData.allowSignature = !userData.isSelf || parseInt(userData.reputation, 10) >= (parseInt(meta.config['min:rep:signature'], 10) || 0); | ||||||
| 			userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 200; | 			userData.profileImageDimension = parseInt(meta.config.profileImageDimension, 10) || 200; | ||||||
| 			userData.defaultAvatar = user.getDefaultAvatar(); | 			userData.defaultAvatar = user.getDefaultAvatar(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -241,7 +241,7 @@ Flags.validate = function (payload, callback) { | |||||||
| 					return callback(err); | 					return callback(err); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; | 				var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; | ||||||
| 				// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) | 				// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply) | ||||||
| 				if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { | 				if (!editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) { | ||||||
| 					return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | 					return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | ||||||
| @@ -257,7 +257,7 @@ Flags.validate = function (payload, callback) { | |||||||
| 					return callback(err); | 					return callback(err); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; | 				var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; | ||||||
| 				// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) | 				// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply) | ||||||
| 				if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { | 				if (!editable && parseInt(data.reporter.reputation, 10) < minimumReputation) { | ||||||
| 					return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | 					return callback(new Error('[[error:not-enough-reputation-to-flag]]')); | ||||||
|   | |||||||
| @@ -179,7 +179,7 @@ module.exports = function (Posts) { | |||||||
| 					return callback(new Error('[[error:self-vote]]')); | 					return callback(new Error('[[error:self-vote]]')); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 				if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['privileges:downvote'], 10)) { | 				if (command === 'downvote' && parseInt(results.reputation, 10) < parseInt(meta.config['min:rep:downvote'], 10)) { | ||||||
| 					return callback(new Error('[[error:not-enough-reputation-to-downvote]]')); | 					return callback(new Error('[[error:not-enough-reputation-to-downvote]]')); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -200,7 +200,7 @@ module.exports = function (privileges) { | |||||||
| 				}, next); | 				}, next); | ||||||
| 			}, | 			}, | ||||||
| 			function (results, next) { | 			function (results, next) { | ||||||
| 				var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 0; | 				var minimumReputation = utils.isNumber(meta.config['min:rep:flag']) ? parseInt(meta.config['min:rep:flag'], 10) : 0; | ||||||
| 				var canFlag = results.isAdminOrMod || parseInt(results.userReputation, 10) >= minimumReputation; | 				var canFlag = results.isAdminOrMod || parseInt(results.userReputation, 10) >= minimumReputation; | ||||||
| 				next(null, { flag: canFlag }); | 				next(null, { flag: canFlag }); | ||||||
| 			}, | 			}, | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								src/upgrades/1.8.0/rename_min_reputation_settings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/upgrades/1.8.0/rename_min_reputation_settings.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | var db = require('../../database'); | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  | 	name: 'Rename privileges:downvote and privileges:flag to min:rep:downvote, min:rep:flag respectively', | ||||||
|  | 	timestamp: Date.UTC(2018, 0, 12), | ||||||
|  | 	method: function (callback) { | ||||||
|  | 		db.getObjectFields('config', ['privileges:downvote', 'privileges:flag'], function (err, config) { | ||||||
|  | 			if (err) { | ||||||
|  | 				return callback(err); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			db.setObject('config', { | ||||||
|  | 				'min:rep:downvote': parseInt(config['privileges:downvote'], 10) || 0, | ||||||
|  | 				'min:rep:flag': parseInt(config['privileges:downvote'], 10) || 0, | ||||||
|  | 			}, function (err) { | ||||||
|  | 				if (err) { | ||||||
|  | 					return callback(err); | ||||||
|  | 				} | ||||||
|  | 				db.deleteObjectFields('config', ['privileges:downvote', 'privileges:flag'], callback); | ||||||
|  | 			}); | ||||||
|  | 		}); | ||||||
|  | 	}, | ||||||
|  | }; | ||||||
| @@ -17,14 +17,6 @@ module.exports = function (User) { | |||||||
| 		var updateUid = data.uid; | 		var updateUid = data.uid; | ||||||
| 		var oldData; | 		var oldData; | ||||||
|  |  | ||||||
| 		if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) { |  | ||||||
| 			return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]')); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) { |  | ||||||
| 			return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]')); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		async.waterfall([ | 		async.waterfall([ | ||||||
| 			function (next) { | 			function (next) { | ||||||
| 				plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next); | 				plugins.fireHook('filter:user.updateProfile', { uid: uid, data: data, fields: fields }, next); | ||||||
| @@ -33,13 +25,7 @@ module.exports = function (User) { | |||||||
| 				fields = data.fields; | 				fields = data.fields; | ||||||
| 				data = data.data; | 				data = data.data; | ||||||
|  |  | ||||||
| 				async.series([ | 				validateData(uid, data, next); | ||||||
| 					async.apply(isEmailAvailable, data, updateUid), |  | ||||||
| 					async.apply(isUsernameAvailable, data, updateUid), |  | ||||||
| 					async.apply(isGroupTitleValid, data), |  | ||||||
| 				], function (err) { |  | ||||||
| 					next(err); |  | ||||||
| 				}); |  | ||||||
| 			}, | 			}, | ||||||
| 			function (next) { | 			function (next) { | ||||||
| 				User.getUserFields(updateUid, fields, next); | 				User.getUserFields(updateUid, fields, next); | ||||||
| @@ -73,6 +59,19 @@ module.exports = function (User) { | |||||||
| 		], callback); | 		], callback); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	function validateData(callerUid, data, callback) { | ||||||
|  | 		async.series([ | ||||||
|  | 			async.apply(isEmailAvailable, data, data.uid), | ||||||
|  | 			async.apply(isUsernameAvailable, data, data.uid), | ||||||
|  | 			async.apply(isGroupTitleValid, data), | ||||||
|  | 			async.apply(isWebsiteValid, callerUid, data), | ||||||
|  | 			async.apply(isAboutMeValid, callerUid, data), | ||||||
|  | 			async.apply(isSignatureValid, callerUid, data), | ||||||
|  | 		], function (err) { | ||||||
|  | 			callback(err); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	function isEmailAvailable(data, uid, callback) { | 	function isEmailAvailable(data, uid, callback) { | ||||||
| 		if (!data.email) { | 		if (!data.email) { | ||||||
| 			return callback(); | 			return callback(); | ||||||
| @@ -141,6 +140,52 @@ module.exports = function (User) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	function isWebsiteValid(callerUid, data, callback) { | ||||||
|  | 		if (!data.website) { | ||||||
|  | 			return setImmediate(callback); | ||||||
|  | 		} | ||||||
|  | 		checkMinReputation(callerUid, data.uid, 'min:rep:website', callback); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function isAboutMeValid(callerUid, data, callback) { | ||||||
|  | 		if (!data.aboutme) { | ||||||
|  | 			return setImmediate(callback); | ||||||
|  | 		} | ||||||
|  | 		if (data.aboutme !== undefined && data.aboutme.length > meta.config.maximumAboutMeLength) { | ||||||
|  | 			return callback(new Error('[[error:about-me-too-long, ' + meta.config.maximumAboutMeLength + ']]')); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		checkMinReputation(callerUid, data.uid, 'min:rep:aboutme', callback); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function isSignatureValid(callerUid, data, callback) { | ||||||
|  | 		if (!data.signature) { | ||||||
|  | 			return setImmediate(callback); | ||||||
|  | 		} | ||||||
|  | 		if (data.signature !== undefined && data.signature.length > meta.config.maximumSignatureLength) { | ||||||
|  | 			return callback(new Error('[[error:signature-too-long, ' + meta.config.maximumSignatureLength + ']]')); | ||||||
|  | 		} | ||||||
|  | 		checkMinReputation(callerUid, data.uid, 'min:rep:signature', callback); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	function checkMinReputation(callerUid, uid, setting, callback) { | ||||||
|  | 		var isSelf = parseInt(callerUid, 10) === parseInt(uid, 10); | ||||||
|  | 		if (!isSelf) { | ||||||
|  | 			return setImmediate(callback); | ||||||
|  | 		} | ||||||
|  | 		async.waterfall([ | ||||||
|  | 			function (next) { | ||||||
|  | 				User.getUserField(uid, 'reputation', next); | ||||||
|  | 			}, | ||||||
|  | 			function (reputation, next) { | ||||||
|  | 				if (parseInt(reputation, 10) < (parseInt(meta.config[setting], 10) || 0)) { | ||||||
|  | 					return next(new Error('[[error:not-enough-reputation-' + setting.replace(/:/g, '-') + ']]')); | ||||||
|  | 				} | ||||||
|  | 				next(); | ||||||
|  | 			}, | ||||||
|  | 		], callback); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	function updateEmail(uid, newEmail, callback) { | 	function updateEmail(uid, newEmail, callback) { | ||||||
| 		async.waterfall([ | 		async.waterfall([ | ||||||
| 			function (next) { | 			function (next) { | ||||||
|   | |||||||
| @@ -32,8 +32,11 @@ | |||||||
| 	<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/reputation:thresholds]]</div> | 	<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/reputation:thresholds]]</div> | ||||||
| 	<div class="col-sm-10 col-xs-12"> | 	<div class="col-sm-10 col-xs-12"> | ||||||
| 		<form> | 		<form> | ||||||
| 			<strong>[[admin/settings/reputation:min-rep-downvote]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="privileges:downvote"><br /> | 			<strong>[[admin/settings/reputation:min-rep-downvote]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="min:rep:downvote"><br /> | ||||||
| 			<strong>[[admin/settings/reputation:min-rep-flag]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="privileges:flag"><br /> | 			<strong>[[admin/settings/reputation:min-rep-flag]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="min:rep:flag"><br /> | ||||||
|  | 			<strong>[[admin/settings/reputation:min-rep-website]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="min:rep:website"><br /> | ||||||
|  | 			<strong>[[admin/settings/reputation:min-rep-aboutme]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="min:rep:aboutme"><br /> | ||||||
|  | 			<strong>[[admin/settings/reputation:min-rep-signature]]</strong><br /> <input type="text" class="form-control" placeholder="0" data-field="min:rep:signature"><br /> | ||||||
| 		</form> | 		</form> | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -591,21 +591,21 @@ describe('Admin Controllers', function () { | |||||||
|  |  | ||||||
| 		it('should error with not enough reputation to flag', function (done) { | 		it('should error with not enough reputation to flag', function (done) { | ||||||
| 			var socketFlags = require('../src/socket.io/flags'); | 			var socketFlags = require('../src/socket.io/flags'); | ||||||
| 			var oldValue = meta.config['privileges:flag']; | 			var oldValue = meta.config['min:rep:flag']; | ||||||
| 			meta.config['privileges:flag'] = 1000; | 			meta.config['min:rep:flag'] = 1000; | ||||||
| 			socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err) { | 			socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err) { | ||||||
| 				assert.equal(err.message, '[[error:not-enough-reputation-to-flag]]'); | 				assert.equal(err.message, '[[error:not-enough-reputation-to-flag]]'); | ||||||
| 				meta.config['privileges:flag'] = oldValue; | 				meta.config['min:rep:flag'] = oldValue; | ||||||
| 				done(); | 				done(); | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		it('should return flag details', function (done) { | 		it('should return flag details', function (done) { | ||||||
| 			var socketFlags = require('../src/socket.io/flags'); | 			var socketFlags = require('../src/socket.io/flags'); | ||||||
| 			var oldValue = meta.config['privileges:flag']; | 			var oldValue = meta.config['min:rep:flag']; | ||||||
| 			meta.config['privileges:flag'] = 0; | 			meta.config['min:rep:flag'] = 0; | ||||||
| 			socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err, data) { | 			socketFlags.create({ uid: regularUid }, { id: pid, type: 'post', reason: 'spam' }, function (err, data) { | ||||||
| 				meta.config['privileges:flag'] = oldValue; | 				meta.config['min:rep:flag'] = oldValue; | ||||||
| 				assert.ifError(err); | 				assert.ifError(err); | ||||||
| 				request(nconf.get('url') + '/api/flags/' + data.flagId, { jar: moderatorJar, json: true }, function (err, res, body) { | 				request(nconf.get('url') + '/api/flags/' + data.flagId, { jar: moderatorJar, json: true }, function (err, res, body) { | ||||||
| 					assert.ifError(err); | 					assert.ifError(err); | ||||||
|   | |||||||
| @@ -364,7 +364,7 @@ describe('Flags', function () { | |||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { | 		it('should not pass validation if flag threshold is set and user rep does not meet it', function (done) { | ||||||
| 			Meta.configs.set('privileges:flag', '50', function (err) { | 			Meta.configs.set('min:rep:flag', '50', function (err) { | ||||||
| 				assert.ifError(err); | 				assert.ifError(err); | ||||||
|  |  | ||||||
| 				Flags.validate({ | 				Flags.validate({ | ||||||
| @@ -374,7 +374,7 @@ describe('Flags', function () { | |||||||
| 				}, function (err) { | 				}, function (err) { | ||||||
| 					assert.ok(err); | 					assert.ok(err); | ||||||
| 					assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); | 					assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); | ||||||
| 					Meta.configs.set('privileges:flag', 0, done); | 					Meta.configs.set('min:rep:flag', 0, done); | ||||||
| 				}); | 				}); | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user