diff --git a/app.js b/app.js index d3b4af0697..175aaaaff6 100644 --- a/app.js +++ b/app.js @@ -309,4 +309,4 @@ function versionCheck() { winston.warn('Your version of Node.js is too outdated for NodeBB. Please update your version of Node.js.'); winston.warn('Recommended ' + range.green + ', '.reset + version.yellow + ' provided\n'.reset); } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 19b5daa20b..c12be8d8db 100644 --- a/package.json +++ b/package.json @@ -59,15 +59,15 @@ "nodebb-plugin-dbsearch": "2.0.6", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.2.1", - "nodebb-plugin-markdown": "8.0.3", - "nodebb-plugin-mentions": "2.1.5", + "nodebb-plugin-markdown": "8.1.0", + "nodebb-plugin-mentions": "2.1.6", "nodebb-plugin-soundpack-default": "1.0.0", "nodebb-plugin-spam-be-gone": "0.5.1", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "4.0.5", - "nodebb-theme-persona": "5.0.21", + "nodebb-theme-persona": "5.0.22", "nodebb-theme-slick": "1.1.0", - "nodebb-theme-vanilla": "6.0.16", + "nodebb-theme-vanilla": "6.0.17", "nodebb-widget-essentials": "3.0.1", "nodemailer": "2.6.4", "nodemailer-sendmail-transport": "1.0.0", diff --git a/public/language/en-GB/admin/general/navigation.json b/public/language/en-GB/admin/general/navigation.json index c4ba0d09ac..9abf7f58cc 100644 --- a/public/language/en-GB/admin/general/navigation.json +++ b/public/language/en-GB/admin/general/navigation.json @@ -11,6 +11,7 @@ "only-admins": "Only display to Admins", "only-global-mods-and-admins": "Only display to Global Moderators and Admins", "only-logged-in": "Only display to logged in users", + "only-guest": "Only display to guests", "open-new-window": "Open in a new window", "installed-plugins-required": "Installed Plugins Required:", diff --git a/public/language/en-GB/admin/manage/post-queue.json b/public/language/en-GB/admin/manage/post-queue.json index 5c52e25538..f748fb0ee2 100644 --- a/public/language/en-GB/admin/manage/post-queue.json +++ b/public/language/en-GB/admin/manage/post-queue.json @@ -2,7 +2,9 @@ "post-queue": "Post Queue", "description": "There are no posts in the post queue.
To enable this feature, go to Settings → Post → Posting Restrictions and enable Post Queue.", "user": "User", + "category": "Category", "title": "Title", "content": "Content", - "posted": "Posted" + "posted": "Posted", + "reply-to": "Reply to \"%1\"" } \ No newline at end of file diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index 8c7ed9d07a..7812eabf7c 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -45,6 +45,7 @@ "new_register": "%1 sent a registration request.", "new_register_multiple": "There are %1 registration requests awaiting review.", "flag_assigned_to_you": "Flag %1 has been assigned to you", + "post_awaiting_review": "Post awaiting review", "email-confirmed": "Email Confirmed", "email-confirmed-message": "Thank you for validating your email. Your account is now fully activated.", diff --git a/public/language/en-GB/pages.json b/public/language/en-GB/pages.json index d34a370480..9ffc04c321 100644 --- a/public/language/en-GB/pages.json +++ b/public/language/en-GB/pages.json @@ -9,6 +9,7 @@ "moderator-tools": "Moderator Tools", "flagged-content": "Flagged Content", "ip-blacklist": "IP Blacklist", + "post-queue": "Post Queue", "users/online": "Online Users", "users/latest": "Latest Users", diff --git a/public/language/fa-IR/admin/advanced/database.json b/public/language/fa-IR/admin/advanced/database.json index b88ca6fc82..a2879dfe45 100644 --- a/public/language/fa-IR/admin/advanced/database.json +++ b/public/language/fa-IR/admin/advanced/database.json @@ -16,7 +16,7 @@ "mongo.index-size": "Index Size", "mongo.file-size": "File Size", "mongo.resident-memory": "Resident Memory", - "mongo.virtual-memory": "Virtual Memory", + "mongo.virtual-memory": "حافظۀ مجازی", "mongo.mapped-memory": "Mapped Memory", "mongo.raw-info": "MongoDB Raw Info", diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 548e0ba47a..e4759935b6 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -41,6 +41,7 @@ var loggedIn = data.config ? data.config.loggedIn : false; if (properties) { if ((properties.loggedIn && !loggedIn) || + (properties.guestOnly && loggedIn) || (properties.globalMod && !data.isGlobalMod && !data.isAdmin) || (properties.adminOnly && !data.isAdmin) || (properties.searchInstalled && !data.searchEnabled)) { diff --git a/src/controllers/admin/postqueue.js b/src/controllers/admin/postqueue.js index e401f7dedd..749b4c0a2a 100644 --- a/src/controllers/admin/postqueue.js +++ b/src/controllers/admin/postqueue.js @@ -4,6 +4,8 @@ var async = require('async'); var db = require('../../database'); var user = require('../../user'); +var topics = require('../../topics'); +var categories = require('../../categories'); var pagination = require('../../pagination'); var utils = require('../../utils'); @@ -51,11 +53,34 @@ postQueueController.get = function (req, res, next) { }); user.getUsersFields(uids, ['username', 'userslug', 'picture'], next); }, - function (userData) { + function (userData, next) { postData.forEach(function (postData, index) { postData.user = userData[index]; }); + async.map(postData, function (postData, next) { + async.waterfall([ + function (next) { + if (postData.data.cid) { + next(null, { cid: postData.data.cid }); + } else if (postData.data.tid) { + topics.getTopicFields(postData.data.tid, ['title', 'cid'], next); + } else { + next(null, { cid: 0 }); + } + }, + function (topicData, next) { + postData.topic = topicData; + categories.getCategoryData(topicData.cid, next); + }, + function (categoryData, next) { + postData.category = categoryData; + next(null, postData); + }, + ], next); + }, next); + }, + function (postData) { res.render('admin/manage/post-queue', { title: '[[pages:post-queue]]', posts: postData, diff --git a/src/messaging.js b/src/messaging.js index 99467959b3..9cb54d0fb3 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -3,6 +3,7 @@ var async = require('async'); var S = require('string'); +var validator = require('validator'); var db = require('./database'); var user = require('./user'); @@ -211,6 +212,7 @@ Messaging.getTeaser = function (uid, roomId, callback) { } if (teaser.content) { teaser.content = S(teaser.content).stripTags().decodeHTMLEntities().s; + teaser.content = validator.escape(String(teaser.content)); } teaser.timestampISO = utils.toISOString(teaser.timestamp); diff --git a/src/middleware/index.js b/src/middleware/index.js index 720fa50771..9072b0441d 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -181,3 +181,8 @@ middleware.processTimeagoLocales = function (req, res, next) { }, ], next); }; + +middleware.delayLoading = function (req, res, next) { + // Introduces an artificial delay during load so that brute force attacks are effectively mitigated + setTimeout(next, 1000); +}; diff --git a/src/posts/queue.js b/src/posts/queue.js index 3b9f555f9d..7e0e90720f 100644 --- a/src/posts/queue.js +++ b/src/posts/queue.js @@ -6,6 +6,7 @@ var db = require('../database'); var user = require('../user'); var meta = require('../meta'); var topics = require('../topics'); +var notifications = require('../notifications'); var privileges = require('../privileges'); var socketHelpers = require('../socket.io/helpers'); @@ -43,6 +44,22 @@ module.exports = function (Posts) { function (next) { user.setUserField(data.uid, 'lastposttime', Date.now(), next); }, + function (next) { + notifications.create({ + nid: 'post-queued-' + id, + mergeId: 'post-queue', + bodyShort: '[[notifications:post_awaiting_review]]', + bodyLong: data.content, + path: '/post-queue', + }, next); + }, + function (notification, next) { + if (notification) { + notifications.pushGroups(notification, ['administrators', 'Global Moderators'], next); + } else { + next(); + } + }, function (next) { next(null, { queued: true, @@ -92,6 +109,9 @@ module.exports = function (Posts) { function (next) { db.delete('post:queue:' + id, next); }, + function (next) { + notifications.rescind('post-queued-' + id, next); + }, ], callback); }; diff --git a/src/routes/index.js b/src/routes/index.js index 52375f9f67..a8c3eeefa7 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -32,7 +32,7 @@ function mainRoutes(app, middleware, controllers) { setupPageRoute(app, '/confirm/:code', middleware, [], controllers.confirmEmail); setupPageRoute(app, '/outgoing', middleware, [], controllers.outgoing); setupPageRoute(app, '/search', middleware, [], controllers.search.search); - setupPageRoute(app, '/reset/:code?', middleware, [], controllers.reset); + setupPageRoute(app, '/reset/:code?', middleware, [middleware.delayLoading], controllers.reset); setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse); app.post('/compose', middleware.applyCSRF, controllers.composePost); diff --git a/src/user/profile.js b/src/user/profile.js index d3067b6945..54ed58267b 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -185,6 +185,7 @@ module.exports = function (User) { function (next) { db.sortedSetAdd('users:notvalidated', Date.now(), uid, next); }, + async.apply(User.reset.cleanByUid, uid), ], function (err) { next(err); }); diff --git a/src/user/reset.js b/src/user/reset.js index 8915a6538d..e2982ca4d1 100644 --- a/src/user/reset.js +++ b/src/user/reset.js @@ -7,6 +7,7 @@ var winston = require('winston'); var user = require('../user'); var utils = require('../utils'); var translator = require('../translator'); +var batch = require('../batch'); var db = require('../database'); var meta = require('../meta'); @@ -167,3 +168,42 @@ UserReset.clean = function (callback) { }, ], callback); }; + +UserReset.cleanByUid = function (uid, callback) { + if (typeof callback !== 'function') { + callback = function () {}; + } + + var toClean = []; + uid = parseInt(uid, 10); + + async.waterfall([ + async.apply(db.getSortedSetRange.bind(db), 'reset:issueDate', 0, -1), + function (tokens, next) { + batch.processArray(tokens, function (tokens, next) { + db.getObjectFields('reset:uid', tokens, function (err, results) { + for (var code in results) { + if (results.hasOwnProperty(code) && parseInt(results[code], 10) === uid) { + toClean.push(code); + } + } + + next(err); + }); + }, next); + }, + function (next) { + if (!toClean.length) { + winston.verbose('[UserReset.cleanByUid] No tokens found for uid (' + uid + ').'); + return setImmediate(next); + } + + winston.verbose('[UserReset.cleanByUid] Found ' + toClean.length + ' token(s), removing...'); + async.parallel([ + async.apply(db.deleteObjectFields, 'reset:uid', toClean), + async.apply(db.sortedSetRemove, 'reset:issueDate', toClean), + async.apply(db.sortedSetRemove, 'reset:issueDate:uid', uid), + ], next); + }, + ], callback); +}; diff --git a/src/views/admin/general/navigation.tpl b/src/views/admin/general/navigation.tpl index 5b5f95cfb1..7be444f3d1 100644 --- a/src/views/admin/general/navigation.tpl +++ b/src/views/admin/general/navigation.tpl @@ -78,6 +78,12 @@ [[admin/general/navigation:only-logged-in]] +
+ +