diff --git a/install/package.json b/install/package.json
index 2b6221eafd..316b6a965f 100644
--- a/install/package.json
+++ b/install/package.json
@@ -90,11 +90,11 @@
"nodebb-plugin-spam-be-gone": "0.6.1",
"nodebb-rewards-essentials": "0.0.13",
"nodebb-theme-lavender": "5.0.9",
- "nodebb-theme-persona": "9.1.27",
+ "nodebb-theme-persona": "9.1.28",
"nodebb-theme-slick": "1.2.23",
"nodebb-theme-vanilla": "10.1.25",
"nodebb-widget-essentials": "4.0.14",
- "nodemailer": "^5.0.0",
+ "nodemailer": "^6.0.0",
"passport": "^0.4.0",
"passport-local": "1.0.0",
"pg": "^7.4.0",
@@ -135,10 +135,10 @@
"@commitlint/cli": "7.5.2",
"@commitlint/config-angular": "7.5.0",
"coveralls": "3.0.3",
- "eslint": "5.15.1",
+ "eslint": "5.15.3",
"eslint-config-airbnb-base": "13.1.0",
"eslint-plugin-import": "2.16.0",
- "grunt": "1.0.3",
+ "grunt": "1.0.4",
"grunt-contrib-watch": "1.1.0",
"husky": "1.3.1",
"jsdom": "14.0.0",
@@ -171,4 +171,4 @@
"url": "https://github.com/barisusakli"
}
]
-}
\ No newline at end of file
+}
diff --git a/public/language/ar/notifications.json b/public/language/ar/notifications.json
index 17faabfd36..982367c5c2 100644
--- a/public/language/ar/notifications.json
+++ b/public/language/ar/notifications.json
@@ -5,20 +5,20 @@
"mark_all_read": "اجعل كل التنبيهات مقروءة",
"back_to_home": "عودة إلى %1",
"outgoing_link": "رابط خارجي",
- "outgoing_link_message": "You are now leaving %1",
+ "outgoing_link_message": "أنت تغادر اﻻن %1",
"continue_to": "استمر إلى %1",
"return_to": "عودة إى %1",
- "new_notification": "You have a new notification",
+ "new_notification": "لديك تنبيه جديد",
"you_have_unread_notifications": "لديك تنبيهات غير مقروءة.",
- "all": "All",
- "topics": "Topics",
- "replies": "Replies",
- "chat": "Chats",
- "follows": "Follows",
- "upvote": "Upvotes",
+ "all": "الكل",
+ "topics": "مواضيع",
+ "replies": "ردود",
+ "chat": "محادثات",
+ "follows": "متابعون",
+ "upvote": "الموافقين",
"new-flags": "New Flags",
"my-flags": "Flags assigned to me",
- "bans": "Bans",
+ "bans": "الحظر",
"new_message_from": "رسالة جديدة من %1",
"upvoted_your_post_in": "%1 أضاف صوتًا إيجابيا إلى مشاركتك في %2.",
"upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.",
@@ -47,10 +47,10 @@
"email-confirm-error-message": "حدث خطأ أثناء التحقق من عنوان بريدك الإلكتروني. ربما رمز التفعيل خاطئ أو انتهت صلاحيته.",
"email-confirm-sent": "تم إرسال بريد التفعيل.",
"none": "None",
- "notification_only": "Notification Only",
- "email_only": "Email Only",
- "notification_and_email": "Notification & Email",
- "notificationType_upvote": "When someone upvotes your post",
+ "notification_only": "التنبيهات فقط",
+ "email_only": "البريد الالكتروني فقط",
+ "notification_and_email": "التنبيهات والبريد اﻻلكتروني",
+ "notificationType_upvote": "عندما يوافقك احدهم على منشورك",
"notificationType_new-topic": "When someone you follow posts a topic",
"notificationType_new-reply": "When a new reply is posted in a topic you are watching",
"notificationType_follow": "When someone starts following you",
diff --git a/public/language/en-GB/admin/manage/users.json b/public/language/en-GB/admin/manage/users.json
index 6a17cbf016..05fc3f043f 100644
--- a/public/language/en-GB/admin/manage/users.json
+++ b/public/language/en-GB/admin/manage/users.json
@@ -6,6 +6,7 @@
"validate-email": "Validate Email",
"send-validation-email": "Send Validation Email",
"password-reset-email": "Send Password Reset Email",
+ "force-password-reset": "Force Password Reset & Log User Out",
"ban": "Ban User(s)",
"temp-ban": "Ban User(s) Temporarily",
"unban": "Unban User(s)",
@@ -81,7 +82,9 @@
"alerts.confirm-remove-moderator": "Do you really want to remove this moderator?",
"alerts.remove-moderator-success": "User is no longer moderator.",
"alerts.confirm-validate-email": "Do you want to validate email(s) of these user(s)?",
+ "alerts.confirm-force-password-reset": "Are you sure you want to force the password reset and log out these user(s)?",
"alerts.validate-email-success": "Emails validated",
+ "alerts.validate-force-password-reset-success": "User(s) passwords have been reset and their existing sessions have been revoked.",
"alerts.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
"alerts.confirm-delete": "Warning!
Do you really want to delete user(s)?
This action is not reversable! Only the user account will be deleted, their posts and topics will remain.",
"alerts.delete-success": "User(s) Deleted!",
diff --git a/public/language/ja/admin/manage/privileges.json b/public/language/ja/admin/manage/privileges.json
index 5e6d451b67..0db45acc4b 100644
--- a/public/language/ja/admin/manage/privileges.json
+++ b/public/language/ja/admin/manage/privileges.json
@@ -1,26 +1,26 @@
{
- "global": "Global",
- "global.no-users": "No user-specific global privileges.",
+ "global": "グローバル",
+ "global.no-users": "ユーザー固有のグローバル特権はありません。",
- "chat": "Chat",
- "upload-images": "Upload Images",
- "upload-files": "Upload Files",
- "signature": "Signature",
+ "chat": "チャット",
+ "upload-images": "画像をアップロード",
+ "upload-files": "ファイルをアップロード",
+ "signature": "署名",
"ban": "Ban",
- "search-content": "Search Content",
- "search-users": "Search Users",
- "search-tags": "Search Tags",
- "view-users": "View Users",
- "view-tags": "View Tags",
- "view-groups": "View Groups",
- "allow-local-login": "Local Login",
- "allow-group-creation": "Group Create",
+ "search-content": "コンテンツを検索",
+ "search-users": "ユーザー検索",
+ "search-tags": "タグ検索",
+ "view-users": "ユーザーを表示",
+ "view-tags": "タグを表示",
+ "view-groups": "グループを表示",
+ "allow-local-login": "ローカルログイン",
+ "allow-group-creation": "グループを作成",
- "find-category": "Find Category",
- "access-category": "Access Category",
- "access-topics": "Access Topics",
- "create-topics": "Create Topics",
- "reply-to-topics": "Reply to Topics",
+ "find-category": "カテゴリを検索",
+ "access-category": "カテゴリにアクセス",
+ "access-topics": "トピックスにアクセス",
+ "create-topics": "トピックスを作成",
+ "reply-to-topics": "トピックスに返信",
"tag-topics": "Tag Topics",
"edit-posts": "Edit Posts",
"view-edit-history": "View Edit History",
diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js
index 9efb1807be..73e33b391b 100644
--- a/public/src/admin/manage/users.js
+++ b/public/src/admin/manage/users.js
@@ -175,6 +175,19 @@ define('admin/manage/users', ['translator', 'benchpress'], function (translator,
});
});
+ $('.force-password-reset').on('click', function () {
+ var uids = getSelectedUids();
+ if (!uids.length) {
+ return;
+ }
+
+ bootbox.confirm('[[admin/manage/users:alerts.confirm-force-password-reset]]', function (confirm) {
+ if (confirm) {
+ socket.emit('admin.user.forcePasswordReset', uids, done('[[admin/manage/users:alerts.validate-force-password-reset-success]]'));
+ }
+ });
+ });
+
$('.delete-user').on('click', function () {
var uids = getSelectedUids();
if (!uids.length) {
diff --git a/public/src/admin/modules/colorpicker.js b/public/src/admin/modules/colorpicker.js
index c219df634f..6000c416d6 100644
--- a/public/src/admin/modules/colorpicker.js
+++ b/public/src/admin/modules/colorpicker.js
@@ -20,6 +20,10 @@ define('admin/modules/colorpicker', function () {
$(colpkr).css('z-index', 1051);
},
});
+
+ $(window).one('action:ajaxify.start', function () {
+ $this.ColorPickerHide();
+ });
});
};
diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js
index 336abf34fa..3e3fdc1280 100644
--- a/public/src/admin/settings/email.js
+++ b/public/src/admin/settings/email.js
@@ -87,18 +87,20 @@ define('admin/settings/email', ['ace/ace', 'admin/settings'], function (ace) {
return app.alertError(err.message);
}
- now = new Date(now);
+ var date = new Date(now.timestamp);
+ var offset = (new Date().getTimezoneOffset() - now.offset) / 60;
+ date.setHours(date.getHours() + offset);
- $('#serverTime').text(now.toString());
+ $('#serverTime').text(date.toLocaleTimeString());
- now.setHours(parseInt(hour, 10), 0, 0, 0);
+ date.setHours(parseInt(hour, 10) - offset, 0, 0, 0);
// If adjusted time is in the past, move to next day
- if (now.getTime() < Date.now()) {
- now.setDate(now.getDate() + 1);
+ if (date.getTime() < Date.now()) {
+ date.setDate(date.getDate() + 1);
}
- $('#nextDigestTime').text(now.toString());
+ $('#nextDigestTime').text(date.toLocaleString());
});
}
diff --git a/public/src/app.js b/public/src/app.js
index 7bf56166b6..f2b6c25ba0 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -134,9 +134,9 @@ app.cacheBuster = null;
// Re-render top bar menu
var toRender = {
+ 'slideout-menu': $('.slideout-menu'),
menu: $('#header-menu .container'),
'chats-menu': $('#chats-menu'),
- 'slideout-menu': $('.slideout-menu'),
};
Promise.all(Object.keys(toRender).map(function (tpl) {
return Benchpress.render('partials/' + tpl, data.header).then(function (render) {
@@ -157,6 +157,8 @@ app.cacheBuster = null;
app.handleSearch();
}
+ handleStatusChange();
+
$(window).trigger('action:app.updateHeader');
});
});
diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js
index 56f7f12287..eda407de46 100644
--- a/src/controllers/authentication.js
+++ b/src/controllers/authentication.js
@@ -273,13 +273,20 @@ function continueLogin(req, res, next) {
if (passwordExpiry && passwordExpiry < Date.now()) {
winston.verbose('[auth] Triggering password reset for uid ' + userData.uid + ' due to password policy');
req.session.passwordExpired = true;
- user.reset.generate(userData.uid, function (err, code) {
+
+ async.series({
+ code: async.apply(user.reset.generate, userData.uid),
+ buildHeader: async.apply(middleware.buildHeader, req, res),
+ header: async.apply(middleware.generateHeader, req, res, {}),
+ }, function (err, payload) {
if (err) {
return helpers.noScriptErrors(req, res, err.message, 403);
}
res.status(200).send({
- next: nconf.get('relative_path') + '/reset/' + code,
+ next: nconf.get('relative_path') + '/reset/' + payload.code,
+ header: payload.header,
+ config: res.locals.config,
});
});
} else {
diff --git a/src/controllers/index.js b/src/controllers/index.js
index 494b0e9f02..f4d082067a 100644
--- a/src/controllers/index.js
+++ b/src/controllers/index.js
@@ -62,10 +62,10 @@ Controllers.reset = function (req, res, next) {
};
if (req.params.code) {
- // Save to session and redirect
req.session.reset_code = req.params.code;
- res.redirect(nconf.get('relative_path') + '/reset');
- } else if (req.session.reset_code) {
+ }
+
+ if (req.session.reset_code) {
// Validate and save to local variable before removing from session
user.reset.validate(req.session.reset_code, function (err, valid) {
if (err) {
diff --git a/src/prestart.js b/src/prestart.js
index cb077d1546..49f6336499 100644
--- a/src/prestart.js
+++ b/src/prestart.js
@@ -69,6 +69,7 @@ function loadConfig(configFile) {
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates'));
nconf.set('upload_path', path.resolve(nconf.get('base_dir'), nconf.get('upload_path')));
+ nconf.set('upload_url', '/assets/uploads');
if (nconf.get('url')) {
nconf.set('url_parsed', url.parse(nconf.get('url')));
diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js
index efd193ede0..02ade56a89 100644
--- a/src/socket.io/admin/user.js
+++ b/src/socket.io/admin/user.js
@@ -126,6 +126,25 @@ User.sendPasswordResetEmail = function (socket, uids, callback) {
}, callback);
};
+User.forcePasswordReset = function (socket, uids, callback) {
+ if (!Array.isArray(uids)) {
+ return callback(new Error('[[error:invalid-data]]'));
+ }
+
+ uids = uids.filter(uid => parseInt(uid, 10));
+
+ async.each(uids, function (uid, next) {
+ async.waterfall([
+ function (next) {
+ user.setUserField(uid, 'passwordExpiry', Date.now(), next);
+ },
+ function (next) {
+ user.auth.revokeAllSessions(uid, next);
+ },
+ ], next);
+ }, callback);
+};
+
User.deleteUsers = function (socket, uids, callback) {
deleteUsers(socket, uids, function (uid, next) {
user.deleteAccount(uid, next);
diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js
index 398bf249f5..e9cd3f00e4 100644
--- a/src/socket.io/meta.js
+++ b/src/socket.io/meta.js
@@ -62,7 +62,12 @@ function leaveCurrentRoom(socket) {
SocketMeta.getServerTime = function (socket, data, callback) {
// Returns server time in milliseconds
- callback(null, Date.now());
+ const now = new Date();
+
+ callback(null, {
+ timestamp: now.getTime(),
+ offset: now.getTimezoneOffset(),
+ });
};
module.exports = SocketMeta;
diff --git a/src/start.js b/src/start.js
index 53893d44b8..b2b4ceebc9 100644
--- a/src/start.js
+++ b/src/start.js
@@ -99,7 +99,6 @@ function setupConfigs() {
nconf.set('use_port', !!urlObject.port);
nconf.set('relative_path', relativePath);
nconf.set('port', nconf.get('PORT') || nconf.get('port') || urlObject.port || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
- nconf.set('upload_url', '/assets/uploads');
}
function printStartupInfo() {
diff --git a/src/views/admin/extend/plugins.tpl b/src/views/admin/extend/plugins.tpl
index cbf44febf8..795c404656 100644
--- a/src/views/admin/extend/plugins.tpl
+++ b/src/views/admin/extend/plugins.tpl
@@ -20,7 +20,32 @@