From fe27d40ff6b2bf4d48ea90823c403e8ac3c286d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 23 Mar 2017 10:58:17 +0300 Subject: [PATCH] closes #5542 --- public/language/en-GB/user.json | 3 +- public/src/client/account/info.js | 12 +++++++ src/controllers/accounts/info.js | 31 ++++++++++++++-- src/socket.io/user.js | 14 ++++---- src/upgrade.js | 2 +- .../1.5.0/moderation_history_refactor.js | 30 ++++++++++++++++ src/user/info.js | 35 ++++++++++++++++++- 7 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/upgrades/1.5.0/moderation_history_refactor.js diff --git a/public/language/en-GB/user.json b/public/language/en-GB/user.json index 52d2a81252..7b79af9ce7 100644 --- a/public/language/en-GB/user.json +++ b/public/language/en-GB/user.json @@ -149,5 +149,6 @@ "info.username-history": "Username History", "info.email-history": "Email History", "info.moderation-note": "Moderation Note", - "info.moderation-note.success": "Moderation note saved" + "info.moderation-note.success": "Moderation note saved", + "info.moderation-note.add": "Add note" } diff --git a/public/src/client/account/info.js b/public/src/client/account/info.js index f366c0a3e7..ee6648aab5 100644 --- a/public/src/client/account/info.js +++ b/public/src/client/account/info.js @@ -17,7 +17,19 @@ define('forum/account/info', ['forum/account/header', 'components'], function (h if (err) { return app.alertError(err.message); } + $('[component="account/moderation-note"]').val(''); app.alertSuccess('[[user:info.moderation-note.success]]'); + var timestamp = Date.now(); + var data = [{ + note: note, + user: app.user, + timestamp: timestamp, + timestampISO: utils.toISOString(timestamp), + }]; + app.parseAndTranslate('account/info', 'moderationNotes', { moderationNotes: data }, function (html) { + $('[component="account/moderation-note/list"]').prepend(html); + html.find('.timeago').timeago(); + }); }); }); } diff --git a/src/controllers/accounts/info.js b/src/controllers/accounts/info.js index e852ebc3df..7f9edb2462 100644 --- a/src/controllers/accounts/info.js +++ b/src/controllers/accounts/info.js @@ -2,14 +2,19 @@ var async = require('async'); +var db = require('../../database'); var user = require('../../user'); var helpers = require('../helpers'); var accountHelpers = require('./helpers'); +var pagination = require('../../pagination'); -var infoController = {}; +var infoController = module.exports; infoController.get = function (req, res, callback) { var userData; + var page = Math.max(1, req.query.page || 1); + var itemsPerPage = 10; + async.waterfall([ function (next) { accountHelpers.getUserDataByUserSlug(req.params.userslug, req.uid, next); @@ -19,11 +24,27 @@ infoController.get = function (req, res, callback) { if (!userData) { return callback(); } + + var start = (page - 1) * itemsPerPage; + var stop = start + itemsPerPage - 1; async.parallel({ history: async.apply(user.getModerationHistory, userData.uid), sessions: async.apply(user.auth.getSessions, userData.uid, req.sessionID), usernames: async.apply(user.getHistory, 'user:' + userData.uid + ':usernames'), emails: async.apply(user.getHistory, 'user:' + userData.uid + ':emails'), + notes: function (next) { + if (!userData.isAdminOrGlobalModeratorOrModerator) { + return setImmediate(next); + } + async.parallel({ + notes: function (next) { + user.getModerationNotes(userData.uid, start, stop, next); + }, + count: function (next) { + db.sortedSetCard('uid:' + userData.uid + ':moderation:notes', next); + }, + }, next); + }, }, next); }, ], function (err, data) { @@ -35,11 +56,15 @@ infoController.get = function (req, res, callback) { userData.sessions = data.sessions; userData.usernames = data.usernames; userData.emails = data.emails; + + if (userData.isAdminOrGlobalModeratorOrModerator) { + userData.moderationNotes = data.notes.notes; + var pageCount = Math.ceil(data.notes.count / itemsPerPage); + userData.pagination = pagination.create(page, pageCount, req.query); + } userData.title = '[[pages:account/info]]'; userData.breadcrumbs = helpers.buildBreadcrumbs([{ text: userData.username, url: '/user/' + userData.userslug }, { text: '[[user:account_info]]' }]); res.render('account/info', userData); }); }; - -module.exports = infoController; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index c161d13f6f..e3f9e7d95d 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -316,7 +316,7 @@ SocketUser.getUserByEmail = function (socket, email, callback) { }; SocketUser.setModerationNote = function (socket, data, callback) { - if (!socket.uid || !data || !data.uid) { + if (!socket.uid || !data || !data.uid || !data.note) { return callback(new Error('[[error:invalid-data]]')); } @@ -335,11 +335,13 @@ SocketUser.setModerationNote = function (socket, data, callback) { if (!allowed) { return next(new Error('[[error:no-privileges]]')); } - if (data.note) { - user.setUserField(data.uid, 'moderationNote', data.note, next); - } else { - db.deleteObjectField('user:' + data.uid, 'moderationNote', next); - } + + var note = { + uid: socket.uid, + note: data.note, + timestamp: Date.now(), + }; + db.sortedSetAdd('uid:' + data.uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); }, ], callback); }; diff --git a/src/upgrade.js b/src/upgrade.js index 2cf75db145..60b074a953 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -51,7 +51,7 @@ var Upgrade = { }, { version: 'develop', // rename this to whatever the next NodeBB version is (breaking) - upgrades: ['flags_refactor', 'post_votes_zset'], + upgrades: ['flags_refactor', 'post_votes_zset', 'moderation_history_refactor'], }, ], }; diff --git a/src/upgrades/1.5.0/moderation_history_refactor.js b/src/upgrades/1.5.0/moderation_history_refactor.js new file mode 100644 index 0000000000..73b4e8f6d7 --- /dev/null +++ b/src/upgrades/1.5.0/moderation_history_refactor.js @@ -0,0 +1,30 @@ +/* jslint node: true */ + +'use strict'; + +var db = require('../../database'); +var batch = require('../../batch'); + +var async = require('async'); + +module.exports = { + name: 'Update moderation notes to zset', + timestamp: Date.UTC(2017, 2, 22), + method: function (callback) { + batch.processSortedSet('users:joindate', function (ids, next) { + async.each(ids, function (uid, next) { + db.getObjectField('user:' + uid, 'moderationNote', function (err, moderationNote) { + if (err || !moderationNote) { + return next(err); + } + var note = { + uid: 1, + note: moderationNote, + timestamp: Date.now(), + }; + db.sortedSetAdd('uid:' + uid + ':moderation:notes', note.timestamp, JSON.stringify(note), next); + }); + }, next); + }, callback); + }, +}; diff --git a/src/user/info.js b/src/user/info.js index 4af1b77f35..c3071b84cf 100644 --- a/src/user/info.js +++ b/src/user/info.js @@ -7,6 +7,7 @@ var validator = require('validator'); var db = require('../database'); var posts = require('../posts'); var topics = require('../topics'); +var utils = require('../../public/src/utils'); module.exports = function (User) { User.getLatestBanInfo = function (uid, callback) { @@ -74,7 +75,7 @@ module.exports = function (User) { } callback(null, data.map(function (set) { set.timestamp = set.score; - set.timestampISO = new Date(set.score).toISOString(); + set.timestampISO = utils.toISOString(set.score); set.value = validator.escape(String(set.value.split(':')[0])); delete set.score; return set; @@ -138,4 +139,36 @@ module.exports = function (User) { return banObj; }); } + + User.getModerationNotes = function (uid, start, stop, callback) { + var noteData; + async.waterfall([ + function (next) { + db.getSortedSetRevRange('uid:' + uid + ':moderation:notes', start, stop, next); + }, + function (notes, next) { + var uids = []; + noteData = notes.map(function (note) { + try { + var data = JSON.parse(note); + uids.push(data.uid); + data.timestampISO = utils.toISOString(data.timestamp); + return data; + } catch (err) { + return next(err); + } + }); + + User.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture'], next); + }, + function (userData, next) { + noteData.forEach(function (note, index) { + if (note) { + note.user = userData[index]; + } + }); + next(null, noteData); + }, + ], callback); + }; };