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]]
+