From 3414a23bcef98909ade17d5f0d5259085ac0371c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 11 Mar 2022 13:34:36 -0500 Subject: [PATCH] feat: min:rep:upvote, and other limits similar to downvotes closes #10380 --- install/data/defaults.json | 3 ++ .../en-GB/admin/settings/reputation.json | 3 ++ public/language/en-GB/error.json | 17 ++++++---- src/flags.js | 4 +-- src/posts/votes.js | 33 ++++++++++--------- src/user/profile.js | 2 +- src/views/admin/settings/reputation.tpl | 13 ++++++++ test/flags.js | 2 +- 8 files changed, 50 insertions(+), 27 deletions(-) diff --git a/install/data/defaults.json b/install/data/defaults.json index 952702a3fb..397d1704e7 100644 --- a/install/data/defaults.json +++ b/install/data/defaults.json @@ -77,9 +77,12 @@ "reputation:disabled": 0, "downvote:disabled": 0, "disableSignatures": 0, + "upvotesPerDay": 20, + "upvotesPerUserPerDay": 6, "downvotesPerDay": 10, "downvotesPerUserPerDay": 3, "min:rep:downvote": 0, + "min:rep:upvote": 0, "min:rep:flag": 0, "min:rep:profile-picture": 0, "min:rep:cover-picture": 0, diff --git a/public/language/en-GB/admin/settings/reputation.json b/public/language/en-GB/admin/settings/reputation.json index 8ea8cda4c1..4140161eb8 100644 --- a/public/language/en-GB/admin/settings/reputation.json +++ b/public/language/en-GB/admin/settings/reputation.json @@ -4,6 +4,9 @@ "disable-down-voting": "Disable Down Voting", "votes-are-public": "All Votes Are Public", "thresholds": "Activity Thresholds", + "min-rep-upvote": "Minimum reputation to upvote posts", + "upvotes-per-day": "Upvotes per day (set to 0 for unlimited upvotes)", + "upvotes-per-user-per-day": "Upvotes per user per day (set to 0 for unlimited upvotes)", "min-rep-downvote": "Minimum reputation to downvote posts", "downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)", "downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)", diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index bf004cf7a3..1f965d5ea5 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -181,19 +181,22 @@ "already-voting-for-this-post": "You have already voted for this post.", "reputation-system-disabled": "Reputation system 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-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", - "not-enough-reputation-min-rep-profile-picture": "You do not have enough reputation to add a profile picture", - "not-enough-reputation-min-rep-cover-picture": "You do not have enough reputation to add a cover picture", + "not-enough-reputation-to-upvote": "You need %1 reputation to upvote", + "not-enough-reputation-to-downvote": "You need %1 reputation to downvote", + "not-enough-reputation-to-flag": "You need %1 reputation to flag this post", + "not-enough-reputation-min-rep-website": "You need %1 reputation to add a website", + "not-enough-reputation-min-rep-aboutme": "You need %1 reputation to add an about me", + "not-enough-reputation-min-rep-signature": "You need %1 reputation to add a signature", + "not-enough-reputation-min-rep-profile-picture": "You need %1 reputation to add a profile picture", + "not-enough-reputation-min-rep-cover-picture": "You need %1 reputation to add a cover picture", "post-already-flagged": "You have already flagged this post", "user-already-flagged": "You have already flagged this user", "post-flagged-too-many-times": "This post has been flagged by others already", "user-flagged-too-many-times": "This user has been flagged by others already", "cant-flag-privileged": "You are not allowed to flag the profiles or content of privileged users (moderators/global moderators/admins)", "self-vote": "You cannot vote on your own post", + "too-many-upvotes-today": "You can only upvote %1 times a day", + "too-many-upvotes-today-user": "You can only upvote a user %1 times a day", "too-many-downvotes-today": "You can only downvote %1 times a day", "too-many-downvotes-today-user": "You can only downvote a user %1 times a day", diff --git a/src/flags.js b/src/flags.js index 990cced428..43e5927c2d 100644 --- a/src/flags.js +++ b/src/flags.js @@ -289,7 +289,7 @@ Flags.validate = async function (payload) { if (payload.type === 'post') { const editable = await privileges.posts.canEdit(payload.id, payload.uid); if (!editable.flag && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) { - throw new Error('[[error:not-enough-reputation-to-flag]]'); + throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`); } } else if (payload.type === 'user') { if (parseInt(payload.id, 10) === parseInt(payload.uid, 10)) { @@ -297,7 +297,7 @@ Flags.validate = async function (payload) { } const editable = await privileges.users.canEdit(payload.uid, payload.id); if (!editable && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) { - throw new Error('[[error:not-enough-reputation-to-flag]]'); + throw new Error(`[[error:not-enough-reputation-to-flag, ${meta.config['min:rep:flag']}]]`); } } else { throw new Error('[[error:invalid-data]]'); diff --git a/src/posts/votes.js b/src/posts/votes.js index 08466a2fe8..dfdfad2899 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -128,8 +128,8 @@ module.exports = function (Posts) { throw new Error('[[error:self-vote]]'); } - if (type === 'downvote') { - await checkDownvoteLimitation(pid, uid); + if (type === 'downvote' || type === 'upvote') { + await checkVoteLimitation(pid, uid, type); } if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) { @@ -139,29 +139,30 @@ module.exports = function (Posts) { return await vote(voteStatus.upvoted ? 'downvote' : 'upvote', true, pid, uid, voteStatus); } - async function checkDownvoteLimitation(pid, uid) { + async function checkVoteLimitation(pid, uid, type) { + // type = 'upvote' or 'downvote' const oneDay = 86400000; - const [reputation, targetUid, downvotedPids] = await Promise.all([ + const [reputation, targetUid, votedPidsToday] = await Promise.all([ user.getUserField(uid, 'reputation'), Posts.getPostField(pid, 'uid'), db.getSortedSetRevRangeByScore( - `uid:${uid}:downvote`, 0, -1, '+inf', Date.now() - oneDay + `uid:${uid}:${type}`, 0, -1, '+inf', Date.now() - oneDay ), ]); - if (reputation < meta.config['min:rep:downvote']) { - throw new Error('[[error:not-enough-reputation-to-downvote]]'); + if (reputation < meta.config[`min:rep:${type}`]) { + throw new Error(`[[error:not-enough-reputation-to-${type}, ${meta.config[`min:rep:${type}`]}]]`); } - - if (meta.config.downvotesPerDay && downvotedPids.length >= meta.config.downvotesPerDay) { - throw new Error(`[[error:too-many-downvotes-today, ${meta.config.downvotesPerDay}]]`); + const votesToday = meta.config[`${type}sPerDay`]; + if (votesToday && votedPidsToday.length >= votesToday) { + throw new Error(`[[error:too-many-${type}s-today, ${votesToday}]]`); } - - if (meta.config.downvotesPerUserPerDay) { - const postData = await Posts.getPostsFields(downvotedPids, ['uid']); - const targetDownvotes = postData.filter(p => p.uid === targetUid).length; - if (targetDownvotes >= meta.config.downvotesPerUserPerDay) { - throw new Error(`[[error:too-many-downvotes-today-user, ${meta.config.downvotesPerUserPerDay}]]`); + const voterPerUserToday = meta.config[`${type}sPerUserPerDay`]; + if (voterPerUserToday) { + const postData = await Posts.getPostsFields(votedPidsToday, ['uid']); + const targetUpVotes = postData.filter(p => p.uid === targetUid).length; + if (targetUpVotes >= voterPerUserToday) { + throw new Error(`[[error:too-many-${type}s-today-user, ${voterPerUserToday}]]`); } } } diff --git a/src/user/profile.js b/src/user/profile.js index 3c93cb8bd0..d66fe8e1cf 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -226,7 +226,7 @@ module.exports = function (User) { } const reputation = await User.getUserField(uid, 'reputation'); if (reputation < meta.config[setting]) { - throw new Error(`[[error:not-enough-reputation-${setting.replace(/:/g, '-')}]]`); + throw new Error(`[[error:not-enough-reputation-${setting.replace(/:/g, '-')}, ${meta.config[setting]}]]`); } }; diff --git a/src/views/admin/settings/reputation.tpl b/src/views/admin/settings/reputation.tpl index 24f8e632a1..0d06b7cc3a 100644 --- a/src/views/admin/settings/reputation.tpl +++ b/src/views/admin/settings/reputation.tpl @@ -31,6 +31,19 @@
[[admin/settings/reputation:thresholds]]
+
+ + +
+
+ + +
+
+ + +
+
diff --git a/test/flags.js b/test/flags.js index 1a5600e973..912a1f150e 100644 --- a/test/flags.js +++ b/test/flags.js @@ -608,7 +608,7 @@ describe('Flags', () => { uid: 3, }, (err) => { assert.ok(err); - assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message); + assert.strictEqual('[[error:not-enough-reputation-to-flag, 50]]', err.message); Meta.configs.set('min:rep:flag', 0, done); }); });