diff --git a/install/package.json b/install/package.json index deacbc651a..280cb852a1 100644 --- a/install/package.json +++ b/install/package.json @@ -74,9 +74,9 @@ "nodebb-plugin-spam-be-gone": "0.5.3", "nodebb-rewards-essentials": "0.0.11", "nodebb-theme-lavender": "5.0.4", - "nodebb-theme-persona": "8.0.10", + "nodebb-theme-persona": "8.0.11", "nodebb-theme-slick": "1.2.1", - "nodebb-theme-vanilla": "9.0.7", + "nodebb-theme-vanilla": "9.0.8", "nodebb-widget-essentials": "4.0.2", "nodemailer": "4.4.1", "passport": "^0.4.0", diff --git a/public/language/de/admin/admin.json b/public/language/de/admin/admin.json index 5669a4fae2..6e51d91fc0 100644 --- a/public/language/de/admin/admin.json +++ b/public/language/de/admin/admin.json @@ -1,5 +1,5 @@ { - "alert.confirm-rebuild-and-restart": "Are you sure you wish to rebuild and restart NodeBB?", + "alert.confirm-rebuild-and-restart": "Bist du sicher, dass du NodeBB neu bauen und neu starten möchtest?", "alert.confirm-restart": "Bist du sicher, dass du NodeBB neu starten möchtest?", "acp-title": "%1 | NodeBB Admin Systemsteuerung", diff --git a/public/language/de/admin/appearance/themes.json b/public/language/de/admin/appearance/themes.json index dc43a00325..09019b588b 100644 --- a/public/language/de/admin/appearance/themes.json +++ b/public/language/de/admin/appearance/themes.json @@ -7,5 +7,5 @@ "revert-confirm": "Bist du dir sicher, dass du das standard NodeBB Theme wieder herstellen willst?", "theme-changed": "Theme geändert", "revert-success": "Du hast dein NodeBB erfolgreich wieder auf das Standard-Theme zurückgesetzt.", - "restart-to-activate": "Please rebuild and restart your NodeBB to fully activate this theme." + "restart-to-activate": "Bitte baue und starte NodeBB neu um das Theme zu aktivieren." } \ No newline at end of file diff --git a/public/language/de/search.json b/public/language/de/search.json index bc779b9579..17afbfc5f4 100644 --- a/public/language/de/search.json +++ b/public/language/de/search.json @@ -6,7 +6,7 @@ "titles": "Titel", "titles-posts": "Titel und Beiträge", "match-words": "Match words", - "all": "All", + "all": "Alle", "any": "Any", "posted-by": "Geschrieben von", "in-categories": "In Kategorien", diff --git a/public/language/en-GB/admin/extend/plugins.json b/public/language/en-GB/admin/extend/plugins.json index 05f7df4ecb..005d9044ae 100644 --- a/public/language/en-GB/admin/extend/plugins.json +++ b/public/language/en-GB/admin/extend/plugins.json @@ -44,6 +44,8 @@ "alert.package-manager-unreachable": "
NodeBB could not reach the package manager, an upgrade is not suggested at this time.
", "alert.incompatible": "Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.
", "alert.possibly-incompatible": "No Compatibility Information Found
This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.
In the event that NodeBB cannot boot properly:
$ ./nodebb reset plugin=\"%1\"
Continue installation of latest version of this plugin?
", + "alert.reorder": "Plugins Re-ordered", + "alert.reorder-success": "Please rebuild and restart your NodeBB to fully complete the process.", "license.title": "Plugin License Information", "license.intro": "The plugin %1 is licensed under the %2. Please read and understand the license terms prior to activating this plugin.", diff --git a/public/language/en-GB/error.json b/public/language/en-GB/error.json index 260c397de6..1a3803cc14 100644 --- a/public/language/en-GB/error.json +++ b/public/language/en-GB/error.json @@ -21,6 +21,7 @@ "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", "invalid-url": "Invalid URL", + "local-login-disabled": "Local login system has been disabled for non-privileged accounts.", "csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again", "invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2", diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index 371300e38b..be64432767 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -150,5 +150,7 @@ "diffs.title": "Post Edit History", "diffs.description": "This post has %1 revisions. Click one of the revisions below to see the post content at that point in time.", - "diffs.no-revisions-description": "This post has %1 revisions." + "diffs.no-revisions-description": "This post has %1 revisions.", + "diffs.current-revision": "current revision", + "diffs.original-revision": "original revision" } diff --git a/public/language/es/admin/manage/post-queue.json b/public/language/es/admin/manage/post-queue.json index 4de24c991b..7503ce5906 100644 --- a/public/language/es/admin/manage/post-queue.json +++ b/public/language/es/admin/manage/post-queue.json @@ -2,10 +2,10 @@ "post-queue": "Post Queue", "description": "There are no posts in the post queue.Hasło zostało zmienione pomyślnie. Zaloguj się ponownie.", + "password_changed.message": "
Hasło zostało zmienione. Zaloguj się ponownie.", "wrong_reset_code.title": "Nieprawidłowy kod resetujący", "wrong_reset_code.message": "Wprowadzony kod resetujący jest nieprawidłowy. Spróbuj ponownie lub uzyskaj nowy kod.", "new_password": "Nowe hasło", diff --git a/public/language/ru/modules.json b/public/language/ru/modules.json index 0bd452bdc3..9cb49d7f94 100644 --- a/public/language/ru/modules.json +++ b/public/language/ru/modules.json @@ -28,8 +28,8 @@ "chat.rename-room": "Rename room", "chat.rename-placeholder": "Enter your room name here", "chat.rename-help": "The room name set here will be viewable by all participants in the room.", - "chat.leave": "Leave Chat", - "chat.leave-prompt": "Are you sure you wish to leave this chat?", + "chat.leave": "Покинуть Чат", + "chat.leave-prompt": "Вы действительно хотите покинуть чат?", "chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.", "chat.in-room": "In this room", "composer.compose": "Редактор сообщений", diff --git a/public/language/ru/search.json b/public/language/ru/search.json index f685b6f9c0..3c2d5ec9b8 100644 --- a/public/language/ru/search.json +++ b/public/language/ru/search.json @@ -6,8 +6,8 @@ "titles": "Названия", "titles-posts": "Названия и записи", "match-words": "Match words", - "all": "All", - "any": "Any", + "all": "Все", + "any": "Любые", "posted-by": "В именах авторов записей", "in-categories": "В сообществах", "search-child-categories": "Искать в рубриках", diff --git a/public/language/zh-CN/admin/settings/post.json b/public/language/zh-CN/admin/settings/post.json index c8bdf3692c..b471b57013 100644 --- a/public/language/zh-CN/admin/settings/post.json +++ b/public/language/zh-CN/admin/settings/post.json @@ -3,7 +3,7 @@ "sorting.post-default": "默认帖子排序", "sorting.oldest-to-newest": "从旧到新", "sorting.newest-to-oldest": "从新到旧", - "sorting.most-votes": "最多投票", + "sorting.most-votes": "最多赞同", "sorting.most-posts": "最多回复", "sorting.topic-default": "默认主题排序", "length": "帖子长度", diff --git a/public/language/zh-CN/global.json b/public/language/zh-CN/global.json index bdb63f6748..a0fddbfdf9 100644 --- a/public/language/zh-CN/global.json +++ b/public/language/zh-CN/global.json @@ -53,7 +53,7 @@ "topics": "主题", "posts": "帖子", "best": "最佳", - "votes": "投票", + "votes": "赞同", "upvoters": "顶的人", "upvoted": "顶", "downvoters": "踩的人", diff --git a/public/language/zh-CN/topic.json b/public/language/zh-CN/topic.json index 19971fde40..0fefa3db42 100644 --- a/public/language/zh-CN/topic.json +++ b/public/language/zh-CN/topic.json @@ -118,7 +118,7 @@ "sort_by": "排序", "oldest_to_newest": "从旧到新", "newest_to_oldest": "从新到旧", - "most_votes": "最多投票", + "most_votes": "最多赞同", "most_posts": "最多回复", "stale.title": "接受建议并创建新主题?", "stale.warning": "您回复的主题已经很古老了,是否发布新主题并引用此主题的内容?", diff --git a/public/src/admin/extend/plugins.js b/public/src/admin/extend/plugins.js index aa0bac657b..a7dd240e04 100644 --- a/public/src/admin/extend/plugins.js +++ b/public/src/admin/extend/plugins.js @@ -182,6 +182,19 @@ define('admin/extend/plugins', ['jqueryui', 'translator', 'benchpress'], functio return app.alertError(err.message); } $('#order-active-plugins-modal').modal('hide'); + + app.alert({ + alert_id: 'plugin_reordered', + title: '[[admin/extend/plugins:alert.reorder]]', + message: '[[admin/extend/plugins:alert.reorder-success]]', + type: 'success', + timeout: 5000, + clickfn: function () { + require(['admin/modules/instance'], function (instance) { + instance.rebuildAndRestart(); + }); + }, + }); }); }); diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 39cbb48c48..589c3a71f8 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -109,9 +109,8 @@ $(document).ready(function () { url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')).toLowerCase(); var isClientToAdmin = url.startsWith('admin') && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') !== 0; var isAdminToClient = !url.startsWith('admin') && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') === 0; - var uploadsOrApi = url.startsWith('assets/') || url.startsWith('uploads') || url.startsWith('api'); - if (isClientToAdmin || isAdminToClient || uploadsOrApi) { + if (isClientToAdmin || isAdminToClient) { window.open(RELATIVE_PATH + '/' + url, '_top'); return true; } @@ -390,6 +389,13 @@ $(document).ready(function () { return; } + // Default behaviour for uploads and direct links to API urls + if (internalLink && ['/uploads', '/assets/uploads/', '/api/'].some(function (prefix) { + return String(_self.pathname).startsWith(config.relative_path + prefix); + })) { + return; + } + if (hrefEmpty(this.href) || this.protocol === 'javascript:' || $(this).attr('href') === '#') { return e.preventDefault(); } diff --git a/public/src/client/topic/diffs.js b/public/src/client/topic/diffs.js index be7b5b0731..6d25d1d4e3 100644 --- a/public/src/client/topic/diffs.js +++ b/public/src/client/topic/diffs.js @@ -15,6 +15,8 @@ define('forum/topic/diffs', ['forum/topic/images', 'benchpress', 'translator'], return app.alertError(err.message); } + timestamps.unshift(Date.now()); + Benchpress.parse('partials/modals/post_history', { diffs: timestamps.map(function (timestamp) { timestamp = parseInt(timestamp, 10); diff --git a/src/controllers/accounts/settings.js b/src/controllers/accounts/settings.js index 5166222706..5f5bb57aa7 100644 --- a/src/controllers/accounts/settings.js +++ b/src/controllers/accounts/settings.js @@ -9,6 +9,7 @@ var meta = require('../../meta'); var plugins = require('../../plugins'); var privileges = require('../../privileges'); var categories = require('../../categories'); +var notifications = require('../../notifications'); var db = require('../../database'); var helpers = require('../helpers'); var accountHelpers = require('./helpers'); @@ -180,15 +181,6 @@ settingsController.get = function (req, res, callback) { }; function getNotificationSettings(userData, callback) { - var types = [ - 'notificationType_upvote', - 'notificationType_new-topic', - 'notificationType_new-reply', - 'notificationType_follow', - 'notificationType_new-chat', - 'notificationType_group-invite', - ]; - var privilegedTypes = []; async.waterfall([ @@ -206,8 +198,7 @@ function getNotificationSettings(userData, callback) { privilegedTypes.push('notificationType_new-user-flag'); } plugins.fireHook('filter:user.notificationTypes', { - userData: userData, - types: types, + types: notifications.baseTypes.slice(), privilegedTypes: privilegedTypes, }, next); }, diff --git a/src/controllers/admin/settings.js b/src/controllers/admin/settings.js index 678a9e4e8c..d711e7cdcc 100644 --- a/src/controllers/admin/settings.js +++ b/src/controllers/admin/settings.js @@ -4,7 +4,7 @@ var async = require('async'); var meta = require('../../meta'); var emailer = require('../../emailer'); -var plugins = require('../../plugins'); +var notifications = require('../../notifications'); var settingsController = module.exports; @@ -45,32 +45,12 @@ function renderEmail(req, res, next) { } function renderUser(req, res, next) { - var types = [ - 'notificationType_upvote', - 'notificationType_new-topic', - 'notificationType_new-reply', - 'notificationType_follow', - 'notificationType_new-chat', - 'notificationType_group-invite', - ]; - - var privilegedTypes = [ - 'notificationType_new-register', - 'notificationType_post-queue', - 'notificationType_new-post-flag', - 'notificationType_new-user-flag', - ]; - async.waterfall([ function (next) { - plugins.fireHook('filter:user.notificationTypes', { - userData: {}, - types: types, - privilegedTypes: privilegedTypes, - }, next); + notifications.getAllNotificationTypes(next); }, - function (results) { - var notificationSettings = results.types.concat(results.privilegedTypes).map(function (type) { + function (notificationTypes) { + var notificationSettings = notificationTypes.map(function (type) { return { name: type, label: '[[notifications:' + type + ']]', diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index f8537da085..f9eb6f75e2 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -387,8 +387,8 @@ authenticationController.localLogin = function (req, username, password, next) { userData: function (next) { db.getObjectFields('user:' + uid, ['password', 'passwordExpiry'], next); }, - isAdmin: function (next) { - user.isAdministrator(uid, next); + isAdminOrGlobalMod: function (next) { + user.isAdminOrGlobalMod(uid, next); }, banned: function (next) { user.isBanned(uid, next); @@ -398,9 +398,9 @@ authenticationController.localLogin = function (req, username, password, next) { function (result, next) { userData = result.userData; userData.uid = uid; - userData.isAdmin = result.isAdmin; + userData.isAdminOrGlobalMod = result.isAdminOrGlobalMod; - if (!result.isAdmin && parseInt(meta.config.allowLocalLogin, 10) === 0) { + if (!result.isAdminOrGlobalMod && parseInt(meta.config.allowLocalLogin, 10) === 0) { return next(new Error('[[error:local-login-disabled]]')); } diff --git a/src/meta/templates.js b/src/meta/templates.js index f8b63d41fa..b2d14801ae 100644 --- a/src/meta/templates.js +++ b/src/meta/templates.js @@ -11,6 +11,7 @@ var _ = require('lodash'); var plugins = require('../plugins'); var file = require('../file'); +var db = require('../database'); var viewsPath = nconf.get('views_dir'); @@ -44,20 +45,22 @@ function processImports(paths, templatePath, source, callback) { } Templates.processImports = processImports; -function getTemplateDirs(callback) { - var pluginTemplates = _.values(plugins.pluginsData) - .filter(function (pluginData) { - return !pluginData.id.startsWith('nodebb-theme-'); - }) - .map(function (pluginData) { - return path.join(__dirname, '../../node_modules/', pluginData.id, pluginData.templates || 'templates'); - }); +function getTemplateDirs(activePlugins, callback) { + var pluginTemplates = activePlugins.map(function (id) { + if (id.startsWith('nodebb-theme-')) { + return nconf.get('theme_templates_path'); + } + if (!plugins.pluginsData[id]) { + return ''; + } + return path.join(__dirname, '../../node_modules/', id, plugins.pluginsData[id].templates || 'templates'); + }).filter(Boolean); var themeConfig = require(nconf.get('theme_config')); var theme = themeConfig.baseTheme; var themePath; - var themeTemplates = [nconf.get('theme_templates_path')]; + var themeTemplates = []; while (theme) { themePath = path.join(nconf.get('themes_path'), theme); themeConfig = require(path.join(themePath, 'theme.json')); @@ -118,6 +121,9 @@ function compile(callback) { function (next) { mkdirp(viewsPath, function (err) { next(err); }); }, + function (next) { + db.getSortedSetRange('plugins:active', 0, -1, next); + }, getTemplateDirs, getTemplateFiles, function (files, next) { diff --git a/src/notifications.js b/src/notifications.js index 8a91c44509..a9ef31f734 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -17,6 +17,36 @@ var emailer = require('./emailer'); var Notifications = module.exports; +Notifications.baseTypes = [ + 'notificationType_upvote', + 'notificationType_new-topic', + 'notificationType_new-reply', + 'notificationType_follow', + 'notificationType_new-chat', + 'notificationType_group-invite', +]; + +Notifications.privilegedTypes = [ + 'notificationType_new-register', + 'notificationType_post-queue', + 'notificationType_new-post-flag', + 'notificationType_new-user-flag', +]; + +Notifications.getAllNotificationTypes = function (callback) { + async.waterfall([ + function (next) { + plugins.fireHook('filter:user.notificationTypes', { + types: Notifications.baseTypes.slice(), + privilegedTypes: Notifications.privilegedTypes.slice(), + }, next); + }, + function (results, next) { + next(null, results.types.concat(results.privilegedTypes)); + }, + ], callback); +}; + Notifications.startJobs = function () { winston.verbose('[notifications.init] Registering jobs.'); new cron('*/30 * * * *', Notifications.prune, null, true); diff --git a/src/posts/diffs.js b/src/posts/diffs.js index 782115b74b..6efaabcfb9 100644 --- a/src/posts/diffs.js +++ b/src/posts/diffs.js @@ -27,7 +27,7 @@ Diffs.get = function (pid, since, callback) { function (timestamps, next) { // Pass those made after `since`, and create keys const keys = timestamps.filter(function (timestamp) { - return (parseInt(timestamp, 10) || 0) > since; + return (parseInt(timestamp, 10) || 0) >= since; }).map(function (timestamp) { return 'diff:' + pid + '.' + timestamp; }); diff --git a/src/user/settings.js b/src/user/settings.js index c654d2dd99..202c440d14 100644 --- a/src/user/settings.js +++ b/src/user/settings.js @@ -6,6 +6,7 @@ var async = require('async'); var meta = require('../meta'); var db = require('../database'); var plugins = require('../plugins'); +var notifications = require('../notifications'); module.exports = function (User) { User.getSettings = function (uid, callback) { @@ -81,12 +82,14 @@ module.exports = function (User) { settings.delayImageLoading = parseInt(getSetting(settings, 'delayImageLoading', 1), 10) === 1; settings.bootswatchSkin = settings.bootswatchSkin || meta.config.bootswatchSkin || 'default'; settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1; - settings.notificationType_upvote = getSetting(settings, 'notificationType_upvote', 'notification'); - settings['notificationType_new-topic'] = getSetting(settings, 'notificationType_new-topic', 'notification'); - settings['notificationType_new-reply'] = getSetting(settings, 'notificationType_new-reply', 'notification'); - settings.notificationType_follow = getSetting(settings, 'notificationType_follow', 'notification'); - settings['notificationType_new-chat'] = getSetting(settings, 'notificationType_new-chat', 'notification'); - settings['notificationType_group-invite'] = getSetting(settings, 'notificationType_group-invite', 'notification'); + + notifications.getAllNotificationTypes(next); + }, + function (notificationTypes, next) { + notificationTypes.forEach(function (notificationType) { + settings[notificationType] = getSetting(settings, notificationType, 'notification'); + }); + next(null, settings); }, ], callback); @@ -139,26 +142,20 @@ module.exports = function (User) { upvoteNotifFreq: data.upvoteNotifFreq, }; - var notificationTypes = [ - 'notificationType_upvote', 'notificationType_new-topic', 'notificationType_new-reply', - 'notificationType_follow', 'notificationType_new-chat', 'notificationType_group-invite', - 'notificationType_new-register', 'notificationType_post-queue', 'notificationType_new-post-flag', - 'notificationType_new-user-flag', - ]; - - notificationTypes.forEach(function (notificationType) { - if (data[notificationType]) { - settings[notificationType] = data[notificationType]; - } - }); - - if (data.bootswatchSkin) { settings.bootswatchSkin = data.bootswatchSkin; } async.waterfall([ function (next) { + notifications.getAllNotificationTypes(next); + }, + function (notificationTypes, next) { + notificationTypes.forEach(function (notificationType) { + if (data[notificationType]) { + settings[notificationType] = data[notificationType]; + } + }); plugins.fireHook('filter:user.saveSettings', { settings: settings, data: data }, next); }, function (result, next) { diff --git a/src/views/500-embed.tpl b/src/views/500-embed.tpl index d9d9f30d5d..3bd6857048 100644 --- a/src/views/500-embed.tpl +++ b/src/views/500-embed.tpl @@ -1,6 +1,6 @@