diff --git a/public/language/en-GB/email.json b/public/language/en-GB/email.json index e893709772..a55f4f1f52 100644 --- a/public/language/en-GB/email.json +++ b/public/language/en-GB/email.json @@ -44,5 +44,10 @@ "unsub.cta": "Click here to alter those settings", + "banned.subject": "You have been banned from %1", + "banned.text1": "The user %1 has been banned from %2.", + "banned.text2": "This ban will last until %1.", + "banned.text3": "This is the reason why you have been banned:", + "closing": "Thanks!" } \ No newline at end of file diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 7473b8bc62..5c30bae07c 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -37,6 +37,7 @@ "user-banned": "User banned", "user-banned-reason": "Sorry, this account has been banned (Reason: %1)", + "user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "user-too-new": "Sorry, you are required to wait %1 second(s) before making your first post", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", "ban-expiry-missing": "Please provide an end date for this ban", diff --git a/public/src/app.js b/public/src/app.js index 61e5deac50..75a68c9da5 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -10,7 +10,6 @@ app.cacheBuster = null; (function () { var showWelcomeMessage = !!utils.params().loggedin; - var showBannedMessage = !!utils.params().banned && app.user && app.user.uid === 0; templates.setGlobal('config', config); @@ -266,11 +265,6 @@ app.cacheBuster = null; title: '[[global:welcome_back]] ' + app.user.username + '!', message: '[[global:you_have_successfully_logged_in]]', }, - banned: { - format: 'modal', - title: '[[error:user-banned]]', - message: '[[error:user-banned-reason, ' + utils.params().banned + ']]', - }, }; function showAlert(type) { @@ -303,13 +297,6 @@ app.cacheBuster = null; showAlert('login'); }); } - - if (showBannedMessage) { - showBannedMessage = false; - $(document).ready(function () { - showAlert('banned'); - }); - } }; app.openChat = function (roomId, uid) { diff --git a/public/src/overrides.js b/public/src/overrides.js index 8f2878c79c..b74d93efba 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -117,6 +117,8 @@ if (typeof window !== 'undefined') { $.timeago.settings.cutoff = 1000 * 60 * 60 * 24 * (parseInt(config.timeagoCutoff, 10) || 30); } + $.timeago.settings.allowFuture = true; + $.fn.timeago = function () { var els = $(this); diff --git a/public/src/sockets.js b/public/src/sockets.js index 6b22ce9ac9..6b11f49e4f 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -121,7 +121,16 @@ app.isConnected = false; app.isConnected = false; } - function onEventBanned() { - window.location.href = config.relative_path + '/'; + function onEventBanned(data) { + var message = data.until ? '[[error:user-banned-reason-until, ' + $.timeago(data.until) + ', ' + data.reason + ']]' : '[[error:user-banned-reason, ' + data.reason + ']]'; + + bootbox.alert({ + title: '[[error:user-banned]]', + message: message, + closeButton: false, + callback: function () { + window.location.href = config.relative_path + '/'; + }, + }); } }()); diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 54d8d99660..77f0fb46b5 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -13,6 +13,7 @@ var user = require('../user'); var plugins = require('../plugins'); var utils = require('../utils'); var Password = require('../password'); +var translator = require('../translator'); var sockets = require('../socket.io'); @@ -379,24 +380,13 @@ authenticationController.localLogin = function (req, username, password, next) { if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { return next(new Error('[[error:local-login-disabled]]')); } + if (!userData || !userData.password) { return next(new Error('[[error:invalid-user-data]]')); } + if (result.banned) { - // Retrieve ban reason and show error - return user.getLatestBanInfo(uid, function (err, banInfo) { - if (err) { - if (err.message === 'no-ban-info') { - next(new Error('[[error:user-banned]]')); - } else { - next(err); - } - } else if (banInfo.reason) { - next(new Error('[[error:user-banned-reason, ' + banInfo.reason + ']]')); - } else { - next(new Error('[[error:user-banned]]')); - } - }); + return banUser(uid, next); } Password.compare(password, userData.password, next); @@ -437,5 +427,25 @@ authenticationController.logout = function (req, res, next) { } }; +function banUser(uid, next) { + user.getLatestBanInfo(uid, function (err, banInfo) { + if (err) { + if (err.message === 'no-ban-info') { + return next(new Error('[[error:user-banned]]')); + } + + return next(err); + } + + if (!banInfo.reason) { + translator.translate('[[user:info.banned-no-reason]]', function (translated) { + banInfo.reason = translated; + next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]')); + }); + } else { + next(new Error(banInfo.expiry ? '[[error:user-banned-reason-until, ' + banInfo.expiry_readable + ', ' + banInfo.reason + ']]' : '[[error:user-banned-reason, ' + banInfo.reason + ']]')); + } + }); +} module.exports = authenticationController; diff --git a/src/middleware/header.js b/src/middleware/header.js index af1a2e0285..f620195a25 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -105,7 +105,7 @@ module.exports = function (middleware) { function (results, next) { if (results.banned) { req.logout(); - return res.redirect('/?banned=' + (results.banReason || 'no-reason')); + return res.redirect('/'); } results.user.isAdmin = results.isAdmin; diff --git a/src/socket.io/user/ban.js b/src/socket.io/user/ban.js index 084307c017..191b98767e 100644 --- a/src/socket.io/user/ban.js +++ b/src/socket.io/user/ban.js @@ -3,10 +3,13 @@ var async = require('async'); var user = require('../../user'); +var meta = require('../../meta'); var websockets = require('../index'); var events = require('../../events'); var privileges = require('../../privileges'); var plugins = require('../../plugins'); +var emailer = require('../../emailer'); +var translator = require('../../translator'); module.exports = function (SocketUser) { SocketUser.banUsers = function (socket, data, callback) { @@ -36,6 +39,9 @@ module.exports = function (SocketUser) { }); next(); }, + function (next) { + user.auth.revokeAllSessions(uid, next); + }, ], next); }, callback); }; @@ -93,10 +99,38 @@ module.exports = function (SocketUser) { if (isAdmin) { return next(new Error('[[error:cant-ban-other-admins]]')); } + + user.getUserField(uid, 'username', next); + }, + function (username, next) { + var siteTitle = meta.config.title || 'NodeBB'; + var data = { + subject: '[[email:banned.subject, ' + siteTitle + ']]', + site_title: siteTitle, + username: username, + until: until ? new Date(until).toString() : false, + reason: reason, + }; + + emailer.send('banned', uid, data, next); + }, + function (next) { user.ban(uid, until, reason, next); }, function (next) { - websockets.in('uid_' + uid).emit('event:banned'); + if (!reason) { + return translator.translate('[[user:info.banned-no-reason]]', function (translated) { + next(false, translated); + }); + } + + next(false, reason); + }, + function (_reason, next) { + websockets.in('uid_' + uid).emit('event:banned', { + until: until, + reason: _reason, + }); next(); }, ], callback); diff --git a/src/user/bans.js b/src/user/bans.js index dc70d012dd..1795fc64e9 100644 --- a/src/user/bans.js +++ b/src/user/bans.js @@ -67,7 +67,7 @@ module.exports = function (User) { } // If they are banned, see if the ban has expired - var stillBanned = !userData['banned:expire'] || Date.now() < userData['banned:expire']; + var stillBanned = !parseInt(userData['banned:expire'], 10) || Date.now() < parseInt(userData['banned:expire'], 10); if (stillBanned) { return next(null, true); diff --git a/src/user/info.js b/src/user/info.js index d3ad7d765d..755c7cff8c 100644 --- a/src/user/info.js +++ b/src/user/info.js @@ -41,7 +41,7 @@ module.exports = function (User) { uid: uid, timestamp: timestamp, expiry: parseInt(expiry, 10), - expiry_readable: new Date(parseInt(expiry, 10)).toString().replace(/:/g, '%3A'), + expiry_readable: new Date(parseInt(expiry, 10)).toString(), reason: validator.escape(String(reason)), }); }); diff --git a/src/views/emails/banned.tpl b/src/views/emails/banned.tpl new file mode 100644 index 0000000000..2384fe1549 --- /dev/null +++ b/src/views/emails/banned.tpl @@ -0,0 +1,18 @@ +

+ [[email:banned.text1, {username}, {site_title}]] + + [[email:banned.text2, {until}]] + +

+ + +

+ [[email:banned.text3]] +

+ +

+ {reason} +

+ + + \ No newline at end of file