feat: min:rep:upvote, and other limits similar to downvotes

closes #10380
isekai-main
Barış Soner Uşaklı 3 years ago
parent 2056ac04e0
commit 3414a23bce

@ -77,9 +77,12 @@
"reputation:disabled": 0, "reputation:disabled": 0,
"downvote:disabled": 0, "downvote:disabled": 0,
"disableSignatures": 0, "disableSignatures": 0,
"upvotesPerDay": 20,
"upvotesPerUserPerDay": 6,
"downvotesPerDay": 10, "downvotesPerDay": 10,
"downvotesPerUserPerDay": 3, "downvotesPerUserPerDay": 3,
"min:rep:downvote": 0, "min:rep:downvote": 0,
"min:rep:upvote": 0,
"min:rep:flag": 0, "min:rep:flag": 0,
"min:rep:profile-picture": 0, "min:rep:profile-picture": 0,
"min:rep:cover-picture": 0, "min:rep:cover-picture": 0,

@ -4,6 +4,9 @@
"disable-down-voting": "Disable Down Voting", "disable-down-voting": "Disable Down Voting",
"votes-are-public": "All Votes Are Public", "votes-are-public": "All Votes Are Public",
"thresholds": "Activity Thresholds", "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", "min-rep-downvote": "Minimum reputation to downvote posts",
"downvotes-per-day": "Downvotes per day (set to 0 for unlimited downvotes)", "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)", "downvotes-per-user-per-day": "Downvotes per user per day (set to 0 for unlimited downvotes)",

@ -181,19 +181,22 @@
"already-voting-for-this-post": "You have already voted for this post.", "already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.", "reputation-system-disabled": "Reputation system is disabled.",
"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-upvote": "You need %1 reputation to upvote",
"not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", "not-enough-reputation-to-downvote": "You need %1 reputation to downvote",
"not-enough-reputation-min-rep-website": "You do not have enough reputation to add a website", "not-enough-reputation-to-flag": "You need %1 reputation to flag this post",
"not-enough-reputation-min-rep-aboutme": "You do not have enough reputation to add an about me", "not-enough-reputation-min-rep-website": "You need %1 reputation to add a website",
"not-enough-reputation-min-rep-signature": "You do not have enough reputation to add a signature", "not-enough-reputation-min-rep-aboutme": "You need %1 reputation to add an about me",
"not-enough-reputation-min-rep-profile-picture": "You do not have enough reputation to add a profile picture", "not-enough-reputation-min-rep-signature": "You need %1 reputation to add a signature",
"not-enough-reputation-min-rep-cover-picture": "You do not have enough reputation to add a cover picture", "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", "post-already-flagged": "You have already flagged this post",
"user-already-flagged": "You have already flagged this user", "user-already-flagged": "You have already flagged this user",
"post-flagged-too-many-times": "This post has been flagged by others already", "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", "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)", "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", "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": "You can only downvote %1 times a day",
"too-many-downvotes-today-user": "You can only downvote a user %1 times a day", "too-many-downvotes-today-user": "You can only downvote a user %1 times a day",

@ -289,7 +289,7 @@ Flags.validate = async function (payload) {
if (payload.type === 'post') { if (payload.type === 'post') {
const editable = await privileges.posts.canEdit(payload.id, payload.uid); const editable = await privileges.posts.canEdit(payload.id, payload.uid);
if (!editable.flag && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) { 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') { } else if (payload.type === 'user') {
if (parseInt(payload.id, 10) === parseInt(payload.uid, 10)) { 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); const editable = await privileges.users.canEdit(payload.uid, payload.id);
if (!editable && !meta.config['reputation:disabled'] && reporter.reputation < meta.config['min:rep:flag']) { 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 { } else {
throw new Error('[[error:invalid-data]]'); throw new Error('[[error:invalid-data]]');

@ -128,8 +128,8 @@ module.exports = function (Posts) {
throw new Error('[[error:self-vote]]'); throw new Error('[[error:self-vote]]');
} }
if (type === 'downvote') { if (type === 'downvote' || type === 'upvote') {
await checkDownvoteLimitation(pid, uid); await checkVoteLimitation(pid, uid, type);
} }
if (!voteStatus || (!voteStatus.upvoted && !voteStatus.downvoted)) { 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); 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 oneDay = 86400000;
const [reputation, targetUid, downvotedPids] = await Promise.all([ const [reputation, targetUid, votedPidsToday] = await Promise.all([
user.getUserField(uid, 'reputation'), user.getUserField(uid, 'reputation'),
Posts.getPostField(pid, 'uid'), Posts.getPostField(pid, 'uid'),
db.getSortedSetRevRangeByScore( 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']) { if (reputation < meta.config[`min:rep:${type}`]) {
throw new Error('[[error:not-enough-reputation-to-downvote]]'); throw new Error(`[[error:not-enough-reputation-to-${type}, ${meta.config[`min:rep:${type}`]}]]`);
} }
const votesToday = meta.config[`${type}sPerDay`];
if (meta.config.downvotesPerDay && downvotedPids.length >= meta.config.downvotesPerDay) { if (votesToday && votedPidsToday.length >= votesToday) {
throw new Error(`[[error:too-many-downvotes-today, ${meta.config.downvotesPerDay}]]`); throw new Error(`[[error:too-many-${type}s-today, ${votesToday}]]`);
} }
const voterPerUserToday = meta.config[`${type}sPerUserPerDay`];
if (meta.config.downvotesPerUserPerDay) { if (voterPerUserToday) {
const postData = await Posts.getPostsFields(downvotedPids, ['uid']); const postData = await Posts.getPostsFields(votedPidsToday, ['uid']);
const targetDownvotes = postData.filter(p => p.uid === targetUid).length; const targetUpVotes = postData.filter(p => p.uid === targetUid).length;
if (targetDownvotes >= meta.config.downvotesPerUserPerDay) { if (targetUpVotes >= voterPerUserToday) {
throw new Error(`[[error:too-many-downvotes-today-user, ${meta.config.downvotesPerUserPerDay}]]`); throw new Error(`[[error:too-many-${type}s-today-user, ${voterPerUserToday}]]`);
} }
} }
} }

@ -226,7 +226,7 @@ module.exports = function (User) {
} }
const reputation = await User.getUserField(uid, 'reputation'); const reputation = await User.getUserField(uid, 'reputation');
if (reputation < meta.config[setting]) { 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]}]]`);
} }
}; };

@ -31,6 +31,19 @@
<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>
<div class="form-group">
<label for="min:rep:upvote">[[admin/settings/reputation:min-rep-upvote]]</label>
<input type="text" class="form-control" placeholder="0" data-field="min:rep:upvote" id="min:rep:upvote">
</div>
<div class="form-group">
<label for="upvotesPerDay">[[admin/settings/reputation:upvotes-per-day]]</label>
<input type="text" class="form-control" placeholder="10" data-field="upvotesPerDay" id="upvotesPerDay">
</div>
<div class="form-group">
<label for="upvotesPerUserPerDay">[[admin/settings/reputation:upvotes-per-user-per-day]]</label>
<input type="text" class="form-control" placeholder="3" data-field="upvotesPerUserPerDay" id="upvotesPerUserPerDay">
</div>
<div class="form-group"> <div class="form-group">
<label for="min:rep:downvote">[[admin/settings/reputation:min-rep-downvote]]</label> <label for="min:rep:downvote">[[admin/settings/reputation:min-rep-downvote]]</label>
<input type="text" class="form-control" placeholder="0" data-field="min:rep:downvote" id="min:rep:downvote"> <input type="text" class="form-control" placeholder="0" data-field="min:rep:downvote" id="min:rep:downvote">

@ -608,7 +608,7 @@ describe('Flags', () => {
uid: 3, uid: 3,
}, (err) => { }, (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, 50]]', err.message);
Meta.configs.set('min:rep:flag', 0, done); Meta.configs.set('min:rep:flag', 0, done);
}); });
}); });

Loading…
Cancel
Save