Skip to content

Commit

Permalink
feat: introduce ACP defined option to rescind notif or do nothing on …
Browse files Browse the repository at this point in the history
…flag resolve/reject

/cc #10867
  • Loading branch information
julianlam committed Aug 26, 2022
1 parent df36d96 commit 15b1561
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 17 deletions.
2 changes: 2 additions & 0 deletions install/data/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
"min:rep:signature": 0,
"flags:limitPerTarget": 0,
"flags:autoFlagOnDownvoteThreshold": 0,
"flags:actionOnResolve": "rescind",
"flags:actionOnReject": "rescind",
"notificationType_upvote": "notification",
"notificationType_new-topic": "notification",
"notificationType_new-reply": "notification",
Expand Down
6 changes: 5 additions & 1 deletion public/language/en-GB/admin/settings/reputation.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@
"flags.limit-per-target-placeholder": "Default: 0",
"flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.",
"flags.auto-flag-on-downvote-threshold": "Number of downvotes to auto flag posts (Set to 0 to disable, default: 0)",
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned"
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned",
"flags.action-on-resolve": "Do the following when a flag is resolved",
"flags.action-on-reject": "Do the following when a flag is rejected",
"flags.action.nothing": "Do nothing",
"flags.action.rescind": "Rescind the notification send to moderators/administrators"
}
5 changes: 4 additions & 1 deletion src/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,10 @@ Flags.update = async function (flagId, uid, changeset) {
} else {
tasks.push(db.sortedSetAdd(`flags:byState:${changeset[prop]}`, now, flagId));
tasks.push(db.sortedSetRemove(`flags:byState:${current[prop]}`, flagId));
if (changeset[prop] === 'resolved' || changeset[prop] === 'rejected') {
if (changeset[prop] === 'resolved' && meta.config['flags:actionOnResolve'] === 'rescind') {
tasks.push(notifications.rescind(`flag:${current.type}:${current.targetId}`));
}
if (changeset[prop] === 'rejected' && meta.config['flags:actionOnReject'] === 'rescind') {
tasks.push(notifications.rescind(`flag:${current.type}:${current.targetId}`));
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/views/admin/settings/reputation.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@
<label for="flags:autoFlagOnDownvoteThreshold">[[admin/settings/reputation:flags.auto-flag-on-downvote-threshold]]</label>
<input type="text" class="form-control" placeholder="0" data-field="flags:autoFlagOnDownvoteThreshold" id="flags:autoFlagOnDownvoteThreshold">
</div>
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="flags:actionOnResolve">[[admin/settings/reputation:flags.action-on-resolve]]</label>
<select class="form-control" data-field="flags:actionOnResolve" name="flags:actionOnResolve" id="flags:actionOnResolve">
<option value="">[[admin/settings/reputation:flags.action.nothing]]</option>
<option value="rescind">[[admin/settings/reputation:flags.action.rescind]]</option>
</select>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="flags:actionOnReject">[[admin/settings/reputation:flags.action-on-reject]]</label>
<select class="form-control" data-field="flags:actionOnReject" name="flags:actionOnReject" id="flags:actionOnReject">
<option value="">[[admin/settings/reputation:flags.action.nothing]]</option>
<option value="rescind">[[admin/settings/reputation:flags.action.rescind]]</option>
</select>
</div>
</div>
</div>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" class="mdl-switch__input" data-field="flags:autoResolveOnBan">
Expand Down
80 changes: 65 additions & 15 deletions test/flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const Groups = require('../src/groups');
const Meta = require('../src/meta');
const Privileges = require('../src/privileges');
const utils = require('../src/utils');
const api = require('../src/api');

describe('Flags', () => {
let uid1;
Expand Down Expand Up @@ -511,26 +512,75 @@ describe('Flags', () => {
assert.strictEqual('wip', state);
});

it('should rescind notification if flag is resolved', async () => {
const flagsAPI = require('../src/api/flags');
const result = await Topics.post({
cid: category.cid,
uid: uid3,
title: 'Topic to flag',
content: 'This is flaggable content',
describe('resolve/reject', () => {
let result;
let flagObj;
beforeEach(async () => {
result = await Topics.post({
cid: category.cid,
uid: uid3,
title: 'Topic to flag',
content: 'This is flaggable content',
});
flagObj = await api.flags.create({ uid: uid1 }, { type: 'post', id: result.postData.pid, reason: 'spam' });
await sleep(2000);
});
const flagObj = await flagsAPI.create({ uid: uid1 }, { type: 'post', id: result.postData.pid, reason: 'spam' });
await sleep(2000);

let userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));
it('should rescind notification if flag is resolved', async () => {
let userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

await Flags.update(flagObj.flagId, adminUid, {
state: 'resolved',
});

userNotifs = await User.notifications.getAll(adminUid);
assert(!userNotifs.includes(`flag:post:${result.postData.pid}`));
});

it('should rescind notification if flag is rejected', async () => {
let userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

await Flags.update(flagObj.flagId, adminUid, {
state: 'rejected',
});

await Flags.update(flagObj.flagId, adminUid, {
state: 'resolved',
userNotifs = await User.notifications.getAll(adminUid);
assert(!userNotifs.includes(`flag:post:${result.postData.pid}`));
});

userNotifs = await User.notifications.getAll(adminUid);
assert(!userNotifs.includes(`flag:post:${result.postData.pid}`));
it('should do nothing if flag is resolved but ACP action is not "rescind"', async () => {
Meta.config['flags:actionOnResolve'] = '';

let userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

await Flags.update(flagObj.flagId, adminUid, {
state: 'resolved',
});

userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

delete Meta.config['flags:actionOnResolve'];
});

it('should do nothing if flag is rejected but ACP action is not "rescind"', async () => {
Meta.config['flags:actionOnReject'] = '';

let userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

await Flags.update(flagObj.flagId, adminUid, {
state: 'rejected',
});

userNotifs = await User.notifications.getAll(adminUid);
assert(userNotifs.includes(`flag:post:${result.postData.pid}`));

delete Meta.config['flags:actionOnReject'];
});
});
});

Expand Down

0 comments on commit 15b1561

Please sign in to comment.