mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-03 22:30:58 +01:00
a bunch of changes here... allowing user profiles to be flagged, #5232
This commit is contained in:
@@ -29,6 +29,9 @@
|
||||
"start-new-chat": "Start New Chat",
|
||||
"go-to-target": "View Flag Target",
|
||||
|
||||
"user-view": "View Profile",
|
||||
"user-edit": "Edit Profile",
|
||||
|
||||
"notes": "Flag Notes",
|
||||
"add-note": "Add Note",
|
||||
"no-notes": "No shared notes.",
|
||||
@@ -43,5 +46,13 @@
|
||||
"state-resolved": "Resolved",
|
||||
"state-rejected": "Rejected",
|
||||
"no-assignee": "Not Assigned",
|
||||
"note-added": "Note Added"
|
||||
"note-added": "Note Added",
|
||||
|
||||
"modal-title": "Report Inappropriate Content",
|
||||
"modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.",
|
||||
"modal-reason-spam": "Spam",
|
||||
"modal-reason-offensive": "Offensive",
|
||||
"modal-reason-custom": "Reason for reporting this content...",
|
||||
"modal-submit": "Submit Report",
|
||||
"modal-submit-success": "Content has been flagged for moderation."
|
||||
}
|
||||
@@ -21,6 +21,9 @@
|
||||
"user_flagged_post_in": "<strong>%1</strong> flagged a post in <strong>%2</strong>",
|
||||
"user_flagged_post_in_dual": "<strong>%1</strong> and <strong>%2</strong> flagged a post in <strong>%3</strong>",
|
||||
"user_flagged_post_in_multiple": "<strong>%1</strong> and %2 others flagged a post in <strong>%3</strong>",
|
||||
"user_flagged_user": "<strong>%1</strong> flagged a user profile (%2)",
|
||||
"user_flagged_user_dual": "<strong>%1</strong> and <strong>%2</strong> flagged a user profile (%3)",
|
||||
"user_flagged_user_multiple": "<strong>%1</strong> and %2 others flagged a user profile (%3)",
|
||||
"user_posted_to" : "<strong>%1</strong> has posted a reply to: <strong>%2</strong>",
|
||||
"user_posted_to_dual" : "<strong>%1</strong> and <strong>%2</strong> have posted replies to: <strong>%3</strong>",
|
||||
"user_posted_to_multiple" : "<strong>%1</strong> and %2 others have posted replies to: <strong>%3</strong>",
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"link": "Link",
|
||||
"share": "Share",
|
||||
"tools": "Tools",
|
||||
"flag": "Flag",
|
||||
"locked": "Locked",
|
||||
"pinned": "Pinned",
|
||||
"moved": "Moved",
|
||||
@@ -36,7 +35,6 @@
|
||||
"bookmark_instructions" : "Click here to return to the last read post in this thread.",
|
||||
|
||||
"flag_title": "Flag this post for moderation",
|
||||
"flag_success": "This post has been flagged for moderation.",
|
||||
|
||||
"deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.",
|
||||
|
||||
@@ -138,10 +136,5 @@
|
||||
"stale.create": "Create a new topic",
|
||||
"stale.reply_anyway": "Reply to this topic anyway",
|
||||
|
||||
"link_back": "Re: [%1](%2)\n\n",
|
||||
|
||||
"spam": "Spam",
|
||||
"offensive": "Offensive",
|
||||
"custom-flag-reason": "Enter a flagging reason"
|
||||
|
||||
"link_back": "Re: [%1](%2)\n\n"
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"chat": "Chat",
|
||||
"chat_with": "Continue chat with %1",
|
||||
"new_chat_with": "Start new chat with %1",
|
||||
"flag-profile": "Flag Profile",
|
||||
"follow": "Follow",
|
||||
"unfollow": "Unfollow",
|
||||
"more": "More",
|
||||
|
||||
@@ -49,6 +49,7 @@ define('forum/account/header', [
|
||||
components.get('account/ban').on('click', banAccount);
|
||||
components.get('account/unban').on('click', unbanAccount);
|
||||
components.get('account/delete').on('click', deleteAccount);
|
||||
components.get('account/flag').on('click', flagAccount);
|
||||
};
|
||||
|
||||
function hidePrivateLinks() {
|
||||
@@ -167,6 +168,15 @@ define('forum/account/header', [
|
||||
});
|
||||
}
|
||||
|
||||
function flagAccount() {
|
||||
require(['flags'], function (flags) {
|
||||
flags.showFlagModal({
|
||||
type: 'user',
|
||||
id: ajaxify.data.uid
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeCover() {
|
||||
socket.emit('user.removeCover', {
|
||||
uid: ajaxify.data.uid
|
||||
|
||||
@@ -23,6 +23,8 @@ define('forum/flags/list', ['components'], function (components) {
|
||||
var qs = payload.map(function (filter) {
|
||||
if (filter.value) {
|
||||
return filter.name + '=' + filter.value;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}).filter(Boolean).join('&');
|
||||
|
||||
|
||||
@@ -167,10 +167,11 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
|
||||
postContainer.on('click', '[component="post/flag"]', function () {
|
||||
var pid = getData($(this), 'data-pid');
|
||||
var username = getData($(this), 'data-username');
|
||||
var userslug = getData($(this), 'data-userslug');
|
||||
require(['forum/topic/flag'], function (flag) {
|
||||
flag.showFlagModal(pid, username, userslug);
|
||||
require(['flags'], function (flags) {
|
||||
flags.showFlagModal({
|
||||
type: 'post',
|
||||
id: pid
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
|
||||
/* globals define, app, socket, templates */
|
||||
|
||||
define('forum/topic/flag', [], function () {
|
||||
|
||||
define('flags', [], function () {
|
||||
var Flag = {},
|
||||
flagModal,
|
||||
flagCommit;
|
||||
|
||||
Flag.showFlagModal = function (pid, username, userslug) {
|
||||
parseModal({
|
||||
pid: pid,
|
||||
username: username,
|
||||
userslug: userslug
|
||||
}, function (html) {
|
||||
Flag.showFlagModal = function (data) {
|
||||
parseModal(data, function (html) {
|
||||
flagModal = $(html);
|
||||
|
||||
flagModal.on('hidden.bs.modal', function () {
|
||||
@@ -23,11 +18,11 @@ define('forum/topic/flag', [], function () {
|
||||
flagCommit = flagModal.find('#flag-post-commit');
|
||||
|
||||
flagModal.on('click', '.flag-reason', function () {
|
||||
flagPost(pid, $(this).text());
|
||||
createFlag(data.type, data.id, $(this).text());
|
||||
});
|
||||
|
||||
flagCommit.on('click', function () {
|
||||
flagPost(pid, flagModal.find('#flag-reason-custom').val());
|
||||
createFlag(data.type, data.id, flagModal.find('#flag-reason-custom').val());
|
||||
});
|
||||
|
||||
flagModal.modal('show');
|
||||
@@ -37,24 +32,24 @@ define('forum/topic/flag', [], function () {
|
||||
};
|
||||
|
||||
function parseModal(tplData, callback) {
|
||||
templates.parse('partials/modals/flag_post_modal', tplData, function (html) {
|
||||
templates.parse('partials/modals/flag_modal', tplData, function (html) {
|
||||
require(['translator'], function (translator) {
|
||||
translator.translate(html, callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function flagPost(pid, reason) {
|
||||
if (!pid || !reason) {
|
||||
function createFlag(type, id, reason) {
|
||||
if (!type || !id || !reason) {
|
||||
return;
|
||||
}
|
||||
socket.emit('flags.create', {type: 'post', id: pid, reason: reason}, function (err) {
|
||||
socket.emit('flags.create', {type: type, id: id, reason: reason}, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
flagModal.modal('hide');
|
||||
app.alertSuccess('[[topic:flag_success]]');
|
||||
app.alertSuccess('[[flags:modal-submit-success]]');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,7 +63,11 @@ modsController.flags.detail = function (req, res, next) {
|
||||
}
|
||||
|
||||
res.render('flags/detail', Object.assign(results.flagData, {
|
||||
assignees: results.assignees
|
||||
assignees: results.assignees,
|
||||
type_bool: ['post', 'user'].reduce(function (memo, cur) {
|
||||
memo[cur] = results.flagData.type === cur;
|
||||
return memo;
|
||||
}, {})
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
76
src/flags.js
76
src/flags.js
@@ -167,20 +167,44 @@ Flags.validate = function (payload, callback) {
|
||||
switch (payload.type) {
|
||||
case 'post':
|
||||
async.parallel({
|
||||
privileges: async.apply(privileges.posts.get, [payload.id], payload.uid)
|
||||
editable: async.apply(privileges.posts.canEdit, payload.id, payload.uid)
|
||||
}, function (err, subdata) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1;
|
||||
if (!subdata.privileges[0].isAdminOrMod && parseInt(data.reporter.reputation, 10) < minimumReputation) {
|
||||
// Check if reporter meets rep threshold (or can edit the target post, in which case threshold does not apply)
|
||||
if (!subdata.editable.flag && parseInt(data.reporter.reputation, 10) < minimumReputation) {
|
||||
return callback(new Error('[[error:not-enough-reputation-to-flag]]'));
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
async.parallel({
|
||||
editable: async.apply(privileges.users.canEdit, payload.uid, payload.id)
|
||||
}, function (err, subdata) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
var minimumReputation = utils.isNumber(meta.config['privileges:flag']) ? parseInt(meta.config['privileges:flag'], 10) : 1;
|
||||
// Check if reporter meets rep threshold (or can edit the target user, in which case threshold does not apply)
|
||||
if (!subdata.editable && parseInt(data.reporter.reputation, 10) < minimumReputation) {
|
||||
return callback(new Error('[[error:not-enough-reputation-to-flag]]'));
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
callback(new Error('[[error:invalid-data]]'));
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -369,13 +393,17 @@ Flags.exists = function (type, id, uid, callback) {
|
||||
|
||||
Flags.targetExists = function (type, id, callback) {
|
||||
switch (type) {
|
||||
case 'topic': // just an example...
|
||||
topics.exists(id, callback);
|
||||
break;
|
||||
|
||||
case 'post':
|
||||
posts.exists(id, callback);
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
user.exists(id, callback);
|
||||
break;
|
||||
|
||||
default:
|
||||
callback(new Error('[[error:invalid-data]]'));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -384,6 +412,10 @@ Flags.getTargetUid = function (type, id, callback) {
|
||||
case 'post':
|
||||
posts.getPostField(id, 'uid', callback);
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
setImmediate(callback, null, id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -553,7 +585,7 @@ Flags.notify = function (flagObj, uid, callback) {
|
||||
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:user_flagged_post_in, ' + flagObj.reporter.username + ', ' + titleEscaped + ']]',
|
||||
bodyLong: results.post.content,
|
||||
bodyLong: flagObj.description,
|
||||
pid: flagObj.targetId,
|
||||
path: '/post/' + flagObj.targetId,
|
||||
nid: 'flag:post:' + flagObj.targetId + ':uid:' + uid,
|
||||
@@ -570,6 +602,36 @@ Flags.notify = function (flagObj, uid, callback) {
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'user':
|
||||
async.parallel({
|
||||
admins: async.apply(groups.getMembers, 'administrators', 0, -1),
|
||||
globalMods: async.apply(groups.getMembers, 'Global Moderators', 0, -1),
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:user_flagged_user, ' + flagObj.reporter.username + ', ' + flagObj.target.username + ']]',
|
||||
bodyLong: flagObj.description,
|
||||
path: '/uid/' + flagObj.targetId,
|
||||
nid: 'flag:user:' + flagObj.targetId + ':uid:' + uid,
|
||||
from: uid,
|
||||
mergeId: 'notifications:user_flagged_user|' + flagObj.targetId
|
||||
}, function (err, notification) {
|
||||
if (err || !notification) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
notifications.push(notification, results.admins.concat(results.globalMods), callback);
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
callback(new Error('[[error:invalid-data]]'));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ module.exports = function (Meta) {
|
||||
'public/src/client/unread.js',
|
||||
'public/src/client/topic.js',
|
||||
'public/src/client/topic/events.js',
|
||||
'public/src/client/topic/flag.js',
|
||||
'public/src/client/topic/fork.js',
|
||||
'public/src/client/topic/move.js',
|
||||
'public/src/client/topic/posts.js',
|
||||
@@ -72,7 +71,8 @@ module.exports = function (Meta) {
|
||||
'public/src/modules/taskbar.js',
|
||||
'public/src/modules/helpers.js',
|
||||
'public/src/modules/sounds.js',
|
||||
'public/src/modules/string.js'
|
||||
'public/src/modules/string.js',
|
||||
'public/src/modules/flags.js'
|
||||
],
|
||||
|
||||
// modules listed below are routed through express (/src/modules) so they can be defined anonymously
|
||||
|
||||
@@ -415,6 +415,7 @@ var utils = require('../public/src/utils');
|
||||
'notifications:user_started_following_you',
|
||||
'notifications:user_posted_to',
|
||||
'notifications:user_flagged_post_in',
|
||||
'notifications:user_flagged_user',
|
||||
'new_register'
|
||||
],
|
||||
isolated, differentiators, differentiator, modifyIndex, set;
|
||||
@@ -462,6 +463,7 @@ var utils = require('../public/src/utils');
|
||||
case 'notifications:user_started_following_you':
|
||||
case 'notifications:user_posted_to':
|
||||
case 'notifications:user_flagged_post_in':
|
||||
case 'notifications:user_flagged_user':
|
||||
var usernames = set.map(function (notifObj) {
|
||||
return notifObj && notifObj.user && notifObj.user.username;
|
||||
}).filter(function (username, idx, array) {
|
||||
|
||||
@@ -32,6 +32,11 @@ describe('Flags', function () {
|
||||
content: 'This is flaggable content'
|
||||
}, next);
|
||||
});
|
||||
},
|
||||
function (topicData, next) {
|
||||
User.create({
|
||||
username: 'testUser2', password: 'abcdef', email: 'c@d.com'
|
||||
}, next);
|
||||
}
|
||||
], done);
|
||||
});
|
||||
@@ -212,7 +217,7 @@ describe('Flags', function () {
|
||||
Flags.validate({
|
||||
type: 'post',
|
||||
id: 1,
|
||||
uid: 1
|
||||
uid: 2
|
||||
}, function (err) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual('[[error:not-enough-reputation-to-flag]]', err.message);
|
||||
@@ -305,6 +310,10 @@ describe('Flags', function () {
|
||||
assert.ifError(err);
|
||||
|
||||
Flags.getHistory(1, function (err, history) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
assert.strictEqual(entries + 1, history.length);
|
||||
done();
|
||||
});
|
||||
@@ -315,6 +324,7 @@ describe('Flags', function () {
|
||||
describe('.getHistory()', function () {
|
||||
it('should retrieve a flag\'s history', function (done) {
|
||||
Flags.getHistory(1, function (err, history) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(history[0].fields[0].value, '[[flags:state-rejected]]');
|
||||
done();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user