mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-21 16:00:26 +01:00
closes #4820
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
"users/sort-posts": "Users with the most posts",
|
"users/sort-posts": "Users with the most posts",
|
||||||
"users/sort-reputation": "Users with the most reputation",
|
"users/sort-reputation": "Users with the most reputation",
|
||||||
"users/banned": "Banned Users",
|
"users/banned": "Banned Users",
|
||||||
|
"users/most-flags": "Most flagged users",
|
||||||
"users/search": "User Search",
|
"users/search": "User Search",
|
||||||
|
|
||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
"latest_users": "Latest Users",
|
"latest_users": "Latest Users",
|
||||||
"top_posters": "Top Posters",
|
"top_posters": "Top Posters",
|
||||||
"most_reputation": "Most Reputation",
|
"most_reputation": "Most Reputation",
|
||||||
|
"most_flags": "Most Flags",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"enter_username": "Enter a username to search",
|
"enter_username": "Enter a username to search",
|
||||||
"load_more": "Load More",
|
"load_more": "Load More",
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
/*global define, socket, app, admin, utils, bootbox, RELATIVE_PATH*/
|
/*global define, socket, app, utils, bootbox, ajaxify*/
|
||||||
|
|
||||||
define('admin/manage/flags', [
|
define('admin/manage/flags', [
|
||||||
'forum/infinitescroll',
|
'forum/infinitescroll',
|
||||||
'admin/modules/selectable',
|
'admin/modules/selectable',
|
||||||
'autocomplete'
|
'autocomplete',
|
||||||
], function(infinitescroll, selectable, autocomplete) {
|
'Chart'
|
||||||
|
], function(infinitescroll, selectable, autocomplete, Chart) {
|
||||||
|
|
||||||
var Flags = {};
|
var Flags = {};
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ define('admin/manage/flags', [
|
|||||||
handleDismissAll();
|
handleDismissAll();
|
||||||
handleDelete();
|
handleDelete();
|
||||||
handleInfiniteScroll();
|
handleInfiniteScroll();
|
||||||
|
handleGraphs();
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleDismiss() {
|
function handleDismiss() {
|
||||||
@@ -101,5 +103,42 @@ define('admin/manage/flags', [
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGraphs() {
|
||||||
|
var dailyCanvas = document.getElementById('flags:daily');
|
||||||
|
var dailyLabels = utils.getDaysArray().map(function(text, idx) {
|
||||||
|
return idx % 3 ? '' : text;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (utils.isMobile()) {
|
||||||
|
Chart.defaults.global.showTooltips = false;
|
||||||
|
}
|
||||||
|
var data = {
|
||||||
|
'flags:daily': {
|
||||||
|
labels: dailyLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "",
|
||||||
|
fillColor: "rgba(151,187,205,0.2)",
|
||||||
|
strokeColor: "rgba(151,187,205,1)",
|
||||||
|
pointColor: "rgba(151,187,205,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||||
|
data: ajaxify.data.analytics
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dailyCanvas.width = $(dailyCanvas).parent().width();
|
||||||
|
new Chart(dailyCanvas.getContext('2d')).Line(data['flags:daily'], {
|
||||||
|
responsive: true,
|
||||||
|
animation: false
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return Flags;
|
return Flags;
|
||||||
});
|
});
|
||||||
@@ -102,14 +102,15 @@ define('forum/users', ['translator'], function(translator) {
|
|||||||
if (!username) {
|
if (!username) {
|
||||||
return loadPage(page);
|
return loadPage(page);
|
||||||
}
|
}
|
||||||
|
var activeSection = getActiveSection();
|
||||||
socket.emit('user.search', {
|
socket.emit('user.search', {
|
||||||
query: username,
|
query: username,
|
||||||
page: page,
|
page: page,
|
||||||
searchBy: 'username',
|
searchBy: 'username',
|
||||||
sortBy: $('.search select').val() || getSortBy(),
|
sortBy: $('.search select').val() || getSortBy(),
|
||||||
onlineOnly: $('.search .online-only').is(':checked') || (getActiveSection() === 'online'),
|
onlineOnly: $('.search .online-only').is(':checked') || (activeSection === 'online'),
|
||||||
bannedOnly: getActiveSection() === 'banned'
|
bannedOnly: activeSection === 'banned',
|
||||||
|
flaggedOnly: activeSection === 'flagged'
|
||||||
}, function(err, data) {
|
}, function(err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
@@ -168,8 +169,8 @@ define('forum/users', ['translator'], function(translator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getActiveSection() {
|
function getActiveSection() {
|
||||||
var url = window.location.href,
|
var url = window.location.href;
|
||||||
parts = url.split('/');
|
var parts = url.split('/');
|
||||||
return parts[parts.length - 1];
|
return parts[parts.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var posts = require('../../posts');
|
var posts = require('../../posts');
|
||||||
|
var analytics = require('../../analytics');
|
||||||
|
|
||||||
var flagsController = {};
|
var flagsController = {};
|
||||||
|
|
||||||
@@ -13,19 +14,27 @@ flagsController.get = function(req, res, next) {
|
|||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
if (byUsername) {
|
async.parallel({
|
||||||
posts.getUserFlags(byUsername, sortBy, req.uid, start, stop, next);
|
posts: function(next) {
|
||||||
} else {
|
if (byUsername) {
|
||||||
var set = sortBy === 'count' ? 'posts:flags:count' : 'posts:flagged';
|
posts.getUserFlags(byUsername, sortBy, req.uid, start, stop, next);
|
||||||
posts.getFlags(set, req.uid, start, stop, next);
|
} else {
|
||||||
}
|
var set = sortBy === 'count' ? 'posts:flags:count' : 'posts:flagged';
|
||||||
|
posts.getFlags(set, req.uid, start, stop, next);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
analytics: function(next) {
|
||||||
|
analytics.getDailyStatsForSet('analytics:flags', Date.now(), 30, next);
|
||||||
|
}
|
||||||
|
}, next);
|
||||||
}
|
}
|
||||||
], function (err, posts) {
|
], function (err, results) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
var data = {
|
var data = {
|
||||||
posts: posts,
|
posts: results.posts,
|
||||||
|
analytics: results.analytics,
|
||||||
next: stop + 1,
|
next: stop + 1,
|
||||||
byUsername: byUsername,
|
byUsername: byUsername,
|
||||||
title: '[[pages:flagged-posts]]'
|
title: '[[pages:flagged-posts]]'
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ usersController.noPosts = function(req, res, next) {
|
|||||||
getUsersByScore('users:postcount', 'noposts', 0, 0, req, res, next);
|
getUsersByScore('users:postcount', 'noposts', 0, 0, req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
usersController.flagged = function(req, res, next) {
|
||||||
|
getUsersByScore('users:flags', 'mostflags', 1, '+inf', req, res, next);
|
||||||
|
};
|
||||||
|
|
||||||
usersController.inactive = function(req, res, next) {
|
usersController.inactive = function(req, res, next) {
|
||||||
var timeRange = 1000 * 60 * 60 * 24 * 30 * (parseInt(req.query.months, 10) || 3);
|
var timeRange = 1000 * 60 * 60 * 24 * 30 * (parseInt(req.query.months, 10) || 3);
|
||||||
var cutoff = Date.now() - timeRange;
|
var cutoff = Date.now() - timeRange;
|
||||||
|
|||||||
@@ -69,6 +69,20 @@ usersController.getBannedUsers = function(req, res, next) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
usersController.getFlaggedUsers = function(req, res, next) {
|
||||||
|
usersController.getUsers('users:flags', req.uid, req.query.page, function(err, userData) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userData.isAdminOrGlobalMod) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(req, res, userData, next);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
usersController.renderUsersPage = function(set, req, res, next) {
|
usersController.renderUsersPage = function(set, req, res, next) {
|
||||||
usersController.getUsers(set, req.uid, req.query.page, function(err, userData) {
|
usersController.getUsers(set, req.uid, req.query.page, function(err, userData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -79,23 +93,16 @@ usersController.renderUsersPage = function(set, req, res, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
usersController.getUsers = function(set, uid, page, callback) {
|
usersController.getUsers = function(set, uid, page, callback) {
|
||||||
var setToTitles = {
|
var setToData = {
|
||||||
'users:postcount': '[[pages:users/sort-posts]]',
|
'users:postcount': {title: '[[pages:users/sort-posts]]', crumb: '[[users:top_posters]]'},
|
||||||
'users:reputation': '[[pages:users/sort-reputation]]',
|
'users:reputation': {title: '[[pages:users/sort-reputation]]', crumb: '[[users:most_reputation]]'},
|
||||||
'users:joindate': '[[pages:users/latest]]',
|
'users:joindate': {title: '[[pages:users/latest]]', crumb: '[[global:users]]'},
|
||||||
'users:online': '[[pages:users/online]]',
|
'users:online': {title: '[[pages:users/online]]', crumb: '[[global:online]]'},
|
||||||
'users:banned': '[[pages:users/banned]]'
|
'users:banned': {title: '[[pages:users/banned]]', crumb: '[[user:banned]]'},
|
||||||
|
'users:flags': {title: '[[pages:users/most-flags]]', crumb: '[[users:most_flags]]'},
|
||||||
};
|
};
|
||||||
|
|
||||||
var setToCrumbs = {
|
var breadcrumbs = [{text: setToData[set].crumb}];
|
||||||
'users:postcount': '[[users:top_posters]]',
|
|
||||||
'users:reputation': '[[users:most_reputation]]',
|
|
||||||
'users:joindate': '[[global:users]]',
|
|
||||||
'users:online': '[[global:online]]',
|
|
||||||
'users:banned': '[[user:banned]]'
|
|
||||||
};
|
|
||||||
|
|
||||||
var breadcrumbs = [{text: setToCrumbs[set]}];
|
|
||||||
|
|
||||||
if (set !== 'users:joindate') {
|
if (set !== 'users:joindate') {
|
||||||
breadcrumbs.unshift({text: '[[global:users]]', url: '/users'});
|
breadcrumbs.unshift({text: '[[global:users]]', url: '/users'});
|
||||||
@@ -127,7 +134,7 @@ usersController.getUsers = function(set, uid, page, callback) {
|
|||||||
users: results.usersData.users,
|
users: results.usersData.users,
|
||||||
pagination: pagination.create(page, pageCount),
|
pagination: pagination.create(page, pageCount),
|
||||||
userCount: results.usersData.count,
|
userCount: results.usersData.count,
|
||||||
title: setToTitles[set] || '[[pages:users/latest]]',
|
title: setToData[set].title || '[[pages:users/latest]]',
|
||||||
breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
|
breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs),
|
||||||
setName: set,
|
setName: set,
|
||||||
isAdminOrGlobalMod: results.isAdministrator || results.isGlobalMod
|
isAdminOrGlobalMod: results.isAdministrator || results.isGlobalMod
|
||||||
@@ -148,6 +155,8 @@ usersController.getUsersAndCount = function(set, uid, start, stop, callback) {
|
|||||||
db.sortedSetCount('users:online', now - 300000, '+inf', next);
|
db.sortedSetCount('users:online', now - 300000, '+inf', next);
|
||||||
} else if (set === 'users:banned') {
|
} else if (set === 'users:banned') {
|
||||||
db.sortedSetCard('users:banned', next);
|
db.sortedSetCard('users:banned', next);
|
||||||
|
} else if (set === 'users:flags') {
|
||||||
|
db.sortedSetCard('users:flags', next);
|
||||||
} else {
|
} else {
|
||||||
db.getObjectField('global', 'userCount', next);
|
db.getObjectField('global', 'userCount', next);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
|
var analytics = require('../analytics');
|
||||||
|
|
||||||
module.exports = function(Posts) {
|
module.exports = function(Posts) {
|
||||||
|
|
||||||
@@ -13,52 +13,59 @@ module.exports = function(Posts) {
|
|||||||
if (!parseInt(uid, 10) || !reason) {
|
if (!parseInt(uid, 10) || !reason) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
async.parallel({
|
|
||||||
hasFlagged: async.apply(hasFlagged, post.pid, uid),
|
|
||||||
exists: async.apply(Posts.exists, post.pid)
|
|
||||||
}, function(err, results) {
|
|
||||||
if (err || !results.exists) {
|
|
||||||
return callback(err || new Error('[[error:no-post]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.hasFlagged) {
|
async.waterfall([
|
||||||
return callback(new Error('[[error:already-flagged]]'));
|
function(next) {
|
||||||
}
|
async.parallel({
|
||||||
var now = Date.now();
|
hasFlagged: async.apply(hasFlagged, post.pid, uid),
|
||||||
|
exists: async.apply(Posts.exists, post.pid)
|
||||||
async.parallel([
|
}, next);
|
||||||
function(next) {
|
},
|
||||||
db.sortedSetAdd('posts:flagged', now, post.pid, next);
|
function(results, next) {
|
||||||
},
|
if (!results.exists) {
|
||||||
function(next) {
|
return next(new Error('[[error:no-post]]'));
|
||||||
db.sortedSetIncrBy('posts:flags:count', 1, post.pid, next);
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
db.incrObjectField('post:' + post.pid, 'flags', next);
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(next) {
|
|
||||||
if (parseInt(post.uid, 10)) {
|
|
||||||
db.setAdd('uid:' + post.uid + ':flagged_by', uid, next);
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
], function(err) {
|
|
||||||
callback(err);
|
if (results.hasFlagged) {
|
||||||
});
|
return next(new Error('[[error:already-flagged]]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = Date.now();
|
||||||
|
async.parallel([
|
||||||
|
function(next) {
|
||||||
|
db.sortedSetAdd('posts:flagged', now, post.pid, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
db.sortedSetIncrBy('posts:flags:count', 1, post.pid, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
db.incrObjectField('post:' + post.pid, 'flags', next);
|
||||||
|
},
|
||||||
|
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)) {
|
||||||
|
async.parallel([
|
||||||
|
async.apply(db.sortedSetIncrBy, 'users:flags', 1, post.uid),
|
||||||
|
async.apply(db.incrObjectField, 'user:' + post.uid, 'flags'),
|
||||||
|
async.apply(db.sortedSetAdd, 'uid:' + post.uid + ':flag:pids', now, post.pid)
|
||||||
|
], next);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
], next);
|
||||||
|
}
|
||||||
|
], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
analytics.increment('flags');
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,41 +74,58 @@ module.exports = function(Posts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Posts.dismissFlag = function(pid, callback) {
|
Posts.dismissFlag = function(pid, callback) {
|
||||||
async.parallel([
|
async.waterfall([
|
||||||
function(next) {
|
function(next) {
|
||||||
db.getObjectField('post:' + pid, 'uid', function(err, uid) {
|
db.getObjectFields('post:' + pid, ['pid', 'uid', 'flags'], next);
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.sortedSetsRemove([
|
|
||||||
'posts:flagged',
|
|
||||||
'posts:flags:count',
|
|
||||||
'uid:' + uid + ':flag:pids'
|
|
||||||
], pid, next);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
function(next) {
|
function(postData, next) {
|
||||||
async.series([
|
if (!postData.pid) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
async.parallel([
|
||||||
function(next) {
|
function(next) {
|
||||||
db.getSortedSetRange('pid:' + pid + ':flag:uids', 0, -1, function(err, uids) {
|
if (parseInt(postData.uid, 10)) {
|
||||||
async.each(uids, function(uid, next) {
|
if (parseInt(postData.flags, 10) > 0) {
|
||||||
var nid = 'post_flag:' + pid + ':uid:' + uid;
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
async.apply(db.delete, 'notifications:' + nid),
|
async.apply(db.sortedSetIncrBy, 'users:flags', -postData.flags, postData.uid),
|
||||||
async.apply(db.sortedSetRemove, 'notifications', 'post_flag:' + pid + ':uid:' + uid)
|
async.apply(db.incrObjectFieldBy, 'user:' + postData.uid, 'flags', -postData.flags)
|
||||||
], next);
|
], next);
|
||||||
}, next);
|
} else {
|
||||||
});
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async.apply(db.delete, 'pid:' + pid + ':flag:uids')
|
function(next) {
|
||||||
|
db.sortedSetsRemove([
|
||||||
|
'posts:flagged',
|
||||||
|
'posts:flags:count',
|
||||||
|
'uid:' + postData.uid + ':flag:pids'
|
||||||
|
], pid, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
async.series([
|
||||||
|
function(next) {
|
||||||
|
db.getSortedSetRange('pid:' + pid + ':flag:uids', 0, -1, function(err, uids) {
|
||||||
|
async.each(uids, function(uid, next) {
|
||||||
|
var nid = 'post_flag:' + pid + ':uid:' + uid;
|
||||||
|
async.parallel([
|
||||||
|
async.apply(db.delete, 'notifications:' + nid),
|
||||||
|
async.apply(db.sortedSetRemove, 'notifications', 'post_flag:' + pid + ':uid:' + uid)
|
||||||
|
], next);
|
||||||
|
}, next);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async.apply(db.delete, 'pid:' + pid + ':flag:uids')
|
||||||
|
], next);
|
||||||
|
},
|
||||||
|
async.apply(db.deleteObjectField, 'post:' + pid, 'flags'),
|
||||||
|
async.apply(db.delete, 'pid:' + pid + ':flag:uid:reason')
|
||||||
], next);
|
], next);
|
||||||
},
|
},
|
||||||
async.apply(db.deleteObjectField, 'post:' + pid, 'flags'),
|
function(results, next) {
|
||||||
async.apply(db.delete, 'pid:' + pid + ':flag:uid:reason')
|
db.sortedSetsRemoveRangeByScore(['users:flags'], '-inf', 0, next);
|
||||||
], function(err) {
|
}
|
||||||
callback(err);
|
], callback);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Posts.dismissAllFlags = function(callback) {
|
Posts.dismissAllFlags = function(callback) {
|
||||||
@@ -109,7 +133,16 @@ module.exports = function(Posts) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
async.eachLimit(pids, 50, Posts.dismissFlag, callback);
|
async.eachSeries(pids, Posts.dismissFlag, callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.dismissUserFlags = function(uid, callback) {
|
||||||
|
db.getSortedSetRange('uid:' + uid + ':flag:pids', 0, -1, function(err, pids) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
async.eachSeries(pids, Posts.dismissFlag, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ function addRoutes(router, middleware, controllers) {
|
|||||||
router.get('/manage/users/not-validated', middlewares, controllers.admin.users.notValidated);
|
router.get('/manage/users/not-validated', middlewares, controllers.admin.users.notValidated);
|
||||||
router.get('/manage/users/no-posts', middlewares, controllers.admin.users.noPosts);
|
router.get('/manage/users/no-posts', middlewares, controllers.admin.users.noPosts);
|
||||||
router.get('/manage/users/inactive', middlewares, controllers.admin.users.inactive);
|
router.get('/manage/users/inactive', middlewares, controllers.admin.users.inactive);
|
||||||
|
router.get('/manage/users/flagged', middlewares, controllers.admin.users.flagged);
|
||||||
router.get('/manage/users/banned', middlewares, controllers.admin.users.banned);
|
router.get('/manage/users/banned', middlewares, controllers.admin.users.banned);
|
||||||
router.get('/manage/registration', middlewares, controllers.admin.users.registrationQueue);
|
router.get('/manage/registration', middlewares, controllers.admin.users.registrationQueue);
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ function userRoutes(app, middleware, controllers) {
|
|||||||
setupPageRoute(app, '/users/sort-posts', middleware, middlewares, controllers.users.getUsersSortedByPosts);
|
setupPageRoute(app, '/users/sort-posts', middleware, middlewares, controllers.users.getUsersSortedByPosts);
|
||||||
setupPageRoute(app, '/users/sort-reputation', middleware, middlewares, controllers.users.getUsersSortedByReputation);
|
setupPageRoute(app, '/users/sort-reputation', middleware, middlewares, controllers.users.getUsersSortedByReputation);
|
||||||
setupPageRoute(app, '/users/banned', middleware, middlewares, controllers.users.getBannedUsers);
|
setupPageRoute(app, '/users/banned', middleware, middlewares, controllers.users.getBannedUsers);
|
||||||
|
setupPageRoute(app, '/users/flagged', middleware, middlewares, controllers.users.getFlaggedUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupRoutes(app, middleware, controllers) {
|
function groupRoutes(app, middleware, controllers) {
|
||||||
|
|||||||
@@ -185,25 +185,15 @@ User.search = function(socket, data, callback) {
|
|||||||
return user && user.uid;
|
return user && user.uid;
|
||||||
});
|
});
|
||||||
|
|
||||||
async.parallel({
|
user.getUsersFields(uids, ['email', 'flags'], function(err, userInfo) {
|
||||||
users: function(next) {
|
|
||||||
user.getUsersFields(uids, ['email'], next);
|
|
||||||
},
|
|
||||||
flagCounts: function(next) {
|
|
||||||
var sets = uids.map(function(uid) {
|
|
||||||
return 'uid:' + uid + ':flagged_by';
|
|
||||||
});
|
|
||||||
db.setsCount(sets, next);
|
|
||||||
}
|
|
||||||
}, function(err, results) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
userData.forEach(function(user, index) {
|
userData.forEach(function(user, index) {
|
||||||
if (user) {
|
if (user && userInfo[index]) {
|
||||||
user.email = (results.users[index] && results.users[index].email) || '';
|
user.email = userInfo[index].email || '';
|
||||||
user.flags = results.flagCounts[index] || 0;
|
user.flags = userInfo[index].flags || 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ module.exports = function(SocketUser) {
|
|||||||
sortBy: data.sortBy,
|
sortBy: data.sortBy,
|
||||||
onlineOnly: data.onlineOnly,
|
onlineOnly: data.onlineOnly,
|
||||||
bannedOnly: data.bannedOnly,
|
bannedOnly: data.bannedOnly,
|
||||||
|
flaggedOnly: data.flaggedOnly,
|
||||||
uid: socket.uid
|
uid: socket.uid
|
||||||
}, function(err, result) {
|
}, function(err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ var utils = require('../public/src/utils');
|
|||||||
};
|
};
|
||||||
|
|
||||||
User.getUsers = function(uids, uid, callback) {
|
User.getUsers = function(uids, uid, callback) {
|
||||||
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
|
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'flags',
|
||||||
|
'banned', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var db = require('../database');
|
var db = require('../database');
|
||||||
|
var posts = require('../posts');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
|
|
||||||
module.exports = function(User) {
|
module.exports = function(User) {
|
||||||
@@ -89,7 +90,6 @@ module.exports = function(User) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
User.unban = function(uid, callback) {
|
User.unban = function(uid, callback) {
|
||||||
db.delete('uid:' + uid + ':flagged_by');
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
User.setUserField(uid, 'banned', 0, next);
|
User.setUserField(uid, 'banned', 0, next);
|
||||||
@@ -108,9 +108,9 @@ module.exports = function(User) {
|
|||||||
if (!Array.isArray(uids) || !uids.length) {
|
if (!Array.isArray(uids) || !uids.length) {
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
var keys = uids.map(function(uid) {
|
|
||||||
return 'uid:' + uid + ':flagged_by';
|
async.eachSeries(uids, function(uid, next) {
|
||||||
});
|
posts.dismissUserFlags(uid, next);
|
||||||
db.deleteAll(keys, callback);
|
}, callback);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async'),
|
var async = require('async');
|
||||||
meta = require('../meta'),
|
var meta = require('../meta');
|
||||||
plugins = require('../plugins'),
|
var plugins = require('../plugins');
|
||||||
db = require('../database');
|
var db = require('../database');
|
||||||
|
|
||||||
module.exports = function(User) {
|
module.exports = function(User) {
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ module.exports = function(User) {
|
|||||||
function filterAndSortUids(uids, data, callback) {
|
function filterAndSortUids(uids, data, callback) {
|
||||||
var sortBy = data.sortBy || 'joindate';
|
var sortBy = data.sortBy || 'joindate';
|
||||||
|
|
||||||
var fields = ['uid', 'status', 'lastonline', 'banned', sortBy];
|
var fields = ['uid', 'status', 'lastonline', 'banned', 'flags', sortBy];
|
||||||
|
|
||||||
User.getUsersFields(uids, fields, function(err, userData) {
|
User.getUsersFields(uids, fields, function(err, userData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -97,12 +97,18 @@ module.exports = function(User) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(data.bannedOnly) {
|
if (data.bannedOnly) {
|
||||||
userData = userData.filter(function(user) {
|
userData = userData.filter(function(user) {
|
||||||
return user && user.banned;
|
return user && user.banned;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.flaggedOnly) {
|
||||||
|
userData = userData.filter(function(user) {
|
||||||
|
return user && parseInt(user.flags, 10) > 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sortUsers(userData, sortBy);
|
sortUsers(userData, sortBy);
|
||||||
|
|
||||||
uids = userData.map(function(user) {
|
uids = userData.map(function(user) {
|
||||||
|
|||||||
@@ -1,32 +1,48 @@
|
|||||||
<div class="flags">
|
<div class="flags">
|
||||||
<div class="col-lg-9">
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div><canvas id="flags:daily" height="250"></canvas></div>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer"><small>Daily flags</small></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="flag-search" method="GET" action="flags">
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<label>Flags by user</label>
|
||||||
|
<input type="text" class="form-control" id="byUsername" placeholder="Search flagged posts by username" name="byUsername" value="{byUsername}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Sort By</label>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<select id="flag-sort-by" class="form-control" name="sortBy">
|
||||||
|
<option value="count">Most Flags</option>
|
||||||
|
<option value="time">Most Recent</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
|
<button class="btn btn-primary" id="dismissAll">Dismiss All</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
<div data-next="{next}">
|
<div data-next="{next}">
|
||||||
<form id="flag-search" method="GET" action="flags">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label>Flags by user</label>
|
|
||||||
<input type="text" class="form-control" id="byUsername" placeholder="Search flagged posts by username" name="byUsername" value="{byUsername}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Sort By</label>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<select id="flag-sort-by" class="form-control" name="sortBy">
|
|
||||||
<option value="count">Most Flags</option>
|
|
||||||
<option value="time">Most Recent</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
|
||||||
</form>
|
|
||||||
<br />
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<div class="post-container" data-next="{next}">
|
<div class="post-container" data-next="{next}">
|
||||||
<!-- IF !posts.length -->
|
<!-- IF !posts.length -->
|
||||||
@@ -53,7 +69,6 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>{posts.content}</p>
|
<p>{posts.content}</p>
|
||||||
<p class="fade-out"></p>
|
|
||||||
</div>
|
</div>
|
||||||
<small>
|
<small>
|
||||||
<span class="pull-right">
|
<span class="pull-right">
|
||||||
@@ -92,15 +107,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-3 acp-sidebar">
|
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Flags Control Panel</div>
|
|
||||||
<div class="panel-body">
|
|
||||||
<div>
|
|
||||||
<button class="btn btn-primary" id="dismissAll">Dismiss All</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<li><a href='{config.relative_path}/admin/manage/users/not-validated'>Not validated</a></li>
|
<li><a href='{config.relative_path}/admin/manage/users/not-validated'>Not validated</a></li>
|
||||||
<li><a href='{config.relative_path}/admin/manage/users/no-posts'>No Posts</a></li>
|
<li><a href='{config.relative_path}/admin/manage/users/no-posts'>No Posts</a></li>
|
||||||
<li><a href='{config.relative_path}/admin/manage/users/inactive'>Inactive</a></li>
|
<li><a href='{config.relative_path}/admin/manage/users/inactive'>Inactive</a></li>
|
||||||
|
<li><a href='{config.relative_path}/admin/manage/users/flagged'>Most Flags</a></li>
|
||||||
<li><a href='{config.relative_path}/admin/manage/users/banned'>Banned</a></li>
|
<li><a href='{config.relative_path}/admin/manage/users/banned'>Banned</a></li>
|
||||||
<li><a href='{config.relative_path}/admin/manage/users/search'>User Search</a></li>
|
<li><a href='{config.relative_path}/admin/manage/users/search'>User Search</a></li>
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@
|
|||||||
posts {users.postcount}
|
posts {users.postcount}
|
||||||
|
|
||||||
<!-- IF users.flags -->
|
<!-- IF users.flags -->
|
||||||
<div><small><span><i class="fa fa-flag"></i> {users.flags}</span></small></div>
|
<div><small><span><i class="fa fa-flag"></i> <a href="{config.relative_path}/admin/manage/flags?byUsername={users.username}">{users.flags}</a></span></small></div>
|
||||||
<!-- ENDIF users.flags -->
|
<!-- ENDIF users.flags -->
|
||||||
</div>
|
</div>
|
||||||
<!-- END users -->
|
<!-- END users -->
|
||||||
|
|||||||
Reference in New Issue
Block a user