mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-07-09 12:33:04 +02:00
closes #3107
This commit is contained in:
@@ -32,7 +32,6 @@
|
||||
"bookmark_instructions" : "Click here to return to the last unread post in this thread.",
|
||||
|
||||
"flag_title": "Flag this post for moderation",
|
||||
"flag_confirm": "Are you sure you want to flag this post?",
|
||||
"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.",
|
||||
|
||||
@@ -117,5 +116,10 @@
|
||||
"most_votes": "Most votes",
|
||||
"most_posts": "Most posts",
|
||||
|
||||
"stale_topic_warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?"
|
||||
"stale_topic_warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
|
||||
|
||||
"spam": "Spam",
|
||||
"offensive": "Offensive",
|
||||
"custom-flag-reason": "Enter a flagging reason"
|
||||
|
||||
}
|
||||
|
||||
64
public/src/client/topic/flag.js
Normal file
64
public/src/client/topic/flag.js
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, app, socket, templates, translator */
|
||||
|
||||
define('forum/topic/flag', [], function() {
|
||||
|
||||
var Flag = {},
|
||||
flagModal,
|
||||
flagCommit;
|
||||
|
||||
Flag.showFlagModal = function(pid) {
|
||||
parseModal(function(html) {
|
||||
flagModal = $(html);
|
||||
|
||||
flagModal.on('hidden.bs.modal', function() {
|
||||
flagModal.remove();
|
||||
});
|
||||
|
||||
flagCommit = flagModal.find('#flag-post-commit');
|
||||
|
||||
flagModal.on('click', '.flag-reason', function() {
|
||||
flagPost(pid, $(this).text());
|
||||
});
|
||||
|
||||
flagCommit.on('click', function() {
|
||||
flagPost(pid, flagModal.find('#flag-reason-custom').val());
|
||||
});
|
||||
|
||||
flagModal.modal('show');
|
||||
|
||||
flagModal.find('#flag-reason-custom').on('keyup blur change', checkFlagButtonEnable);
|
||||
});
|
||||
};
|
||||
|
||||
function parseModal(callback) {
|
||||
templates.parse('partials/modals/flag_post_modal', {}, function(html) {
|
||||
translator.translate(html, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function flagPost(pid, reason) {
|
||||
if (!pid || !reason) {
|
||||
return;
|
||||
}
|
||||
socket.emit('posts.flag', {pid: pid, reason: reason}, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
flagModal.modal('hide');
|
||||
app.alertSuccess('[[topic:flag_success]]');
|
||||
});
|
||||
}
|
||||
|
||||
function checkFlagButtonEnable() {
|
||||
if (flagModal.find('#flag-reason-custom').val()) {
|
||||
flagCommit.removeAttr('disabled');
|
||||
} else {
|
||||
flagCommit.attr('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
return Flag;
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* globals define, app, ajaxify, bootbox, socket, templates, utils */
|
||||
/* globals define, app, ajaxify, bootbox, socket, templates, utils, config */
|
||||
|
||||
define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator'], function(share, navigator, components, translator) {
|
||||
|
||||
@@ -110,33 +110,36 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/flag"]', function() {
|
||||
flagPost(getData($(this), 'data-pid'));
|
||||
var pid = getData($(this), 'data-pid');
|
||||
require(['forum/topic/flag'], function(flag) {
|
||||
flag.showFlagModal(pid);
|
||||
});
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/edit"]', function(e) {
|
||||
postContainer.on('click', '[component="post/edit"]', function() {
|
||||
var btn = $(this);
|
||||
$(window).trigger('action:composer.post.edit', {
|
||||
pid: getData(btn, 'data-pid')
|
||||
});
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/delete"]', function(e) {
|
||||
postContainer.on('click', '[component="post/delete"]', function() {
|
||||
togglePostDelete($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/restore"]', function(e) {
|
||||
postContainer.on('click', '[component="post/restore"]', function() {
|
||||
togglePostDelete($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/purge"]', function(e) {
|
||||
postContainer.on('click', '[component="post/purge"]', function() {
|
||||
purgePost($(this), tid);
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/move"]', function(e) {
|
||||
postContainer.on('click', '[component="post/move"]', function() {
|
||||
openMovePostModal($(this));
|
||||
});
|
||||
|
||||
postContainer.on('click', '[component="post/chat"]', function(e) {
|
||||
postContainer.on('click', '[component="post/chat"]', function() {
|
||||
openChat($(this));
|
||||
});
|
||||
}
|
||||
@@ -369,22 +372,7 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
|
||||
});
|
||||
}
|
||||
|
||||
function flagPost(pid) {
|
||||
translator.translate('[[topic:flag_confirm]]', function(message) {
|
||||
bootbox.confirm(message, function(confirm) {
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
socket.emit('posts.flag', pid, function(err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
app.alertSuccess('[[topic:flag_success]]');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openChat(button) {
|
||||
var post = button.parents('[data-pid]');
|
||||
|
||||
@@ -9,7 +9,10 @@ var async = require('async'),
|
||||
|
||||
module.exports = function(Posts) {
|
||||
|
||||
Posts.flag = function(post, uid, callback) {
|
||||
Posts.flag = function(post, uid, reason, callback) {
|
||||
if (!parseInt(uid, 10) || !reason) {
|
||||
return callback();
|
||||
}
|
||||
async.parallel({
|
||||
hasFlagged: async.apply(hasFlagged, post.pid, uid),
|
||||
exists: async.apply(Posts.exists, post.pid)
|
||||
@@ -36,6 +39,9 @@ module.exports = function(Posts) {
|
||||
function(next) {
|
||||
db.sortedSetAdd('pid:' + post.pid + ':flag:uids', now, uid, next);
|
||||
},
|
||||
function(next) {
|
||||
db.sortedSetAdd('pid:' + post.pid + ':flag:uid:reason', 0, uid + ':' + reason, next);
|
||||
},
|
||||
function(next) {
|
||||
if (parseInt(post.uid, 10)) {
|
||||
db.sortedSetAdd('uid:' + post.uid + ':flag:pids', now, post.pid, next);
|
||||
@@ -50,7 +56,7 @@ module.exports = function(Posts) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
], function(err, results) {
|
||||
], function(err) {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
@@ -80,8 +86,11 @@ module.exports = function(Posts) {
|
||||
},
|
||||
function(next) {
|
||||
db.delete('pid:' + pid + ':flag:uids', next);
|
||||
},
|
||||
function(next) {
|
||||
db.delete('pid:' + pid + ':flag:uid:reason', next);
|
||||
}
|
||||
], function(err, results) {
|
||||
], function(err) {
|
||||
callback(err);
|
||||
});
|
||||
};
|
||||
@@ -96,15 +105,56 @@ module.exports = function(Posts) {
|
||||
};
|
||||
|
||||
Posts.getFlags = function(set, uid, start, stop, callback) {
|
||||
db.getSortedSetRevRange(set, start, stop, function(err, pids) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRange(set, start, stop, next);
|
||||
},
|
||||
function (pids, next) {
|
||||
getFlaggedPostsWithReasons(pids, uid, next);
|
||||
}
|
||||
|
||||
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags']}, callback);
|
||||
});
|
||||
], callback);
|
||||
};
|
||||
|
||||
function getFlaggedPostsWithReasons(pids, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
uidsReasons: function(next) {
|
||||
async.map(pids, function(pid, next) {
|
||||
db.getSortedSetRange('pid:' + pid + ':flag:uid:reason', 0, -1, next);
|
||||
}, next);
|
||||
},
|
||||
posts: function(next) {
|
||||
Posts.getPostSummaryByPids(pids, uid, {stripTags: false, extraFields: ['flags']}, next);
|
||||
}
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
async.map(results.uidsReasons, function(uidReasons, next) {
|
||||
async.map(uidReasons, function(uidReason, next) {
|
||||
var uid = uidReason.split(':')[0];
|
||||
var reason = uidReason.substr(uidReason.indexOf(':') + 1);
|
||||
user.getUserFields(uid, ['username', 'userslug', 'picture'], function(err, userData) {
|
||||
next(err, {user: userData, reason: reason});
|
||||
});
|
||||
}, next);
|
||||
}, function(err, reasons) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
results.posts.forEach(function(post, index) {
|
||||
if (post) {
|
||||
post.flagReasons = reasons[index];
|
||||
}
|
||||
});
|
||||
|
||||
next(null, results.posts);
|
||||
});
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
Posts.getUserFlags = function(byUsername, sortBy, callerUID, start, stop, callback) {
|
||||
async.waterfall([
|
||||
function(next) {
|
||||
@@ -117,7 +167,7 @@ module.exports = function(Posts) {
|
||||
db.getSortedSetRevRange('uid:' + uid + ':flag:pids', 0, -1, next);
|
||||
},
|
||||
function(pids, next) {
|
||||
Posts.getPostSummaryByPids(pids, callerUID, {stripTags: false, extraFields: ['flags']}, next);
|
||||
getFlaggedPostsWithReasons(pids, callerUID, next);
|
||||
},
|
||||
function(posts, next) {
|
||||
if (sortBy === 'count') {
|
||||
@@ -125,6 +175,7 @@ module.exports = function(Posts) {
|
||||
return b.flags - a.flags;
|
||||
});
|
||||
}
|
||||
|
||||
next(null, posts.slice(start, stop));
|
||||
}
|
||||
], callback);
|
||||
|
||||
@@ -13,17 +13,21 @@ var meta = require('../../meta');
|
||||
|
||||
module.exports = function(SocketPosts) {
|
||||
|
||||
SocketPosts.flag = function(socket, pid, callback) {
|
||||
SocketPosts.flag = function(socket, data, callback) {
|
||||
if (!socket.uid) {
|
||||
return callback(new Error('[[error:not-logged-in]]'));
|
||||
}
|
||||
|
||||
if (!data || !data.pid || !data.reason) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
var flaggingUser = {},
|
||||
post;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next);
|
||||
posts.getPostFields(data.pid, ['pid', 'tid', 'uid', 'content', 'deleted'], next);
|
||||
},
|
||||
function (postData, next) {
|
||||
if (parseInt(postData.deleted, 10) === 1) {
|
||||
@@ -55,7 +59,7 @@ module.exports = function(SocketPosts) {
|
||||
flaggingUser = user.userData;
|
||||
flaggingUser.uid = socket.uid;
|
||||
|
||||
posts.flag(post, socket.uid, next);
|
||||
posts.flag(post, socket.uid, data.reason, next);
|
||||
},
|
||||
function (next) {
|
||||
async.parallel({
|
||||
@@ -74,8 +78,8 @@ module.exports = function(SocketPosts) {
|
||||
notifications.create({
|
||||
bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + post.topic.title + ']]',
|
||||
bodyLong: post.content,
|
||||
pid: pid,
|
||||
nid: 'post_flag:' + pid + ':uid:' + socket.uid,
|
||||
pid: data.pid,
|
||||
nid: 'post_flag:' + data.pid + ':uid:' + socket.uid,
|
||||
from: socket.uid
|
||||
}, function(err, notification) {
|
||||
if (err || !notification) {
|
||||
|
||||
@@ -58,6 +58,11 @@
|
||||
</div>
|
||||
|
||||
<span class="badge badge-warning"><i class="fa fa-flag"></i> {posts.flags}</span>
|
||||
<br/>
|
||||
<!-- BEGIN posts.flagReasons -->
|
||||
<a target="_blank" href="{config.relative_path}/user/{../user.userslug}"><img class="user-img" src="{../user.picture}">{../user.username}</a>: "{../reason}" <br/>
|
||||
<!-- END posts.flagReasons -->
|
||||
<br/>
|
||||
<button class="btn btn-warning dismiss">Dismiss</button>
|
||||
<button class="btn btn-danger delete">Delete</button>
|
||||
<br/><br/>
|
||||
|
||||
Reference in New Issue
Block a user