From 61c76e4aba1213995af8352250057bb320cd4537 Mon Sep 17 00:00:00 2001 From: akhoury Date: Sun, 7 Feb 2016 13:16:50 -0500 Subject: [PATCH 01/62] add continuation-local-storage support --- package.json | 1 + src/middleware/cls.js | 41 +++++++++++++++++++++++++++++++++++++++++ src/middleware/index.js | 2 ++ 3 files changed, 44 insertions(+) create mode 100644 src/middleware/cls.js diff --git a/package.json b/package.json index b7f7ed676a..0fa110de82 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "connect-mongo": "~1.1.0", "connect-multiparty": "^2.0.0", "connect-redis": "~3.0.2", + "continuation-local-storage": "^3.1.6", "cookie-parser": "^1.3.3", "cron": "^1.0.5", "csurf": "^1.6.1", diff --git a/src/middleware/cls.js b/src/middleware/cls.js new file mode 100644 index 0000000000..31a1065891 --- /dev/null +++ b/src/middleware/cls.js @@ -0,0 +1,41 @@ + +var continuationLocalStorage = require('continuation-local-storage'); + +var NAMESPACE = 'nodebb'; +var namespace = continuationLocalStorage.createNamespace(NAMESPACE); + +var cls = function (req, res, next) { + namespace.run(function() { + var value = {req: req}; + if (process.env.NODE_ENV == 'development') { + value.audit = {created: process.hrtime()}; + } + namespace.set('route', { + req: req, + res: res + }); + next(); + }); +}; + +cls.storage = function () { + return cls.getNamespace(NAMESPACE); +}; + +cls.get = function (key) { + return namespace.get(key); +}; + +cls.set = function (key, value) { + return namespace.set(key, value); +}; + +cls.setItem = cls.set; +cls.getItem = cls.set; +cls.getNamespace = cls.storage; +cls.namespace = namespace; +cls.continuationLocalStorage = continuationLocalStorage; + +module.exports = cls; + + diff --git a/src/middleware/index.js b/src/middleware/index.js index 1cbac02323..3efb1a18f2 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -14,6 +14,7 @@ var meta = require('../meta'), compression = require('compression'), favicon = require('serve-favicon'), session = require('express-session'), + cls = require('./cls'), useragent = require('express-useragent'); @@ -73,6 +74,7 @@ module.exports = function(app) { app.use(middleware.addHeaders); app.use(middleware.processRender); + app.use(cls); auth.initialize(app, middleware); return middleware; From 852a1a178e95821d33d877272b992352946e8f17 Mon Sep 17 00:00:00 2001 From: akhoury Date: Sun, 7 Feb 2016 13:34:24 -0500 Subject: [PATCH 02/62] oops --- src/middleware/cls.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/middleware/cls.js b/src/middleware/cls.js index 31a1065891..76a26f9c4f 100644 --- a/src/middleware/cls.js +++ b/src/middleware/cls.js @@ -6,14 +6,12 @@ var namespace = continuationLocalStorage.createNamespace(NAMESPACE); var cls = function (req, res, next) { namespace.run(function() { - var value = {req: req}; + var routeData = {req: req, res: res}; + if (process.env.NODE_ENV == 'development') { - value.audit = {created: process.hrtime()}; + routeData.audit = {created: process.hrtime()}; } - namespace.set('route', { - req: req, - res: res - }); + namespace.set('route', routeData); next(); }); }; From 4245cb2739595557a713e5b94bc9b56e15f041ec Mon Sep 17 00:00:00 2001 From: akhoury Date: Fri, 12 Feb 2016 12:20:21 -0500 Subject: [PATCH 03/62] adding cls support for ws --- src/middleware/cls.js | 27 +++++++++++++++------------ src/middleware/index.js | 2 +- src/socket.io/index.js | 15 +++++++++++---- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/middleware/cls.js b/src/middleware/cls.js index 76a26f9c4f..16b0e4f848 100644 --- a/src/middleware/cls.js +++ b/src/middleware/cls.js @@ -1,23 +1,26 @@ - +var path = require('path'); var continuationLocalStorage = require('continuation-local-storage'); +var APP_NAMESPACE = require(path.join(__dirname, '../../package.json')).name; +var namespace = continuationLocalStorage.createNamespace(APP_NAMESPACE); -var NAMESPACE = 'nodebb'; -var namespace = continuationLocalStorage.createNamespace(NAMESPACE); +var cls = {}; -var cls = function (req, res, next) { +cls.http = function (req, res, next) { namespace.run(function() { - var routeData = {req: req, res: res}; - - if (process.env.NODE_ENV == 'development') { - routeData.audit = {created: process.hrtime()}; - } - namespace.set('route', routeData); + namespace.set('http', {req: req, res: res}); next(); }); }; -cls.storage = function () { - return cls.getNamespace(NAMESPACE); +cls.socket = function (socket, payload, event, next) { + namespace.run(function() { + namespace.set('ws', { + socket: socket, + payload: payload, + // if it's a '*' event, then we grab it from the payload + event: event || ((payload || {}).data || [])[0]}); + next(); + }); }; cls.get = function (key) { diff --git a/src/middleware/index.js b/src/middleware/index.js index 3efb1a18f2..f7e41b8f9b 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -74,7 +74,7 @@ module.exports = function(app) { app.use(middleware.addHeaders); app.use(middleware.processRender); - app.use(cls); + app.use(cls.http); auth.initialize(app, middleware); return middleware; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 826e5db452..4e91eb9ca6 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -11,6 +11,7 @@ var SocketIO = require('socket.io'), user = require('../user'), logger = require('../logger'), ratelimit = require('../middleware/ratelimit'), + cls = require('../middleware/cls'), Sockets = {}, Namespaces = {}; @@ -43,14 +44,20 @@ function onConnection(socket) { logger.io_one(socket, socket.uid); - onConnect(socket); + cls.socket(socket, null, 'connection', function () { + onConnect(socket); + }); - socket.on('disconnect', function(data) { - onDisconnect(socket, data); + socket.on('disconnect', function(payload) { + cls.socket(socket, payload, 'disconnect', function () { + onDisconnect(socket, payload); + }); }); socket.on('*', function(payload) { - onMessage(socket, payload); + cls.socket(socket, payload, null, function() { + onMessage(socket, payload); + }); }); } From 496e5ae8bfe9c8abf55b3a37bde9bdb05662cad5 Mon Sep 17 00:00:00 2001 From: akhoury Date: Fri, 12 Feb 2016 12:30:39 -0500 Subject: [PATCH 04/62] comment --- src/middleware/cls.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/cls.js b/src/middleware/cls.js index 16b0e4f848..ce3d79b922 100644 --- a/src/middleware/cls.js +++ b/src/middleware/cls.js @@ -17,7 +17,7 @@ cls.socket = function (socket, payload, event, next) { namespace.set('ws', { socket: socket, payload: payload, - // if it's a '*' event, then we grab it from the payload + // if it's a null event, then we grab it from the payload event: event || ((payload || {}).data || [])[0]}); next(); }); From f47c06279ae1a79a9acbe432b8e6de76e3f6dd53 Mon Sep 17 00:00:00 2001 From: akhoury Date: Tue, 16 Feb 2016 23:07:36 -0500 Subject: [PATCH 05/62] added depracation warning --- src/plugins/hooks.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 3330608154..7148654eeb 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -10,6 +10,9 @@ module.exports = function(Plugins) { 'action:user.loggedOut': 'static:user.loggedOut' }; + // todo: remove when breaking all hooks params by removing req/res/socket/uid + Plugins.clsDeprecatedParamsWarnedHooks = {}; + /* `data` is an object consisting of (* is required): `data.hook`*, the name of the NodeBB hook @@ -29,7 +32,7 @@ module.exports = function(Plugins) { var method; if (Object.keys(Plugins.deprecatedHooks).indexOf(data.hook) !== -1) { - winston.warn('[plugins/' + id + '] Hook `' + data.hook + '` is deprecated, ' + + winston.warn('[plugins/' + id + '] Hook `' + data.hook + '` is deprecated, ' + (Plugins.deprecatedHooks[data.hook] ? 'please use `' + Plugins.deprecatedHooks[data.hook] + '` instead.' : 'there is no alternative.' @@ -71,6 +74,20 @@ module.exports = function(Plugins) { var hookList = Plugins.loadedHooks[hook]; var hookType = hook.split(':')[0]; + // todo: remove when breaking all hooks params by removing req/res/socket/uid + if (!Plugins.clsDeprecatedParamsWarnedHooks[hook] + && params + && Array.isArray(hookList) + && hookList.length + && (params.req || params.res || params.socket || params.uid)) { + + Plugins.clsDeprecatedParamsWarnedHooks[hook] = true; + + winston.warn('[plugins] hook `' + hook + '` \'s `params.req`, `params.res`, `params.uid` and `params.socket` are being deprecated, ' + + 'plugins should use the `middleware/cls` module instead to get a reference to the http-req/res and socket (which you can get the current `uid`) ' + + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-req-res-socket-and-uid-within-any-plugin-hook'); + } + switch (hookType) { case 'filter': fireFilterHook(hook, hookList, params, callback); From a8411d44fdc7bd143ec34a73e98caff0584bf4b4 Mon Sep 17 00:00:00 2001 From: akhoury Date: Sun, 28 Feb 2016 14:52:51 -0500 Subject: [PATCH 06/62] merge --- src/socket.io/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 8f176e5241..de504ab523 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -1,11 +1,5 @@ "use strict"; -var SocketIO = require('socket.io'), - socketioWildcard = require('socketio-wildcard')(), - async = require('async'), - nconf = require('nconf'), - cookieParser = require('cookie-parser')(nconf.get('secret')), - winston = require('winston'), var SocketIO = require('socket.io'); var socketioWildcard = require('socketio-wildcard')(); var async = require('async'); From 509676fdf40d17a6867418d7d6ad176e4f0d028e Mon Sep 17 00:00:00 2001 From: akhoury Date: Sun, 28 Feb 2016 15:24:31 -0500 Subject: [PATCH 07/62] add deprecation warnings for CLS --- src/plugins/hooks.js | 48 +++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 7148654eeb..04691bce71 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -10,8 +10,27 @@ module.exports = function(Plugins) { 'action:user.loggedOut': 'static:user.loggedOut' }; - // todo: remove when breaking all hooks params by removing req/res/socket/uid - Plugins.clsDeprecatedParamsWarnedHooks = {}; + Plugins.deprecatedHooksParams = { + 'action:homepage.get': '{req, res}', + 'filter:register.check': '{req, res}', + 'action:user.loggedOut': '{req, res}', + 'static:user.loggedOut': '{req, res}', + 'filter:categories.build': '{req, res}', + 'filter:category.build': '{req, res}', + 'filter:group.build': '{req, res}', + 'filter:register.build': '{req, res}', + 'filter:composer.build': '{req, res}', + 'filter:popular.build': '{req, res}', + 'filter:recent.build': '{req, res}', + 'filter:topic.build': '{req, res}', + 'filter:users.build': '{req, res}', + 'filter:admin.category.get': '{req, res}', + 'filter:middleware.renderHeader': '{req, res}', + 'filter:widget.render': '{req, res}', + 'filter:middleware.buildHeader': '{req, locals}', + 'action:middleware.pageView': '{req}', + 'action:meta.override404': '{req}' + }; /* `data` is an object consisting of (* is required): @@ -38,6 +57,17 @@ module.exports = function(Plugins) { 'there is no alternative.' ) ); + } else { + // handle hook's startsWith, i.e. action:homepage.get + var _parts = data.hook.split(':'); + _parts.pop(); + var _hook = _parts.join(':'); + if (Plugins.deprecatedHooksParams[_hook]) { + winston.warn('[plugins/' + id + '] Hook `' + _hook + '` parameters: `' + Plugins.deprecatedHooksParams[_hook] + '`, are being deprecated, ' + + 'all plugins should now use the `middleware/cls` module instead of hook\'s arguments to get a reference to the `req`, `res` and/or `socket` object(s) (from which you can get the current `uid` if you need to.) ' + + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-req-res-socket-and-uid-within-any-plugin-hook') + delete Plugins.deprecatedHooksParams[_hook]; + } } if (data.hook && data.method) { @@ -74,20 +104,6 @@ module.exports = function(Plugins) { var hookList = Plugins.loadedHooks[hook]; var hookType = hook.split(':')[0]; - // todo: remove when breaking all hooks params by removing req/res/socket/uid - if (!Plugins.clsDeprecatedParamsWarnedHooks[hook] - && params - && Array.isArray(hookList) - && hookList.length - && (params.req || params.res || params.socket || params.uid)) { - - Plugins.clsDeprecatedParamsWarnedHooks[hook] = true; - - winston.warn('[plugins] hook `' + hook + '` \'s `params.req`, `params.res`, `params.uid` and `params.socket` are being deprecated, ' - + 'plugins should use the `middleware/cls` module instead to get a reference to the http-req/res and socket (which you can get the current `uid`) ' - + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-req-res-socket-and-uid-within-any-plugin-hook'); - } - switch (hookType) { case 'filter': fireFilterHook(hook, hookList, params, callback); From 65848d1a763be07cf09aa4b90a41e7d8830b56ca Mon Sep 17 00:00:00 2001 From: Yami Date: Fri, 1 Apr 2016 12:14:03 +0000 Subject: [PATCH 08/62] Updating instructions to match reality --- tests/mocks/databasemock.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mocks/databasemock.js b/tests/mocks/databasemock.js index a895d25c8a..06b3790a65 100644 --- a/tests/mocks/databasemock.js +++ b/tests/mocks/databasemock.js @@ -36,14 +36,14 @@ ' "host": "127.0.0.1",' + '\n' + ' "port": "6379",' + '\n' + ' "password": "",' + '\n' + - ' "database": "1"' + '\n' + + ' "database": "redis"' + '\n' + '}\n'+ ' or (mongo):\n' + '"test_database": {' + '\n' + ' "host": "127.0.0.1",' + '\n' + ' "port": "27017",' + '\n' + ' "password": "",' + '\n' + - ' "database": "1"' + '\n' + + ' "database": "mongo"' + '\n' + '}\n'+ ' or (mongo) in a replicaset' + '\n' + '"test_database": {' + '\n' + From d78edf6f726889ce01b0c8d5aa79990a35ec5b4e Mon Sep 17 00:00:00 2001 From: Yami Date: Fri, 1 Apr 2016 12:20:02 +0000 Subject: [PATCH 09/62] adding istanbul coverage --- .gitignore | 5 ++++- package.json | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 144cfe268a..a8ea9cb1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,7 @@ pidfile ## Transifex tx.exe -.transifexrc \ No newline at end of file +.transifexrc + +##Coverage output +coverage \ No newline at end of file diff --git a/package.json b/package.json index e675f0d17f..910279dd35 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "main": "app.js", "scripts": { "start": "node loader.js", - "test": "mocha ./tests -t 10000" + "test": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- ./tests -t 10000" }, "dependencies": { "async": "~1.5.0", @@ -85,9 +85,10 @@ "xregexp": "~3.1.0" }, "devDependencies": { - "mocha": "~1.13.0", "grunt": "~0.4.5", - "grunt-contrib-watch": "^0.6.1" + "grunt-contrib-watch": "^0.6.1", + "istanbul": "^0.4.2", + "mocha": "~1.13.0" }, "bugs": { "url": "https://github.com/NodeBB/NodeBB/issues" From 31dcd99e6e32f29798f79a2f672c56f6e8e8b54b Mon Sep 17 00:00:00 2001 From: Yami Date: Fri, 1 Apr 2016 12:23:51 +0000 Subject: [PATCH 10/62] Changed to automatically report coverage --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 910279dd35..cc73da942e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "main": "app.js", "scripts": { "start": "node loader.js", - "test": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- ./tests -t 10000" + "test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- ./tests -t 10000" }, "dependencies": { "async": "~1.5.0", From 1a77b18a8261956b0083004d027098ce7bff7fce Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Tue, 5 Apr 2016 19:47:45 -0500 Subject: [PATCH 11/62] Revert "Updating instructions to match reality" Example of a working config file: { "url": "http://localhost:4567", "secret": "c6e41882-ac49-4977-a712-312e47eb0fa4", "database": "mongo", "port": "4567", "mongo": { "host": "172.17.0.2", "port": 27017 }, "test_database": { "host": "172.17.0.2", "port": "27017", "password": "", "database": "1" } } This reverts commit 65848d1a763be07cf09aa4b90a41e7d8830b56ca. --- tests/mocks/databasemock.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mocks/databasemock.js b/tests/mocks/databasemock.js index 06b3790a65..a895d25c8a 100644 --- a/tests/mocks/databasemock.js +++ b/tests/mocks/databasemock.js @@ -36,14 +36,14 @@ ' "host": "127.0.0.1",' + '\n' + ' "port": "6379",' + '\n' + ' "password": "",' + '\n' + - ' "database": "redis"' + '\n' + + ' "database": "1"' + '\n' + '}\n'+ ' or (mongo):\n' + '"test_database": {' + '\n' + ' "host": "127.0.0.1",' + '\n' + ' "port": "27017",' + '\n' + ' "password": "",' + '\n' + - ' "database": "mongo"' + '\n' + + ' "database": "1"' + '\n' + '}\n'+ ' or (mongo) in a replicaset' + '\n' + '"test_database": {' + '\n' + From 53e96270012fe3ea0a6cb605ffdfcb0b94ce25f2 Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 15:55:55 -0400 Subject: [PATCH 12/62] unify request as a store key for both http and websockets calls --- src/middleware/cls.js | 11 ++++------- src/socket.io/index.js | 7 +++++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/middleware/cls.js b/src/middleware/cls.js index ce3d79b922..ce795eae6a 100644 --- a/src/middleware/cls.js +++ b/src/middleware/cls.js @@ -1,4 +1,6 @@ var path = require('path'); +var sockets = require('path'); +var websockets = require('../socket.io'); var continuationLocalStorage = require('continuation-local-storage'); var APP_NAMESPACE = require(path.join(__dirname, '../../package.json')).name; var namespace = continuationLocalStorage.createNamespace(APP_NAMESPACE); @@ -7,18 +9,14 @@ var cls = {}; cls.http = function (req, res, next) { namespace.run(function() { - namespace.set('http', {req: req, res: res}); + namespace.set('request', req); next(); }); }; cls.socket = function (socket, payload, event, next) { namespace.run(function() { - namespace.set('ws', { - socket: socket, - payload: payload, - // if it's a null event, then we grab it from the payload - event: event || ((payload || {}).data || [])[0]}); + namespace.set('request', websockets.reqFromSocket(socket, payload, event)); next(); }); }; @@ -33,7 +31,6 @@ cls.set = function (key, value) { cls.setItem = cls.set; cls.getItem = cls.set; -cls.getNamespace = cls.storage; cls.namespace = namespace; cls.continuationLocalStorage = continuationLocalStorage; diff --git a/src/socket.io/index.js b/src/socket.io/index.js index de504ab523..76fda883a6 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -248,7 +248,7 @@ Sockets.getOnlineAnonCount = function () { return room ? room.length : 0; }; -Sockets.reqFromSocket = function(socket) { +Sockets.reqFromSocket = function(socket, payload, event) { var headers = socket.request.headers; var host = headers.host; var referer = headers.referer || ''; @@ -256,11 +256,14 @@ Sockets.reqFromSocket = function(socket) { return { ip: headers['x-forwarded-for'] || socket.ip, host: host, + uid: socket.uid, protocol: socket.request.connection.encrypted ? 'https' : 'http', secure: !!socket.request.connection.encrypted, url: referer, + body: {event: event || ((payload || {}).data || [])[0], payload: payload}, path: referer.substr(referer.indexOf(host) + host.length), - headers: headers + headers: headers, + _socket: socket }; }; From 4f3a962f7fe3636094c4b5062ca6236e3448453f Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 16:42:22 -0400 Subject: [PATCH 13/62] what did i do? --- src/socket.io/index.js | 344 ++++++++++++++++++++--------------------- 1 file changed, 164 insertions(+), 180 deletions(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 4092b576bf..809f21ea11 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -8,227 +8,211 @@ var cookieParser = require('cookie-parser')(nconf.get('secret')); var winston = require('winston'); var db = require('../database'); -var user = require('../user'); var logger = require('../logger'); var ratelimit = require('../middleware/ratelimit'); -var cls = require('../middleware/cls'); + +var Sockets = {}; +var Namespaces = {}; var io; -(function(Sockets) { - var Namespaces = {}; +Sockets.init = function(server) { + requireModules(); - Sockets.init = function(server) { - requireModules(); + io = new SocketIO({ + path: nconf.get('relative_path') + '/socket.io' + }); - io = new SocketIO({ - path: nconf.get('relative_path') + '/socket.io' - }); + addRedisAdapter(io); - addRedisAdapter(io); + io.use(socketioWildcard); + io.use(authorize); - io.use(socketioWildcard); - io.use(authorize); + io.on('connection', onConnection); - io.on('connection', onConnection); + io.listen(server, { + transports: nconf.get('socket.io:transports') + }); - io.on('disconnect', function(data) { - onDisconnect(io, data); - }); + Sockets.server = io; +}; - io.listen(server, { - transports: nconf.get('socket.io:transports') - }); +function onConnection(socket) { + socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; - Sockets.server = io; - }; + logger.io_one(socket, socket.uid); - function onConnection(socket) { - socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; + onConnect(socket); - logger.io_one(socket, socket.uid); + socket.on('*', function(payload) { + onMessage(socket, payload); + }); +} - cls.socket(socket, null, 'connection', function () { - onConnect(socket); - }); +function onConnect(socket) { + if (socket.uid) { + socket.join('uid_' + socket.uid); + socket.join('online_users'); + } else { + socket.join('online_guests'); + } +} - socket.on('*', function(payload) { - cls.socket(socket, payload, null, function() { - onMessage(socket, payload); - }); - }); + +function onMessage(socket, payload) { + if (!payload.data.length) { + return winston.warn('[socket.io] Empty payload'); } - function onConnect(socket) { - if (socket.uid) { - socket.join('uid_' + socket.uid); - socket.join('online_users'); + var eventName = payload.data[0]; + var params = payload.data[1]; + var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function() {}; + + if (!eventName) { + return winston.warn('[socket.io] Empty method name'); + } + + var parts = eventName.toString().split('.'); + var namespace = parts[0]; + var methodToCall = parts.reduce(function(prev, cur) { + if (prev !== null && prev[cur]) { + return prev[cur]; } else { - socket.join('online_guests'); + return null; } + }, Namespaces); + + if(!methodToCall) { + if (process.env.NODE_ENV === 'development') { + winston.warn('[socket.io] Unrecognized message: ' + eventName); + } + return; } - function onDisconnect(socket) { - cls.socket(socket, null, 'disconnect', function() {}); + socket.previousEvents = socket.previousEvents || []; + socket.previousEvents.push(eventName); + if (socket.previousEvents.length > 20) { + socket.previousEvents.shift(); } + if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { + winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); + return socket.disconnect(); + } - function onMessage(socket, payload) { - if (!payload.data.length) { - return winston.warn('[socket.io] Empty payload'); - } - - var eventName = payload.data[0]; - var params = payload.data[1]; - var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function() {}; - - if (!eventName) { - return winston.warn('[socket.io] Empty method name'); - } - - var parts = eventName.toString().split('.'); - var namespace = parts[0]; - var methodToCall = parts.reduce(function(prev, cur) { - if (prev !== null && prev[cur]) { - return prev[cur]; + async.waterfall([ + function (next) { + validateSession(socket, next); + }, + function (next) { + if (Namespaces[namespace].before) { + Namespaces[namespace].before(socket, eventName, params, next); } else { - return null; + next(); } - }, Namespaces); + }, + function (next) { + methodToCall(socket, params, next); + } + ], function(err, result) { + callback(err ? {message: err.message} : null, result); + }); +} - if(!methodToCall) { - if (process.env.NODE_ENV === 'development') { - winston.warn('[socket.io] Unrecognized message: ' + eventName); - } - return; +function requireModules() { + var modules = ['admin', 'categories', 'groups', 'meta', 'modules', + 'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' + ]; + + modules.forEach(function(module) { + Namespaces[module] = require('./' + module); + }); +} + +function validateSession(socket, callback) { + var req = socket.request; + if (!req.signedCookies || !req.signedCookies['express.sid']) { + return callback(new Error('[[error:invalid-session]]')); + } + db.sessionStore.get(req.signedCookies['express.sid'], function(err, sessionData) { + if (err || !sessionData) { + return callback(err || new Error('[[error:invalid-session]]')); } - socket.previousEvents = socket.previousEvents || []; - socket.previousEvents.push(eventName); - if (socket.previousEvents.length > 20) { - socket.previousEvents.shift(); - } + callback(); + }); +} - if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { - winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); - return socket.disconnect(); - } +function authorize(socket, callback) { + var request = socket.request; - async.waterfall([ - function (next) { - validateSession(socket, next); - }, - function (next) { - if (Namespaces[namespace].before) { - Namespaces[namespace].before(socket, eventName, params, next); - } else { - next(); + if (!request) { + return callback(new Error('[[error:not-authorized]]')); + } + + async.waterfall([ + function(next) { + cookieParser(request, {}, next); + }, + function(next) { + db.sessionStore.get(request.signedCookies['express.sid'], function(err, sessionData) { + if (err) { + return next(err); } - }, - function (next) { - methodToCall(socket, params, next); - } - ], function(err, result) { - callback(err ? {message: err.message} : null, result); - }); - } - - function requireModules() { - var modules = ['admin', 'categories', 'groups', 'meta', 'modules', - 'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' - ]; - - modules.forEach(function(module) { - Namespaces[module] = require('./' + module); - }); - } - - function validateSession(socket, callback) { - var req = socket.request; - if (!req.signedCookies || !req.signedCookies['express.sid']) { - return callback(new Error('[[error:invalid-session]]')); + if (sessionData && sessionData.passport && sessionData.passport.user) { + request.session = sessionData; + socket.uid = parseInt(sessionData.passport.user, 10); + } else { + socket.uid = 0; + } + next(); + }); } - db.sessionStore.get(req.signedCookies['express.sid'], function(err, sessionData) { - if (err || !sessionData) { - return callback(err || new Error('[[error:invalid-session]]')); - } + ], callback); +} - callback(); - }); +function addRedisAdapter(io) { + if (nconf.get('redis')) { + var redisAdapter = require('socket.io-redis'); + var redis = require('../database/redis'); + var pub = redis.connect({return_buffers: true}); + var sub = redis.connect({return_buffers: true}); + + io.adapter(redisAdapter({pubClient: pub, subClient: sub})); + } else if (nconf.get('isCluster') === 'true') { + winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); + } +} + +Sockets.in = function(room) { + return io.in(room); +}; + +Sockets.getUserSocketCount = function(uid) { + if (!io) { + return 0; } - function authorize(socket, callback) { - var request = socket.request; + var room = io.sockets.adapter.rooms['uid_' + uid]; + return room ? room.length : 0; +}; - if (!request) { - return callback(new Error('[[error:not-authorized]]')); - } - async.waterfall([ - function(next) { - cookieParser(request, {}, next); - }, - function(next) { - db.sessionStore.get(request.signedCookies['express.sid'], function(err, sessionData) { - if (err) { - return next(err); - } - if (sessionData && sessionData.passport && sessionData.passport.user) { - request.session = sessionData; - socket.uid = parseInt(sessionData.passport.user, 10); - } else { - socket.uid = 0; - } - next(); - }); - } - ], callback); - } +Sockets.reqFromSocket = function(socket) { + var headers = socket.request.headers; + var host = headers.host; + var referer = headers.referer || ''; - function addRedisAdapter(io) { - if (nconf.get('redis')) { - var redisAdapter = require('socket.io-redis'); - var redis = require('../database/redis'); - var pub = redis.connect({return_buffers: true}); - var sub = redis.connect({return_buffers: true}); - - io.adapter(redisAdapter({pubClient: pub, subClient: sub})); - } else if (nconf.get('isCluster') === 'true') { - winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); - } - } - - Sockets.in = function(room) { - return io.in(room); - }; - - Sockets.getUserSocketCount = function(uid) { - if (!io) { - return 0; - } - - var room = io.sockets.adapter.rooms['uid_' + uid]; - return room ? room.length : 0; + return { + ip: headers['x-forwarded-for'] || socket.ip, + host: host, + protocol: socket.request.connection.encrypted ? 'https' : 'http', + secure: !!socket.request.connection.encrypted, + url: referer, + path: referer.substr(referer.indexOf(host) + host.length), + headers: headers }; +}; - Sockets.reqFromSocket = function(socket, payload, event) { - var headers = socket.request.headers; - var host = headers.host; - var referer = headers.referer || ''; - - return { - ip: headers['x-forwarded-for'] || socket.ip, - host: host, - uid: socket.uid, - protocol: socket.request.connection.encrypted ? 'https' : 'http', - secure: !!socket.request.connection.encrypted, - url: referer, - body: {event: event || ((payload || {}).data || [])[0], payload: payload}, - path: referer.substr(referer.indexOf(host) + host.length), - headers: headers, - _socket: socket - }; - }; - -})(exports); \ No newline at end of file +module.exports = Sockets; \ No newline at end of file From 807e3a9d7e28e92249713653db32cb34b5850b09 Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 16:45:04 -0400 Subject: [PATCH 14/62] adding cls back in socketio index --- src/socket.io/index.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 809f21ea11..419b083478 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -10,6 +10,7 @@ var winston = require('winston'); var db = require('../database'); var logger = require('../logger'); var ratelimit = require('../middleware/ratelimit'); +var cls = require('../middleware/cls'); var Sockets = {}; var Namespaces = {}; @@ -29,6 +30,7 @@ Sockets.init = function(server) { io.use(authorize); io.on('connection', onConnection); + io.on('disconnect', onDisconnect); io.listen(server, { transports: nconf.get('socket.io:transports') @@ -42,10 +44,14 @@ function onConnection(socket) { logger.io_one(socket, socket.uid); - onConnect(socket); + cls.socket(socket, null, 'connection', function() { + onConnect(socket); + }); socket.on('*', function(payload) { - onMessage(socket, payload); + cls.socket(socket, payload, null, function() { + onMessage(socket, payload); + }); }); } @@ -58,6 +64,10 @@ function onConnect(socket) { } } +function onDisconnect(socket) { + cls.socket(socket, null, 'disconnect', function() {}); +} + function onMessage(socket, payload) { if (!payload.data.length) { From c07e29bad6b1f7b568ad9c87cf4d90e4dfc71f8b Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 16:47:55 -0400 Subject: [PATCH 15/62] fix circular dependency -- involves indentations fix --- src/socket.io/index.js | 406 ++++++++++++++++++++--------------------- 1 file changed, 203 insertions(+), 203 deletions(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 419b083478..e417f6d250 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -12,217 +12,217 @@ var logger = require('../logger'); var ratelimit = require('../middleware/ratelimit'); var cls = require('../middleware/cls'); -var Sockets = {}; -var Namespaces = {}; +(function(Sockets) { + var Namespaces = {}; + var io; -var io; + Sockets.init = function (server) { + requireModules(); -Sockets.init = function(server) { - requireModules(); - - io = new SocketIO({ - path: nconf.get('relative_path') + '/socket.io' - }); - - addRedisAdapter(io); - - io.use(socketioWildcard); - io.use(authorize); - - io.on('connection', onConnection); - io.on('disconnect', onDisconnect); - - io.listen(server, { - transports: nconf.get('socket.io:transports') - }); - - Sockets.server = io; -}; - -function onConnection(socket) { - socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; - - logger.io_one(socket, socket.uid); - - cls.socket(socket, null, 'connection', function() { - onConnect(socket); - }); - - socket.on('*', function(payload) { - cls.socket(socket, payload, null, function() { - onMessage(socket, payload); + io = new SocketIO({ + path: nconf.get('relative_path') + '/socket.io' }); - }); -} -function onConnect(socket) { - if (socket.uid) { - socket.join('uid_' + socket.uid); - socket.join('online_users'); - } else { - socket.join('online_guests'); - } -} + addRedisAdapter(io); -function onDisconnect(socket) { - cls.socket(socket, null, 'disconnect', function() {}); -} + io.use(socketioWildcard); + io.use(authorize); + io.on('connection', onConnection); + io.on('disconnect', onDisconnect); -function onMessage(socket, payload) { - if (!payload.data.length) { - return winston.warn('[socket.io] Empty payload'); - } + io.listen(server, { + transports: nconf.get('socket.io:transports') + }); - var eventName = payload.data[0]; - var params = payload.data[1]; - var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function() {}; - - if (!eventName) { - return winston.warn('[socket.io] Empty method name'); - } - - var parts = eventName.toString().split('.'); - var namespace = parts[0]; - var methodToCall = parts.reduce(function(prev, cur) { - if (prev !== null && prev[cur]) { - return prev[cur]; - } else { - return null; - } - }, Namespaces); - - if(!methodToCall) { - if (process.env.NODE_ENV === 'development') { - winston.warn('[socket.io] Unrecognized message: ' + eventName); - } - return; - } - - socket.previousEvents = socket.previousEvents || []; - socket.previousEvents.push(eventName); - if (socket.previousEvents.length > 20) { - socket.previousEvents.shift(); - } - - if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { - winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); - return socket.disconnect(); - } - - async.waterfall([ - function (next) { - validateSession(socket, next); - }, - function (next) { - if (Namespaces[namespace].before) { - Namespaces[namespace].before(socket, eventName, params, next); - } else { - next(); - } - }, - function (next) { - methodToCall(socket, params, next); - } - ], function(err, result) { - callback(err ? {message: err.message} : null, result); - }); -} - -function requireModules() { - var modules = ['admin', 'categories', 'groups', 'meta', 'modules', - 'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' - ]; - - modules.forEach(function(module) { - Namespaces[module] = require('./' + module); - }); -} - -function validateSession(socket, callback) { - var req = socket.request; - if (!req.signedCookies || !req.signedCookies['express.sid']) { - return callback(new Error('[[error:invalid-session]]')); - } - db.sessionStore.get(req.signedCookies['express.sid'], function(err, sessionData) { - if (err || !sessionData) { - return callback(err || new Error('[[error:invalid-session]]')); - } - - callback(); - }); -} - -function authorize(socket, callback) { - var request = socket.request; - - if (!request) { - return callback(new Error('[[error:not-authorized]]')); - } - - async.waterfall([ - function(next) { - cookieParser(request, {}, next); - }, - function(next) { - db.sessionStore.get(request.signedCookies['express.sid'], function(err, sessionData) { - if (err) { - return next(err); - } - if (sessionData && sessionData.passport && sessionData.passport.user) { - request.session = sessionData; - socket.uid = parseInt(sessionData.passport.user, 10); - } else { - socket.uid = 0; - } - next(); - }); - } - ], callback); -} - -function addRedisAdapter(io) { - if (nconf.get('redis')) { - var redisAdapter = require('socket.io-redis'); - var redis = require('../database/redis'); - var pub = redis.connect({return_buffers: true}); - var sub = redis.connect({return_buffers: true}); - - io.adapter(redisAdapter({pubClient: pub, subClient: sub})); - } else if (nconf.get('isCluster') === 'true') { - winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); - } -} - -Sockets.in = function(room) { - return io.in(room); -}; - -Sockets.getUserSocketCount = function(uid) { - if (!io) { - return 0; - } - - var room = io.sockets.adapter.rooms['uid_' + uid]; - return room ? room.length : 0; -}; - - -Sockets.reqFromSocket = function(socket) { - var headers = socket.request.headers; - var host = headers.host; - var referer = headers.referer || ''; - - return { - ip: headers['x-forwarded-for'] || socket.ip, - host: host, - protocol: socket.request.connection.encrypted ? 'https' : 'http', - secure: !!socket.request.connection.encrypted, - url: referer, - path: referer.substr(referer.indexOf(host) + host.length), - headers: headers + Sockets.server = io; }; -}; + + function onConnection(socket) { + socket.ip = socket.request.headers['x-forwarded-for'] || socket.request.connection.remoteAddress; + + logger.io_one(socket, socket.uid); + + cls.socket(socket, null, 'connection', function () { + onConnect(socket); + }); + + socket.on('*', function (payload) { + cls.socket(socket, payload, null, function () { + onMessage(socket, payload); + }); + }); + } + + function onConnect(socket) { + if (socket.uid) { + socket.join('uid_' + socket.uid); + socket.join('online_users'); + } else { + socket.join('online_guests'); + } + } + + function onDisconnect(socket) { + cls.socket(socket, null, 'disconnect', function () { + }); + } -module.exports = Sockets; \ No newline at end of file + function onMessage(socket, payload) { + if (!payload.data.length) { + return winston.warn('[socket.io] Empty payload'); + } + + var eventName = payload.data[0]; + var params = payload.data[1]; + var callback = typeof payload.data[payload.data.length - 1] === 'function' ? payload.data[payload.data.length - 1] : function () { + }; + + if (!eventName) { + return winston.warn('[socket.io] Empty method name'); + } + + var parts = eventName.toString().split('.'); + var namespace = parts[0]; + var methodToCall = parts.reduce(function (prev, cur) { + if (prev !== null && prev[cur]) { + return prev[cur]; + } else { + return null; + } + }, Namespaces); + + if (!methodToCall) { + if (process.env.NODE_ENV === 'development') { + winston.warn('[socket.io] Unrecognized message: ' + eventName); + } + return; + } + + socket.previousEvents = socket.previousEvents || []; + socket.previousEvents.push(eventName); + if (socket.previousEvents.length > 20) { + socket.previousEvents.shift(); + } + + if (!eventName.startsWith('admin.') && ratelimit.isFlooding(socket)) { + winston.warn('[socket.io] Too many emits! Disconnecting uid : ' + socket.uid + '. Events : ' + socket.previousEvents); + return socket.disconnect(); + } + + async.waterfall([ + function (next) { + validateSession(socket, next); + }, + function (next) { + if (Namespaces[namespace].before) { + Namespaces[namespace].before(socket, eventName, params, next); + } else { + next(); + } + }, + function (next) { + methodToCall(socket, params, next); + } + ], function (err, result) { + callback(err ? {message: err.message} : null, result); + }); + } + + function requireModules() { + var modules = ['admin', 'categories', 'groups', 'meta', 'modules', + 'notifications', 'plugins', 'posts', 'topics', 'user', 'blacklist' + ]; + + modules.forEach(function (module) { + Namespaces[module] = require('./' + module); + }); + } + + function validateSession(socket, callback) { + var req = socket.request; + if (!req.signedCookies || !req.signedCookies['express.sid']) { + return callback(new Error('[[error:invalid-session]]')); + } + db.sessionStore.get(req.signedCookies['express.sid'], function (err, sessionData) { + if (err || !sessionData) { + return callback(err || new Error('[[error:invalid-session]]')); + } + + callback(); + }); + } + + function authorize(socket, callback) { + var request = socket.request; + + if (!request) { + return callback(new Error('[[error:not-authorized]]')); + } + + async.waterfall([ + function (next) { + cookieParser(request, {}, next); + }, + function (next) { + db.sessionStore.get(request.signedCookies['express.sid'], function (err, sessionData) { + if (err) { + return next(err); + } + if (sessionData && sessionData.passport && sessionData.passport.user) { + request.session = sessionData; + socket.uid = parseInt(sessionData.passport.user, 10); + } else { + socket.uid = 0; + } + next(); + }); + } + ], callback); + } + + function addRedisAdapter(io) { + if (nconf.get('redis')) { + var redisAdapter = require('socket.io-redis'); + var redis = require('../database/redis'); + var pub = redis.connect({return_buffers: true}); + var sub = redis.connect({return_buffers: true}); + + io.adapter(redisAdapter({pubClient: pub, subClient: sub})); + } else if (nconf.get('isCluster') === 'true') { + winston.warn('[socket.io] Clustering detected, you are advised to configure Redis as a websocket store.'); + } + } + + Sockets.in = function (room) { + return io.in(room); + }; + + Sockets.getUserSocketCount = function (uid) { + if (!io) { + return 0; + } + + var room = io.sockets.adapter.rooms['uid_' + uid]; + return room ? room.length : 0; + }; + + + Sockets.reqFromSocket = function (socket) { + var headers = socket.request.headers; + var host = headers.host; + var referer = headers.referer || ''; + + return { + ip: headers['x-forwarded-for'] || socket.ip, + host: host, + protocol: socket.request.connection.encrypted ? 'https' : 'http', + secure: !!socket.request.connection.encrypted, + url: referer, + path: referer.substr(referer.indexOf(host) + host.length), + headers: headers + }; + }; + +})(exports); \ No newline at end of file From 02e53fd4428f3861cbe422ba9955e045cb1fbc78 Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 16:59:25 -0400 Subject: [PATCH 16/62] update deprecation message --- src/plugins/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 04691bce71..c7d72f7d3e 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -65,7 +65,7 @@ module.exports = function(Plugins) { if (Plugins.deprecatedHooksParams[_hook]) { winston.warn('[plugins/' + id + '] Hook `' + _hook + '` parameters: `' + Plugins.deprecatedHooksParams[_hook] + '`, are being deprecated, ' + 'all plugins should now use the `middleware/cls` module instead of hook\'s arguments to get a reference to the `req`, `res` and/or `socket` object(s) (from which you can get the current `uid` if you need to.) ' - + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-req-res-socket-and-uid-within-any-plugin-hook') + + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-each-request-from-within-any-plugin-hook'); delete Plugins.deprecatedHooksParams[_hook]; } } From 8920c952817a0c53c0e86cee631a1b27008f3748 Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 17:07:24 -0400 Subject: [PATCH 17/62] reqFromSocket now support payload and event and uid --- src/socket.io/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index e417f6d250..5706c0b62c 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -209,12 +209,15 @@ var cls = require('../middleware/cls'); }; - Sockets.reqFromSocket = function (socket) { + Sockets.reqFromSocket = function (socket, payload, event) { var headers = socket.request.headers; var host = headers.host; var referer = headers.referer || ''; + var data = ((payload || {}).data || []); return { + uid: socket.uid, + body: {event: event || data[0], params: data[1], payload: payload}, ip: headers['x-forwarded-for'] || socket.ip, host: host, protocol: socket.request.connection.encrypted ? 'https' : 'http', From aac30cb5ec5aedc7f8d0edad82f9b8172cd6757c Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 17:17:54 -0400 Subject: [PATCH 18/62] hmm .. --- src/socket.io/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 5706c0b62c..42647fb9a9 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -217,7 +217,9 @@ var cls = require('../middleware/cls'); return { uid: socket.uid, - body: {event: event || data[0], params: data[1], payload: payload}, + params: data[1], + method: event, + body: payload, ip: headers['x-forwarded-for'] || socket.ip, host: host, protocol: socket.request.connection.encrypted ? 'https' : 'http', From 3dc63438debb9cf37e6cb22c1711cb1f1c23b2e2 Mon Sep 17 00:00:00 2001 From: Aziz Khoury Date: Fri, 15 Apr 2016 17:21:26 -0400 Subject: [PATCH 19/62] hmm-2 ... --- src/socket.io/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/index.js b/src/socket.io/index.js index 42647fb9a9..fc97f7cc87 100644 --- a/src/socket.io/index.js +++ b/src/socket.io/index.js @@ -218,7 +218,7 @@ var cls = require('../middleware/cls'); return { uid: socket.uid, params: data[1], - method: event, + method: event || data[0], body: payload, ip: headers['x-forwarded-for'] || socket.ip, host: host, From 2ce24f8ba921507ee3bea908687fae54d85c4722 Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Tue, 3 May 2016 09:02:29 -0400 Subject: [PATCH 20/62] Latest translations and fallbacks --- public/language/sv/category.json | 2 +- public/language/sv/email.json | 20 +++---- public/language/sv/error.json | 52 ++++++++--------- public/language/sv/global.json | 56 +++++++++--------- public/language/sv/groups.json | 24 ++++---- public/language/sv/modules.json | 34 +++++------ public/language/sv/notifications.json | 38 ++++++------- public/language/sv/pages.json | 48 ++++++++-------- public/language/sv/recent.json | 6 +- public/language/sv/register.json | 6 +- public/language/sv/reset_password.json | 6 +- public/language/sv/search.json | 2 +- public/language/sv/tags.json | 6 +- public/language/sv/topic.json | 48 ++++++++-------- public/language/sv/uploads.json | 4 +- public/language/sv/user.json | 78 +++++++++++++------------- public/language/sv/users.json | 4 +- 17 files changed, 217 insertions(+), 217 deletions(-) diff --git a/public/language/sv/category.json b/public/language/sv/category.json index 12204804ed..6736de62eb 100644 --- a/public/language/sv/category.json +++ b/public/language/sv/category.json @@ -6,7 +6,7 @@ "no_topics": "Det finns inga ämnen i denna kategori.
Varför skapar inte du ett ämne?", "browsing": "läser", "no_replies": "Ingen har svarat", - "no_new_posts": "Inga nya inlägg", + "no_new_posts": "Inga nya inlägg.", "share_this_category": "Dela den här kategorin", "watch": "Bevaka", "ignore": "Ignorera", diff --git a/public/language/sv/email.json b/public/language/sv/email.json index 5cf3bec72b..e51dfda9e5 100644 --- a/public/language/sv/email.json +++ b/public/language/sv/email.json @@ -5,9 +5,9 @@ "greeting_no_name": "Hej", "greeting_with_name": "Hej %1", "welcome.text1": "Tack för att du registerar dig på %1!", - "welcome.text2": "För att slutföra aktiveringen av ditt konto, behöver vi verifiera att du har tillgång till den epostadress du registrerade dig med.", + "welcome.text2": "För att slutföra aktiveringen av ditt konto, behöver vi verifiera att du har tillgång till den e-postadress du registrerade dig med.", "welcome.text3": "En administrator har accepterat din registreringsansökan. Du kan logga in med ditt användarnamn och lösenord nu.", - "welcome.cta": "Klicka här för att bekräfta din epostadress ", + "welcome.cta": "Klicka här för att bekräfta din e-postadress ", "invitation.text1": "%1 har bjudit in dig till %2", "invitation.ctr": "Klicka här för att skapa ditt konto.", "reset.text1": "Vi fick en förfrågan om att återställa ditt lösenord, möjligen för att du har glömt det. Om detta inte är fallet, så kan du bortse från det här epostmeddelandet. ", @@ -15,22 +15,22 @@ "reset.cta": "Klicka här för att återställa ditt lösenord", "reset.notify.subject": "Lösenordet ändrat", "reset.notify.text1": "Vi vill uppmärksamma dig på att ditt lösenord ändrades den %1", - "reset.notify.text2": "Om du inte godkänt det här så vänligen kontakta en admin snarast. ", + "reset.notify.text2": "Om du inte godkänt det här så vänligen kontakta en administratör snarast. ", "digest.notifications": "Du har olästa notiser från %1:", "digest.latest_topics": "Senaste ämnen från %1", "digest.cta": "Klicka här för att besöka %1", "digest.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", - "digest.no_topics": "Inga aktiva ämnen dom senaste %1", - "digest.day": "day", - "digest.week": "week", - "digest.month": "month", - "digest.subject": "Digest for %1", + "digest.no_topics": "Inga aktiva ämnen de senaste %1", + "digest.day": "dag", + "digest.week": "vecka", + "digest.month": "månad", + "digest.subject": "Sammanställt flöde för %1", "notif.chat.subject": "Nytt chatt-meddelande från %1", "notif.chat.cta": "Klicka här för att fortsätta konversationen", "notif.chat.unsub.info": "Denna chatt-notifikation skickades till dig på grund av dina inställningar för prenumerationer.", "notif.post.cta": "Klicka här för att läsa hela ämnet", "notif.post.unsub.info": "Det här meddelandet fick du på grund av dina inställningar för prenumeration. ", - "test.text1": "\nDet här är ett textmeddelande som verifierar att eposten är korrekt installerat för din NodeBB. ", - "unsub.cta": "Klicka här för att ändra dom inställningarna", + "test.text1": "\nDet här är ett testmeddelande som verifierar att e-posten är korrekt installerad för din NodeBB. ", + "unsub.cta": "Klicka här för att ändra de inställningarna", "closing": "Tack!" } \ No newline at end of file diff --git a/public/language/sv/error.json b/public/language/sv/error.json index cdc57388fd..64b1436f5e 100644 --- a/public/language/sv/error.json +++ b/public/language/sv/error.json @@ -14,7 +14,7 @@ "invalid-password": "Ogiltigt lösenord", "invalid-username-or-password": "Specificera både användarnamn och lösenord", "invalid-search-term": "Ogiltig sökterm", - "invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2", + "invalid-pagination-value": "Ogiltigt värde för siduppdelning. Värdet måste vara mellan %1 och %2", "username-taken": "Användarnamn upptaget", "email-taken": "Epostadress upptagen", "email-not-confirmed": "Din epostadress är ännu inte bekräftad. Klicka här för att bekräfta din epostadress.", @@ -27,7 +27,7 @@ "password-too-long": "Lösenordet är för långt", "user-banned": "Användare bannlyst", "user-too-new": "När du är ny medlem måste du vänta %1 sekund(er) innan du gör ditt första inlägg", - "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", + "blacklisted-ip": "Din IP-adress har blivit bannlyst från det här forumet. Om du tror att det beror på ett misstag, vad god kontakta en administratör. ", "no-category": "Kategorin finns inte", "no-topic": "Ämnet finns inte", "no-post": "Inlägget finns inte", @@ -44,15 +44,15 @@ "title-too-long": "Skriv en kortare rubrik. Rubriker kan inte innehålla mer än %1 tecken.", "too-many-posts": "Du måste vänta minst %1 sekund(er) mellan varje inlägg", "too-many-posts-newbie": "Som ny användare måste du vänta %1 sekund(er) mellan varje inlägg tills dess du har %2 förtroende", - "tag-too-short": "Fyll i ett längre märkord. Märkord måste vara minst %1 tecken långa", - "tag-too-long": "Fyll i ett kortare märkord. Märkord kan ej vara längre än %1 tecken långa", - "not-enough-tags": "Ej tillräckligt många märkord. Ämnen måste ha minst %1 märkord", - "too-many-tags": "För många märkord. Ämnen kan ej har mer än %1 märkord", + "tag-too-short": "Fyll i en längre tagg. Taggar måste vara minst %1 tecken långa", + "tag-too-long": "Fyll i en kortare tagg. Taggar kan ej vara längre än %1 tecken långa", + "not-enough-tags": "Otillräckligt antal taggar. Ämnen måste ha minst %1 taggar", + "too-many-tags": "För många taggar. Ämnen kan ej har mer än %1 tagg(ar)", "still-uploading": "Vänta medan uppladdningen slutförs.", - "file-too-big": "Den maximalt tillåtna filstorleken är %1 kB - ladda upp en mindre fil", - "guest-upload-disabled": "Guest uploading has been disabled", - "already-favourited": "You have already bookmarked this post", - "already-unfavourited": "You have already unbookmarked this post", + "file-too-big": "Den maximalt tillåtna filstorleken är %1 kB - var god ladda upp en mindre fil", + "guest-upload-disabled": "Uppladdningar av oregistrerade användare har inaktiverats", + "already-favourited": "Du har redan lagt till bokmärke för det här inlägget", + "already-unfavourited": "Du har redan tagit bort bokmärket för det här inlägget", "cant-ban-other-admins": "Du kan inte bannlysa andra administratörer.", "cant-remove-last-admin": "Du är den enda administratören. Lägg till en annan användare som administratör innan du tar bort dig själv.", "invalid-image-type": "Ogiltig bildtyp. Tillåtna typer är: % 1", @@ -61,8 +61,8 @@ "group-name-too-short": "Gruppnamnet är för kort", "group-already-exists": "Gruppen existerar redan", "group-name-change-not-allowed": "Gruppnamnet får inte ändras", - "group-already-member": "Already part of this group", - "group-not-member": "Not a member of this group", + "group-already-member": "Redan i denna grupp", + "group-not-member": "Ej medlem av denna grupp", "group-needs-owner": "Gruppen kräver minst en ägare", "group-already-invited": "Användaren har redan bjudits in", "group-already-requested": "Din medlemsskapsförfrågan har redan skickats", @@ -70,22 +70,22 @@ "post-already-restored": "Inlägget är redan återställt", "topic-already-deleted": "Ämnet är redan raderat", "topic-already-restored": "Ämnet är redan återställt", - "cant-purge-main-post": "Huvudinlägg kan ej rensas, ta bort ämnet istället", + "cant-purge-main-post": "Huvudinlägg kan ej rensas bort, ta bort ämnet istället", "topic-thumbnails-are-disabled": "Miniatyrbilder för ämnen är inaktiverat", "invalid-file": "Ogiltig fil", "uploads-are-disabled": "Uppladdningar är inaktiverat", "signature-too-long": "Din signatur kan inte vara längre än %1 tecken.", - "about-me-too-long": "Din om mig kan inte vara längre än %1 tecken.", + "about-me-too-long": "Din text om dig själv kan inte vara längre än %1 tecken.", "cant-chat-with-yourself": "Du kan inte chatta med dig själv.", - "chat-restricted": "Denna användaren har begränsat sina chatt-meddelanden. Användaren måste följa dig innan ni kan chatta med varann", - "chat-disabled": "Chat system disabled", + "chat-restricted": "Denna användaren har begränsat sina chatt-meddelanden. Användaren måste följa dig innan ni kan chatta med varandra", + "chat-disabled": "Chatt är inaktiverat", "too-many-messages": "Du har skickat för många meddelanden, var god vänta", "invalid-chat-message": "Ogiltigt chattmeddelande", "chat-message-too-long": "Chattmeddelande är för långt", - "cant-edit-chat-message": "You are not allowed to edit this message", - "cant-remove-last-user": "You can't remove the last user", - "cant-delete-chat-message": "You are not allowed to delete this message", - "already-voting-for-this-post": "You have already voted for this post.", + "cant-edit-chat-message": "Du har inte rättigheter att redigera det här meddelandet", + "cant-remove-last-user": "Du kan inte ta bort den sista användaren", + "cant-delete-chat-message": "Du har inte rättigheter att radera det här meddelandet", + "already-voting-for-this-post": "Du har redan röstat på det här inlägget.", "reputation-system-disabled": "Ryktessystemet är inaktiverat.", "downvoting-disabled": "Nedröstning är inaktiverat", "not-enough-reputation-to-downvote": "Du har inte tillräckligt förtroende för att rösta ner det här meddelandet", @@ -94,11 +94,11 @@ "reload-failed": "NodeBB stötte på problem med att ladda om: \"%1\". NodeBB kommer fortsätta servera den befintliga resurser till klienten, men du borde återställa det du gjorde alldeles innan du försökte ladda om.", "registration-error": "Registreringsfel", "parse-error": "Något gick fel vid tolkning av svar från servern", - "wrong-login-type-email": "Använd din e-post adress för att logga in", + "wrong-login-type-email": "Använd din e-postadress för att logga in", "wrong-login-type-username": "Använd ditt användarnamn för att logga in", - "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).", - "no-session-found": "No login session found!", - "not-in-room": "User not in room", - "no-users-in-room": "No users in this room", - "cant-kick-self": "You can't kick yourself from the group" + "invite-maximum-met": "Du har bjudit in det maximala antalet användare (%1 av %2)", + "no-session-found": "Ingen login-session hittades!", + "not-in-room": "Användaren finns inte i rummet", + "no-users-in-room": "Inga användare i det här rummet", + "cant-kick-self": "Du kan inte sparka ut dig själv ifrån gruppen" } \ No newline at end of file diff --git a/public/language/sv/global.json b/public/language/sv/global.json index 00a61776c7..d9c105b855 100644 --- a/public/language/sv/global.json +++ b/public/language/sv/global.json @@ -8,7 +8,7 @@ "404.title": "Sidan saknas", "404.message": "Du verkar ha ramlat in på en sida som inte finns. Återgå till första sidan.", "500.title": "Internt fel.", - "500.message": "Hoppsan! Verkar som att något gått snett!", + "500.message": "Hoppsan! Något verkar ha gått snett!", "register": "Registrera", "login": "Logga in", "please_log_in": "Var god logga in", @@ -25,7 +25,7 @@ "header.categories": "Kategorier", "header.recent": "Senaste", "header.unread": "Olästa", - "header.tags": "Märkningar", + "header.tags": "Taggar", "header.popular": "Populära", "header.users": "Användare", "header.groups": "Grupper", @@ -33,15 +33,15 @@ "header.notifications": "Notiser", "header.search": "Sök", "header.profile": "Profil", - "header.navigation": "Navigation", - "notifications.loading": "Laddar Notiser", - "chats.loading": "Laddar Chattar", - "motd.welcome": "Välkommen till NodeBB, framtidens diskussions-plattform.", + "header.navigation": "Navigering", + "notifications.loading": "Laddar notiser", + "chats.loading": "Laddar chattar", + "motd.welcome": "Välkommen till NodeBB, framtidens diskussionsplattform.", "previouspage": "Föregående sida", "nextpage": "Nästa sida", - "alert.success": "Success", + "alert.success": "Lyckat", "alert.error": "Fel", - "alert.banned": "Bannad", + "alert.banned": "Bannlyst", "alert.banned.message": "Du har blivit bannlyst och kommer nu att loggas ut. ", "alert.unfollow": "Du följer inte längre %1!", "alert.follow": "Du följer nu %1!", @@ -49,46 +49,46 @@ "users": "Användare", "topics": "Ämnen", "posts": "Inlägg", - "best": "Best", - "upvoted": "Upvoted", - "downvoted": "Downvoted", + "best": "Bästa", + "upvoted": "Uppröstad", + "downvoted": "Nedröstad", "views": "Visningar", "reputation": "Rykte", "read_more": "läs mer", "more": "Mer", "posted_ago_by_guest": "inskickad %1 av anonym", "posted_ago_by": "inskickad %1 av %2", - "posted_ago": "inskickad %1", - "posted_in": "posted in %1", - "posted_in_by": "posted in %1 by %2", + "posted_ago": "postat %1", + "posted_in": "postat i %1", + "posted_in_by": "postat i %1 av %2", "posted_in_ago": "inskickad i %1 %2", - "posted_in_ago_by": "inskickad i %1 %2 av %3", - "user_posted_ago": "%1 skickades in %2", - "guest_posted_ago": "Anonym skickade in %1", - "last_edited_by": "last edited by %1", + "posted_in_ago_by": "postat i %1 %2 av %3", + "user_posted_ago": "%1 postades %2", + "guest_posted_ago": "Anonym postade %1", + "last_edited_by": "Senaste redigerad av %1", "norecentposts": "Inga nya inlägg", "norecenttopics": "Inga nya ämnen", - "recentposts": "Senaste ämnena", + "recentposts": "Senaste inläggen", "recentips": "Nyligen inloggade IPn", "away": "Borta", - "dnd": "Do not disturb", + "dnd": "Stör inte", "invisible": "Osynlig", "offline": "Offline", - "email": "Epost", + "email": "E-post", "language": "Språk", "guest": "Anonym", "guests": "Anonyma", - "updated.title": "Forum uppdaterades", + "updated.title": "Forumet uppdaterades", "updated.message": "Det här forumet har nu uppdaterats till senaste versionen. Klicka här för att ladda om sidan.", "privacy": "Integritet", "follow": "Följ", "unfollow": "Sluta följ", - "delete_all": "Ta bort Alla", - "map": "Map", - "sessions": "Login Sessions", - "ip_address": "IP Address", - "enter_page_number": "Enter page number", + "delete_all": "Ta bort alla", + "map": "Karta", + "sessions": "Login-sessioner", + "ip_address": "IP-adress", + "enter_page_number": "Skriv in sidnummer", "upload_file": "Ladda upp en fil", "upload": "Ladda upp", - "allowed-file-types": "Allowed file types are %1" + "allowed-file-types": "Tillåtna filtyper är %1" } \ No newline at end of file diff --git a/public/language/sv/groups.json b/public/language/sv/groups.json index 2fa0d40823..a88962291c 100644 --- a/public/language/sv/groups.json +++ b/public/language/sv/groups.json @@ -7,30 +7,30 @@ "pending.accept": "Acceptera", "pending.reject": "Neka", "pending.accept_all": "Acceptera alla", - "pending.reject_all": "Neka alla", + "pending.reject_all": "Avvisa alla", "pending.none": "Det finns inga väntande medlemmar just nu", "invited.none": "Det finns inga inbjudna medlemmar just nu", "invited.uninvite": "Dra tillbaka inbjudan", "invited.search": "Sök efter en användare att lägga till i denna grupp", - "invited.notification_title": "You have been invited to join %1", - "request.notification_title": "Group Membership Request from %1", - "request.notification_text": "%1 has requested to become a member of %2", + "invited.notification_title": "Du har blivit inbjuden att bli medlem i %1", + "request.notification_title": "Förfrågan om gruppmedlemskap från %1", + "request.notification_text": "%1 har skickat en förfrågan om medlemskap i %2", "cover-save": "Spara", "cover-saving": "Sparar", "details.title": "Detaljer för gruppen ", - "details.members": "Medlemmar", + "details.members": "Medlemslista", "details.pending": "Väntande medlemmar", "details.invited": "Inbjudna medlemmar", "details.has_no_posts": "Den här gruppens medlemmar har inte skrivit några inlägg.", "details.latest_posts": "Senaste inlägg", "details.private": "Privat", - "details.disableJoinRequests": "Disable join requests", - "details.grant": "Ge/Ta ifrån ägarskap", + "details.disableJoinRequests": "Inaktivera förfrågningar om att gå med", + "details.grant": "Tilldela/Dra tillbaka ägarskap", "details.kick": "Sparka ut", "details.owner_options": "Gruppadministration", "details.group_name": "Gruppnamn", "details.member_count": "Medlemsantal", - "details.creation_date": "Skapatdatum", + "details.creation_date": "Skapardatum", "details.description": "Beskrivning", "details.badge_preview": "Förhandsgranskning av märke", "details.change_icon": "Byt ikon", @@ -41,14 +41,14 @@ "details.hidden": "Dold", "details.hidden_help": "Om aktiverat kommer gruppen inte synas i grupplistan och användare måste bli inbjudna manuellt", "details.delete_group": "Ta bort grupp", - "details.private_system_help": "Private groups is disabled at system level, this option does not do anything", - "event.updated": "Gruppdetaljerna har uppdaterats", + "details.private_system_help": "Privata grupper är ej tillgängligt. Den här inställningen har ingen effekt.", + "event.updated": "Gruppinformationen har uppdaterats", "event.deleted": "Gruppen \"%1\" har tagits bort", "membership.accept-invitation": "Acceptera inbjudan", "membership.invitation-pending": "Inbjudan väntar på svar", "membership.join-group": "Gå med i grupp", "membership.leave-group": "Lämna grupp", "membership.reject": "Neka", - "new-group.group_name": "Group Name:", - "upload-group-cover": "Upload group cover" + "new-group.group_name": "Gruppnamn:", + "upload-group-cover": "Ladda upp omslagsbild för grupp" } \ No newline at end of file diff --git a/public/language/sv/modules.json b/public/language/sv/modules.json index 19bc422e9b..25349f9baa 100644 --- a/public/language/sv/modules.json +++ b/public/language/sv/modules.json @@ -1,38 +1,38 @@ { "chat.chatting_with": "Chatta med ", - "chat.placeholder": "Skriv chatmeddelande här och tryck sen enter för att skicka ", + "chat.placeholder": "Skriv chattmeddelande här och tryck sen Enter för att skicka ", "chat.send": "Skicka", "chat.no_active": "Du har inte några aktiva chattar.", "chat.user_typing": "%1 skriver ...", "chat.user_has_messaged_you": "%1 har skickat ett medelande till dig.", "chat.see_all": "Se alla chattar", - "chat.mark_all_read": "Mark all chats read", - "chat.no-messages": "Välj mottagare för att visa historik för chatmeddelande", - "chat.no-users-in-room": "No users in this room", + "chat.mark_all_read": "Markera alla chattar som lästa", + "chat.no-messages": "Välj mottagare för att visa historik för chattmeddelande", + "chat.no-users-in-room": "Inga användare i detta rum", "chat.recent-chats": "Senaste chattarna", "chat.contacts": "Kontakter ", "chat.message-history": "Historik för meddelande", "chat.pop-out": "Utskjutande chatt", "chat.maximize": "Maximera", - "chat.seven_days": "7 Dagar", - "chat.thirty_days": "30 Dagar", - "chat.three_months": "3 Månader", - "chat.delete_message_confirm": "Are you sure you wish to delete this message?", - "chat.roomname": "Chat Room %1", - "chat.add-users-to-room": "Add users to room", + "chat.seven_days": "7 dagar", + "chat.thirty_days": "30 dagar", + "chat.three_months": "3 månader", + "chat.delete_message_confirm": "Är du säker på att du vill radera det här meddelandet?", + "chat.roomname": "Chattrum %1", + "chat.add-users-to-room": "Addera användare till rum", "composer.compose": "Komponera", "composer.show_preview": "Visa förhandsgranskning", "composer.hide_preview": "Dölj förhandsgranskning", "composer.user_said_in": "%1 sa i %2:", "composer.user_said": "%1 sa:", - "composer.discard": "Är du säker på att du vill förkasta det här inlägget?", + "composer.discard": "Är du säker på att du vill ta bort det här inlägget?", "composer.submit_and_lock": "Skicka och lås", "composer.toggle_dropdown": "Visa/Dölj dropdown", - "composer.uploading": "Uploading %1", + "composer.uploading": "Laddar upp %1", "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm", - "cover.dragging_title": "Cover Photo Positioning", - "cover.dragging_message": "Drag the cover photo to the desired position and click \"Save\"", - "cover.saved": "Cover photo image and position saved" + "bootbox.cancel": "Avbryt", + "bootbox.confirm": "Bekräfta", + "cover.dragging_title": "Positionering av omslagsbild", + "cover.dragging_message": "Dra omslagsbilden till önskad position och tryck \"Spara\"", + "cover.saved": "Omslagsbilden sparad" } \ No newline at end of file diff --git a/public/language/sv/notifications.json b/public/language/sv/notifications.json index 0eeb2e9509..df0b331ac3 100644 --- a/public/language/sv/notifications.json +++ b/public/language/sv/notifications.json @@ -5,34 +5,34 @@ "mark_all_read": "Markera alla notiser som lästa", "back_to_home": "Tillbaka till %1", "outgoing_link": "Utgående länk", - "outgoing_link_message": "You are now leaving %1", + "outgoing_link_message": "Du lämnar nu %1", "continue_to": "Fortsätt till %1", "return_to": "Återgå till %1", "new_notification": "Ny notis", "you_have_unread_notifications": "Du har olästa notiser.", "new_message_from": "Nytt medelande från %1", "upvoted_your_post_in": "%1 har röstat upp ditt inlägg i %2", - "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.", - "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.", - "moved_your_post": "%1 has moved your post to %2", - "moved_your_topic": "%1 has moved %2", - "favourited_your_post_in": "%1 has bookmarked your post in %2.", - "favourited_your_post_in_dual": "%1 and %2 have bookmarked your post in %3.", - "favourited_your_post_in_multiple": "%1 and %2 others have bookmarked your post in %3.", + "upvoted_your_post_in_dual": "%1 och %2 har röstat upp ditt inlägg i %3.", + "upvoted_your_post_in_multiple": "%1 och %2 andra har röstat upp ditt inlägg i %3.", + "moved_your_post": "%1 har flyttat ditt inlägg till %2", + "moved_your_topic": "%1 har flyttat %2", + "favourited_your_post_in": "%1 har lagt till bokmärke på ditt inlägg i %2.", + "favourited_your_post_in_dual": "%1 och %2 har lagt till bokmärke på ditt inlägg i %3.", + "favourited_your_post_in_multiple": "%1 och %2 andra har lagt till bokmärke på ditt inlägg i %3.", "user_flagged_post_in": "%1 flaggade ett inlägg i %2", - "user_flagged_post_in_dual": "%1 and %2 flagged a post in %3", - "user_flagged_post_in_multiple": "%1 and %2 others flagged a post in %3", + "user_flagged_post_in_dual": "%1 och %2 rapporterade ett inlägg i %3", + "user_flagged_post_in_multiple": "%1 och %2 andra rapporterade ett inlägg i %3", "user_posted_to": "%1 har skrivit ett svar på: %2", - "user_posted_to_dual": "%1 and %2 have posted replies to: %3", - "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", + "user_posted_to_dual": "%1 och %2 har svarat på: %3", + "user_posted_to_multiple": "%1 och %2 andra har svarat på: %3", "user_posted_topic": "%1 har skapat ett nytt ämne: %2", "user_started_following_you": "%1 började följa dig.", - "user_started_following_you_dual": "%1 and %2 started following you.", - "user_started_following_you_multiple": "%1 and %2 others started following you.", + "user_started_following_you_dual": "%1 och %2 började följa dig.", + "user_started_following_you_multiple": "%1 och %2 andra började följa dig.", "new_register": "%1 skickade en registreringsförfrågan.", - "new_register_multiple": "There are %1 registration requests awaiting review.", - "email-confirmed": "Epost bekräftad", - "email-confirmed-message": "Tack för att du bekräftat din epostadress. Ditt konto är nu fullt ut aktiverat.", - "email-confirm-error-message": "Det uppstod ett fel med att bekräfta din epostadress. Kanske var koden ogiltig eller har gått ut.", - "email-confirm-sent": "Bekräftelseepost skickat." + "new_register_multiple": "Det finns %1 förfrågningar om registrering som inväntar granskning.", + "email-confirmed": "E-post bekräftad", + "email-confirmed-message": "Tack för att du bekräftat din e-postadress. Ditt konto är nu fullt ut aktiverat.", + "email-confirm-error-message": "Det uppstod ett problem med bekräftelsen av din e-postadress. Kanske var koden felaktig eller ogiltig.", + "email-confirm-sent": "Bekräftelsemeddelande skickat." } \ No newline at end of file diff --git a/public/language/sv/pages.json b/public/language/sv/pages.json index 7c4baee1e2..a2ad0b69b1 100644 --- a/public/language/sv/pages.json +++ b/public/language/sv/pages.json @@ -4,43 +4,43 @@ "popular-day": "Populära ämnen idag", "popular-week": "Populära ämnen den här veckan", "popular-month": "Populära ämnen denna månad", - "popular-alltime": "All time popular topics", + "popular-alltime": "Populäraste ämnena genom tiderna", "recent": "Senaste ämnena", "flagged-posts": "Flaggade inlägg", "users/online": "Användare online", "users/latest": "Senaste Användare", "users/sort-posts": "Användare med flest inlägg", - "users/sort-reputation": "Users with the most reputation", - "users/banned": "Banned Users", + "users/sort-reputation": "Användare med bäst rykte", + "users/banned": "Bannlysta användare", "users/search": "Användar Sök", "notifications": "Notiser", "tags": "Etiketter", "tag": "Ämnen märkta med \"%1\"", - "register": "Register an account", + "register": "Registrera ett konto", "login": "Logga in på ditt konto", "reset": "Återställ lösenord", "categories": "Kategorier", "groups": "Grupper", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", - "account/edit/password": "Editing password of \"%1\"", - "account/edit/username": "Editing username of \"%1\"", - "account/edit/email": "Editing email of \"%1\"", - "account/following": "People %1 follows", - "account/followers": "People who follow %1", - "account/posts": "Posts made by %1", - "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", - "account/favourites": "%1's Bookmarked Posts", + "group": "%1 grupp", + "chats": "Chattar", + "chat": "Chattar med %1", + "account/edit": "Redigerar \"%1\"", + "account/edit/password": "Redigerar lösenord för \"%1\"", + "account/edit/username": "Redigerar användarnamn för \"%1\"", + "account/edit/email": "Redigerar e-postadress för \"%1\"", + "account/following": "Användare som %1 följer", + "account/followers": "Användare som följer %1", + "account/posts": "Inlägg skapade av %1", + "account/topics": "Ämnen skapade av %1 ", + "account/groups": "%1's grupper", + "account/favourites": "%1's bokmärken", "account/settings": "Avnändarinställningar", - "account/watched": "Topics watched by %1", - "account/upvoted": "Posts upvoted by %1", - "account/downvoted": "Posts downvoted by %1", - "account/best": "Best posts made by %1", - "confirm": "Email Confirmed", + "account/watched": "Ämnen som bevakas av %1", + "account/upvoted": "Inlägg som röstats upp av %1", + "account/downvoted": "Inlägg som röstats ned av %1", + "account/best": "Bästa inläggen skapade av %1", + "confirm": "E-postadress bekräftad", "maintenance.text": "%1 genomgår underhåll just nu. Vänligen kom tillbaka lite senare.", - "maintenance.messageIntro": "Ytterligare så lämnade administratören detta meddelande:", - "throttled.text": "%1 is currently unavailable due to excessive load. Please come back another time." + "maintenance.messageIntro": "Utöver det så lämnade administratören följande meddelande:", + "throttled.text": "%1 ligger tillfälligt nere på grund av överbelastning. Var god återkom senare. " } \ No newline at end of file diff --git a/public/language/sv/recent.json b/public/language/sv/recent.json index 46d7531d3c..0e75be8355 100644 --- a/public/language/sv/recent.json +++ b/public/language/sv/recent.json @@ -6,12 +6,12 @@ "year": "År", "alltime": "Alltid", "no_recent_topics": "Det finns inga olästa ämnen.", - "no_popular_topics": "Det finns inga populära ämnen", - "there-is-a-new-topic": "Det finns ett nytt ämne", + "no_popular_topics": "Det finns inga populära ämnen.", + "there-is-a-new-topic": "Det finns ett nytt ämne.", "there-is-a-new-topic-and-a-new-post": "Det finns ett nytt ämne och ett nytt inlägg.", "there-is-a-new-topic-and-new-posts": "Det finns ett nytt ämne och %1 nya inlägg.", "there-are-new-topics": "Det finns %1 nya ämnen.", - "there-are-new-topics-and-a-new-post": "Det finns %1 nya ämnen och ett nytt inlägg..", + "there-are-new-topics-and-a-new-post": "Det finns %1 nya ämnen och ett nytt inlägg.", "there-are-new-topics-and-new-posts": "Det finns %1 nya ämnen och %2 nya inlägg.", "there-is-a-new-post": "Det finns ett nytt inlägg.", "there-are-new-posts": "Det finns %1 nya inlägg.", diff --git a/public/language/sv/register.json b/public/language/sv/register.json index 6f1aa3764b..1ada86d0ba 100644 --- a/public/language/sv/register.json +++ b/public/language/sv/register.json @@ -1,10 +1,10 @@ { "register": "Registrera", - "help.email": "Som standard, är din epost-adress dold för allmänheten.", + "help.email": "Som standard, är din e-postadress dold för allmänheten.", "help.username_restrictions": "Ett unikt användarnamn mellan %1 och %2 bokstäver. Andra kan nämna dig med @användarnamn.", "help.minimum_password_length": "Ditt lösenord måste vara minst %1 bokstäver.", - "email_address": "Epost-adress", - "email_address_placeholder": "Ange Epost-adress", + "email_address": "E-postadress", + "email_address_placeholder": "Ange E-postadress", "username": "Användarnamn", "username_placeholder": "Ange användarnamn", "password": "Lösenord", diff --git a/public/language/sv/reset_password.json b/public/language/sv/reset_password.json index 9f79207d43..a6ee1793e1 100644 --- a/public/language/sv/reset_password.json +++ b/public/language/sv/reset_password.json @@ -7,10 +7,10 @@ "wrong_reset_code.message": "Den mottagna återställningskoden var felaktig. Var god försök igen, eller begär en ny återställningskod.", "new_password": "Nytt lösenord", "repeat_password": "Bekräfta lösenord", - "enter_email": "Var god fyll i din epost-adress så får du snart en epost med instruktioner hur du återsätller ditt konto.", - "enter_email_address": "Skriv in epostadress", + "enter_email": "Var god fyll i din e-postadress så skickas ett e-postmeddelande med instruktioner hur du återställer ditt konto.", + "enter_email_address": "Skriv in e-postadress", "password_reset_sent": "Lösenordsåterställning skickad", - "invalid_email": "Felaktig epost / Epost finns inte!", + "invalid_email": "Felaktig e-post / E-post finns inte!", "password_too_short": "Lösenordet är för kort, var god välj ett annat lösenord.", "passwords_do_not_match": "De två lösenorden du har fyllt i matchar ej varandra.", "password_expired": "Ditt lösenord har gått ut, var god välj ett nytt lösenord." diff --git a/public/language/sv/search.json b/public/language/sv/search.json index eeb5a36feb..3682c8bef5 100644 --- a/public/language/sv/search.json +++ b/public/language/sv/search.json @@ -4,7 +4,7 @@ "advanced-search": "Avancerad sökning", "in": "i", "titles": "Ämnen", - "titles-posts": "Ämnen och Inlägg", + "titles-posts": "Ämnen och inlägg", "posted-by": "Skapad av", "in-categories": "I kategorier", "search-child-categories": "Sök i underkategorier", diff --git a/public/language/sv/tags.json b/public/language/sv/tags.json index 341c5aad2b..cc8a7e02dc 100644 --- a/public/language/sv/tags.json +++ b/public/language/sv/tags.json @@ -1,7 +1,7 @@ { - "no_tag_topics": "Det finns inga ämnen med detta märkord.", + "no_tag_topics": "Det finns inga ämnen med denna tagg.", "tags": "Taggar", - "enter_tags_here": "Fyll i märkord på mellan %1 och %2 tecken här.", + "enter_tags_here": "Fyll i taggar på %1 till %2 tecken här.", "enter_tags_here_short": "Ange taggar...", - "no_tags": "Det finns inga märkord ännu." + "no_tags": "Det finns inga taggar ännu." } \ No newline at end of file diff --git a/public/language/sv/topic.json b/public/language/sv/topic.json index 8f1e9c94b6..603390ac0e 100644 --- a/public/language/sv/topic.json +++ b/public/language/sv/topic.json @@ -13,7 +13,7 @@ "notify_me": "Få notiser om nya svar i detta ämne", "quote": "Citera", "reply": "Svara", - "reply-as-topic": "Reply as topic", + "reply-as-topic": "Svara som ämne", "guest-login-reply": "Logga in för att posta", "edit": "Ändra", "delete": "Ta bort", @@ -26,7 +26,7 @@ "tools": "Verktyg", "flag": "Rapportera", "locked": "Låst", - "bookmark_instructions": "Click here to return to the last read post in this thread.", + "bookmark_instructions": "Klicka här för att återgå till senast lästa inlägg i detta ämne.", "flag_title": "Rapportera detta inlägg för granskning", "flag_success": "Det här inlägget har flaggats för moderering.", "deleted_message": "Det här ämnet har raderats. Endast användare med ämneshanterings-privilegier kan se det.", @@ -34,8 +34,8 @@ "not_following_topic.message": "Du kommer inte längre få notiser från detta ämne.", "login_to_subscribe": "Var god registrera eller logga in för att kunna prenumerera på detta ämne.", "markAsUnreadForAll.success": "Ämne markerat som oläst av alla.", - "mark_unread": "Mark unread", - "mark_unread.success": "Topic marked as unread.", + "mark_unread": "Markera som oläst", + "mark_unread.success": "Ämne markerat som oläst.", "watch": "Bevaka", "unwatch": "Sluta bevaka", "watch.title": "Få notis om nya svar till det här ämnet", @@ -43,46 +43,46 @@ "share_this_post": "Dela detta inlägg", "thread_tools.title": "Ämnesverktyg", "thread_tools.markAsUnreadForAll": "Markera som oläst", - "thread_tools.pin": "Fäst ämne", + "thread_tools.pin": "Nåla fast ämne", "thread_tools.unpin": "Lösgör ämne", "thread_tools.lock": "Lås ämne", - "thread_tools.unlock": "Öppna upp ämne", + "thread_tools.unlock": "Lås upp ämne", "thread_tools.move": "Flytta ämne", - "thread_tools.move_all": "Flytta alla.", + "thread_tools.move_all": "Flytta alla", "thread_tools.fork": "Grena ämne", "thread_tools.delete": "Ta bort ämne", - "thread_tools.delete-posts": "Delete Posts", + "thread_tools.delete-posts": "Radera inlägg", "thread_tools.delete_confirm": "Är du säker på att du vill ta bort det här ämnet?", "thread_tools.restore": "Återställ ämne", "thread_tools.restore_confirm": "Är du säker på att du vill återställa det här ämnet?", - "thread_tools.purge": "Rensa ämne", - "thread_tools.purge_confirm": "Är du säker att du vill rensa ut det här ämnet?", + "thread_tools.purge": "Rensa bort ämne", + "thread_tools.purge_confirm": "Är du säker att du vill rensa bort det här ämnet?", "topic_move_success": "Det här ämnet har flyttats till %1", "post_delete_confirm": "Är du säker på att du vill ta bort det här inlägget?", "post_restore_confirm": "Är du säker på att du vill återställa det här inlägget?", - "post_purge_confirm": "Är du säker att du vill rensa ut det här inlägget?", + "post_purge_confirm": "Är du säker att du vill rensa bort det här inlägget?", "load_categories": "Laddar kategorier", "disabled_categories_note": "Inaktiverade kategorier är utgråade", "confirm_move": "Flytta", "confirm_fork": "Grena", - "favourite": "Bookmark", - "favourites": "Bookmarks", - "favourites.has_no_favourites": "You haven't bookmarked any posts yet.", + "favourite": "Bokmärke", + "favourites": "Bokmärken", + "favourites.has_no_favourites": "Du har inte lagt till bokmärke på något inlägg än.", "loading_more_posts": "Laddar fler inlägg", "move_topic": "Flytta ämne", "move_topics": "Flytta ämnen", "move_post": "Flytta inlägg", "post_moved": "Inlägget flyttades.", "fork_topic": "Grena ämne", - "topic_will_be_moved_to": "Detta ämne kommer bli flytta till kategori", + "topic_will_be_moved_to": "Detta ämne kommer att flyttas till kategorin", "fork_topic_instruction": "Klicka på de inlägg du vill grena", "fork_no_pids": "Inga inlägg valda!", "fork_success": "Ämnet har blivit förgrenat. Klicka här för att gå till det förgrenade ämnet.", - "delete_posts_instruction": "Click the posts you want to delete/purge", + "delete_posts_instruction": "Klicka på inläggen du vill radera/rensa bort", "composer.title_placeholder": "Skriv in ämnets titel här...", "composer.handle_placeholder": "Namn", "composer.discard": "Avbryt", - "composer.submit": "Skicka", + "composer.submit": "Posta inlägg", "composer.replying_to": "Svarar till %1", "composer.new_topic": "Nytt ämne", "composer.uploading": "laddar upp...", @@ -92,21 +92,21 @@ "composer.thumb_file_label": "Eller ladda upp en fil", "composer.thumb_remove": "Töm fält", "composer.drag_and_drop_images": "Dra och släpp bilder här", - "more_users_and_guests": "%1 fler användare() och %2 gäst(er)", - "more_users": "%1 fler användare()", + "more_users_and_guests": "%1 fler användare och %2 gäst(er)", + "more_users": "%1 fler användare", "more_guests": "1% fler gäst(er)", "users_and_others": "%1 och %2 andra", "sort_by": "Sortera på", "oldest_to_newest": "Äldst till nyaste", "newest_to_oldest": "Nyaste till äldst", - "most_votes": "Mest röster", - "most_posts": "Felst inlägg", + "most_votes": "Flest röster", + "most_posts": "Flest inlägg", "stale.title": "Skapa nytt ämne istället?", - "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", + "stale.warning": "Ämnet du svarar på är ganska gammalt. Vill du skapa ett nytt ämne istället och inkludera en referens till det här ämnet i ditt inlägg?", "stale.create": "Skapa nytt ämne", "stale.reply_anyway": "Svara på ämnet ändå", "link_back": "Re: [%1](%2)", "spam": "Spam", - "offensive": "Offensive", - "custom-flag-reason": "Enter a flagging reason" + "offensive": "Kränkande", + "custom-flag-reason": "Ange skälet för rapporteringen" } \ No newline at end of file diff --git a/public/language/sv/uploads.json b/public/language/sv/uploads.json index 3c116a397f..4546acd888 100644 --- a/public/language/sv/uploads.json +++ b/public/language/sv/uploads.json @@ -1,6 +1,6 @@ { "uploading-file": "Laddar upp filen...", "select-file-to-upload": "Välj en fil att ladda upp!", - "upload-success": "File uploaded successfully!", - "maximum-file-size": "Maximum %1 kb" + "upload-success": "Filen laddades upp!", + "maximum-file-size": "Maximalt %1 kb" } \ No newline at end of file diff --git a/public/language/sv/user.json b/public/language/sv/user.json index cb7c26150e..008e790f70 100644 --- a/public/language/sv/user.json +++ b/public/language/sv/user.json @@ -1,11 +1,11 @@ { - "banned": "Bannad", + "banned": "Bannlyst", "offline": "Offline", "username": "Användarnamn", "joindate": "Gick med", "postcount": "Antal inlägg", - "email": "Epost", - "confirm_email": "Bekräfta epostadress ", + "email": "E-post", + "confirm_email": "Bekräfta e-postadress ", "ban_account": "Bannlys konto", "ban_account_confirm": "Vill du verkligen bannlysa den här användaren?", "unban_account": "Ta bort bannlysning", @@ -22,7 +22,7 @@ "profile": "Profil", "profile_views": "Profil-visningar", "reputation": "Rykte", - "favourites": "Bookmarks", + "favourites": "Bokmärken", "watched": "Bevakad", "followers": "Följare", "following": "Följer", @@ -30,20 +30,20 @@ "signature": "Signatur", "birthday": "Födelsedag", "chat": "Chatta", - "chat_with": "Chat with %1", + "chat_with": "Chatta med %1", "follow": "Följ", "unfollow": "Sluta följ", "more": "Mer", "profile_update_success": "Profilen uppdaterades.", "change_picture": "Ändra bild", - "change_username": "Change Username", - "change_email": "Change Email", + "change_username": "Ändra användarnamn", + "change_email": "Ändra e-postadress", "edit": "Ändra", - "edit-profile": "Edit Profile", + "edit-profile": "Redigera profil", "default_picture": "Standard-ikon", "uploaded_picture": "Uppladdad bild", "upload_new_picture": "Ladda upp ny bild", - "upload_new_picture_from_url": "Ladda upp ny bild från länk", + "upload_new_picture_from_url": "Ladda upp ny bild via länk", "current_password": "Nuvarande lösenord", "change_password": "Ändra lösenord", "change_password_error": "Ogiltigt lösenord.", @@ -56,56 +56,56 @@ "password": "Lösenord", "username_taken_workaround": "Användarnamnet är redan upptaget, så vi förändrade det lite. Du kallas nu för %1", "password_same_as_username": "Ditt lösenord är samma som ditt användarnamn, välj ett annat lösenord.", - "password_same_as_email": "Your password is the same as your email, please select another password.", + "password_same_as_email": "Ditt lösenord är detsamma som din e-postadress. Var god välj ett annat lösenord.", "upload_picture": "Ladda upp bild", "upload_a_picture": "Ladda upp en bild", "remove_uploaded_picture": "Ta bort uppladdad bild", - "upload_cover_picture": "Upload cover picture", + "upload_cover_picture": "Ladda upp omslagsbild", "settings": "Inställningar", - "show_email": "Visa min epost", - "show_fullname": "Visa Fullständigt Namn", + "show_email": "Visa min e-postadress", + "show_fullname": "Visa fullständigt namn", "restrict_chats": "Tillåt endast chatt-meddelanden från användare som jag följer", "digest_label": "Prenumerera på sammanställt flöde", - "digest_description": "Prenumerera på epostuppdateringar för det här forumet (notiser och ämnen) med en viss regelbundenhet", + "digest_description": "Prenumerera på e-postuppdateringar för det här forumet (notiser och ämnen) med en viss regelbundenhet", "digest_off": "Avslagen", - "digest_daily": "Daligen", + "digest_daily": "Dagligen", "digest_weekly": "Veckovis", "digest_monthly": "Månadsvis", - "send_chat_notifications": "Skicka ett epostmeddelande om nya chatt-meddelanden tas emot när jag inte är online.", - "send_post_notifications": "Skicka ett epost när svar kommit på ämnen jag prenumererar på till", + "send_chat_notifications": "Skicka ett e-postmeddelande om nya chatt-meddelanden tas emot när jag inte är online.", + "send_post_notifications": "Skicka ett e-postmeddelande när svar tillkommit på ämnen jag prenumererar på", "settings-require-reload": "Vissa inställningar som ändrades kräver att sidan laddas om. Klicka här för att ladda om sidan.", "has_no_follower": "Denna användare har inga följare :(", "follows_no_one": "Denna användare följer ingen :(", - "has_no_posts": "Användaren har inte skrivit några inlägg ännu", - "has_no_topics": "Användaren har inte skrivit några ämnen ännu", - "has_no_watched_topics": "Användaren har inte bevakat några ämnen ännu", - "has_no_upvoted_posts": "This user hasn't upvoted any posts yet.", - "has_no_downvoted_posts": "This user hasn't downvoted any posts yet.", - "has_no_voted_posts": "This user has no voted posts", - "email_hidden": "Epost dold", + "has_no_posts": "Användaren har inte skrivit några inlägg ännu.", + "has_no_topics": "Användaren har inte postat några ämnen ännu.", + "has_no_watched_topics": "Användaren har inte bevakat några ämnen ännu.", + "has_no_upvoted_posts": "Den här användaren har inte röstat upp några inlägg än.", + "has_no_downvoted_posts": "Den här användaren har inte röstat ned några inlägg än.", + "has_no_voted_posts": "Den här användaren har inga inlägg med röster", + "email_hidden": "E-post dold", "hidden": "dold", "paginate_description": "Gör så att ämnen och inlägg visas som sidor istället för oändlig skroll", "topics_per_page": "Ämnen per sida", "posts_per_page": "Inlägg per sida", "notification_sounds": "Spela ett ljud när du får en notis", "browsing": "Inställning för bläddring", - "open_links_in_new_tab": "Öppna utgående länkar på ny flik", - "enable_topic_searching": "Aktivera Sökning Inom Ämne", + "open_links_in_new_tab": "Öppna utgående länkar i ny flik", + "enable_topic_searching": "Aktivera sökning inom ämne", "topic_search_help": "Om aktiverat kommer sökning inom ämne överskrida webbläsarens vanliga funktionen för sökning bland sidor och tillåta dig att söka genom hela ämnet istället för det som endast visas på skärmen.", - "delay_image_loading": "Delay Image Loading", - "image_load_delay_help": "If enabled, images in topics will not load until they are scrolled into view", - "scroll_to_my_post": "After posting a reply, show the new post", - "follow_topics_you_reply_to": "Följ ämnen som du svarat på", - "follow_topics_you_create": "Följ ämnen du skapat", - "grouptitle": "Group Title", + "delay_image_loading": "Fördröj inladdning av bilder", + "image_load_delay_help": "Aktivera för att hindra bilder ifrån att ladda in, innan de skrollats fram på skärmen. ", + "scroll_to_my_post": "Visa det nya inlägget när ett svar har postats", + "follow_topics_you_reply_to": "Följ ämnen som du svarar på", + "follow_topics_you_create": "Följ ämnen du skapar", + "grouptitle": "Grupptitel", "no-group-title": "Ingen titel på gruppen", "select-skin": "Välj ett Skin", - "select-homepage": "Select a Homepage", - "homepage": "Homepage", - "homepage_description": "Select a page to use as the forum homepage or 'None' to use the default homepage.", - "custom_route": "Custom Homepage Route", - "custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\", or \"popular\")", + "select-homepage": "Välj en startsida", + "homepage": "Startsida", + "homepage_description": "Välj en sida som ska användas som forumets startsida eller 'Ingen' för att använda standardstartsidan.", + "custom_route": "Sökväg till egen startsida", + "custom_route_help": "Skriv in ett sökvägsnamn här, utan föregående slash. (tex \"recent\" eller \"popular\")", "sso.title": "Single Sign-on-tjänster", - "sso.associated": "Associated with", - "sso.not-associated": "Click here to associate with" + "sso.associated": "Associerad med", + "sso.not-associated": "Klicka här för att associera med" } \ No newline at end of file diff --git a/public/language/sv/users.json b/public/language/sv/users.json index 29f91b76c0..a6b2aebd35 100644 --- a/public/language/sv/users.json +++ b/public/language/sv/users.json @@ -15,6 +15,6 @@ "popular_topics": "Populära ämnen", "unread_topics": "Olästa ämnen", "categories": "Kategorier", - "tags": "Märkord", - "no-users-found": "No users found!" + "tags": "Taggar", + "no-users-found": "Inga användare hittades!" } \ No newline at end of file From 9b54ce7235bd9a5db9b5b16127aa03c37c4921ee Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 3 May 2016 16:14:41 +0300 Subject: [PATCH 21/62] fix uploads with no extensions --- src/controllers/uploads.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 223f7818a1..d096ecf0de 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -185,6 +185,9 @@ function uploadFile(uid, uploadedFile, callback) { if (meta.config.hasOwnProperty('allowedFileExtensions')) { var allowed = file.allowedExtensions(); var extension = path.extname(uploadedFile.name); + if (!extension) { + extension = '.' + mime.extension(uploadedFile.type); + } if (allowed.length > 0 && allowed.indexOf(extension) === -1) { return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); } @@ -195,7 +198,7 @@ function uploadFile(uid, uploadedFile, callback) { function saveFileToLocal(uploadedFile, callback) { var extension = path.extname(uploadedFile.name); - if(!extension) { + if (!extension) { extension = '.' + mime.extension(uploadedFile.type); } From e791ed90029a92d910698244c58d9a6c364f496b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 3 May 2016 16:28:20 +0300 Subject: [PATCH 22/62] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e7842cbe4..61eaaf7af0 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "3.0.27", + "nodebb-plugin-composer-default": "3.0.28", "nodebb-plugin-dbsearch": "1.0.1", "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", From c758f59014a382513147c423c3e977acf0bdfa37 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 3 May 2016 17:17:38 +0300 Subject: [PATCH 23/62] closes #2302 --- public/src/widgets.js | 3 ++- src/controllers/api.js | 1 + src/views/admin/partials/widget-settings.tpl | 18 +++++++++++++++++ src/widgets/admin.js | 21 ++++++++++---------- src/widgets/index.js | 17 +++++++++------- 5 files changed, 42 insertions(+), 18 deletions(-) create mode 100644 src/views/admin/partials/widget-settings.tpl diff --git a/public/src/widgets.js b/public/src/widgets.js index 2a8b052bba..ffa539c6ff 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -40,7 +40,8 @@ $.get(RELATIVE_PATH + '/api/widgets/render' + (config['cache-buster'] ? '?v=' + config['cache-buster'] : ''), { locations: locations, template: template + '.tpl', - url: url + url: url, + isMobile: utils.isMobile() }, function(renderedAreas) { for (var x=0; x + +
+ + + + +
+ +
+ +
+ +
+ +
+ +
diff --git a/src/widgets/admin.js b/src/widgets/admin.js index 110f523e21..03f16f3a0a 100644 --- a/src/widgets/admin.js +++ b/src/widgets/admin.js @@ -1,8 +1,9 @@ "use strict"; - -var async = require('async'), - plugins = require('../plugins'); +var fs = require('fs'); +var path = require('path'); +var async = require('async'); +var plugins = require('../plugins'); var admin = {}; @@ -22,6 +23,9 @@ admin.get = function(callback) { }, widgets: function(next) { plugins.fireHook('filter:widgets.getWidgets', [], next); + }, + adminTemplate: function(next) { + fs.readFile(path.resolve(__dirname, '../../public/templates/admin/partials/widget-settings.tpl'), 'utf8', next); } }, function(err, widgetData) { if (err) { @@ -34,17 +38,14 @@ admin.get = function(callback) { area.data = areaData; next(err); }); - }, function(err) { if (err) { return callback(err); } - for (var w in widgetData.widgets) { - if (widgetData.widgets.hasOwnProperty(w)) { - // if this gets anymore complicated, it needs to be a template - widgetData.widgets[w].content += "

"; - } - } + + widgetData.widgets.forEach(function(w) { + w.content += widgetData.adminTemplate; + }); var templates = [], list = {}, index = 0; diff --git a/src/widgets/index.js b/src/widgets/index.js index c1421b44b6..d220f73740 100644 --- a/src/widgets/index.js +++ b/src/widgets/index.js @@ -1,12 +1,12 @@ "use strict"; -var async = require('async'), - winston = require('winston'), - templates = require('templates.js'), +var async = require('async'); +var winston = require('winston'); +var templates = require('templates.js'); - plugins = require('../plugins'), - translator = require('../../public/src/modules/translator'), - db = require('../database'); +var plugins = require('../plugins'); +var translator = require('../../public/src/modules/translator'); +var db = require('../database'); var widgets = {}; @@ -30,7 +30,10 @@ widgets.render = function(uid, area, req, res, callback) { } async.map(widgetsByLocation[location], function(widget, next) { - if (!widget || !widget.data || (!!widget.data['hide-registered'] && uid !== 0) || (!!widget.data['hide-guests'] && uid === 0)) { + if (!widget || !widget.data || + (!!widget.data['hide-registered'] && uid !== 0) || + (!!widget.data['hide-guests'] && uid === 0) || + (!!widget.data['hide-mobile'] && area.isMobile)) { return next(); } From cef7fb545b6c7525a1002e05cf6a890b7f31e760 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 3 May 2016 19:13:10 +0300 Subject: [PATCH 24/62] closes #1972 --- public/src/ajaxify.js | 16 +++++++++------- public/src/client/login.js | 7 ++++++- src/controllers/authentication.js | 2 ++ src/controllers/index.js | 22 +++++++++++++++++----- src/middleware/middleware.js | 26 ++++++++++++++++++++++++-- 5 files changed, 58 insertions(+), 15 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 53f979224a..993e45eb81 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -63,6 +63,10 @@ $(document).ready(function() { url = ajaxify.start(url, quiet); + if (!window.location.pathname.match(/\/(403|404)$/g)) { + app.previousUrl = window.location.href; + } + $('body').removeClass(ajaxify.data.bodyClass); $('#footer, #content').removeClass('hide').addClass('ajaxifying'); @@ -85,9 +89,10 @@ $(document).ready(function() { ajaxify.handleRedirects = function(url) { url = ajaxify.removeRelativePath(url.replace(/\/$/, '')).toLowerCase(); - var isAdminRoute = url.startsWith('admin') && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') !== 0; + 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('uploads') || url.startsWith('api'); - if (isAdminRoute || uploadsOrApi) { + if (isClientToAdmin || isAdminToClient || uploadsOrApi) { window.open(RELATIVE_PATH + '/' + url, '_top'); return true; } @@ -100,10 +105,6 @@ $(document).ready(function() { $(window).trigger('action:ajaxify.start', {url: url}); - if (!window.location.pathname.match(/\/(403|404)$/g)) { - app.previousUrl = window.location.href; - } - ajaxify.currentPage = url.split(/[?#]/)[0]; if (window.history && window.history.pushState) { window.history[!quiet ? 'pushState' : 'replaceState']({ @@ -136,7 +137,8 @@ $(document).ready(function() { } else if (status === 401) { app.alertError('[[global:please_log_in]]'); app.previousUrl = url; - return ajaxify.go('login'); + window.location.href = config.relative_path + '/login'; + return; } else if (status === 302 || status === 308) { if (data.responseJSON.external) { window.location.href = data.responseJSON.external; diff --git a/public/src/client/login.js b/public/src/client/login.js index ecbd607f05..85aeb68be5 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -45,7 +45,12 @@ define('forum/login', ['csrf', 'translator'], function(csrf, translator) { return false; }); - $('#content #username').focus(); + if ($('#content #username').attr('readonly')) { + $('#content #password').focus(); + } else { + $('#content #username').focus(); + } + // Add "returnTo" data if present if (app.previousUrl) { diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 560965f8ec..0912d99ac9 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -208,6 +208,8 @@ authenticationController.onSuccessfulLogin = function(req, uid, callback) { var uuid = utils.generateUUID(); req.session.meta = {}; + delete req.session.forceLogin; + // Associate IP used during login with user account user.logIP(uid, req.ip); req.session.meta.ip = req.ip; diff --git a/src/controllers/index.js b/src/controllers/index.js index 282d6c1f0a..0a3bf6f67f 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -95,15 +95,17 @@ Controllers.reset = function(req, res, next) { }; Controllers.login = function(req, res, next) { - var data = {}, - loginStrategies = require('../routes/authentication').getLoginStrategies(), - registrationType = meta.config.registrationType || 'normal'; + var data = {}; + var loginStrategies = require('../routes/authentication').getLoginStrategies(); + var registrationType = meta.config.registrationType || 'normal'; + + var allowLoginWith = (meta.config.allowLoginWith || 'username-email'); data.alternate_logins = loginStrategies.length > 0; data.authentication = loginStrategies; data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval'; - data.allowLoginWith = '[[login:' + (meta.config.allowLoginWith || 'username-email') + ']]'; + data.allowLoginWith = '[[login:' + allowLoginWith + ']]'; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:login]]'}]); data.error = req.flash('error')[0]; data.title = '[[pages:login]]'; @@ -113,8 +115,18 @@ Controllers.login = function(req, res, next) { external: data.authentication[0].url }); } + if (req.uid) { + user.getUserFields(req.uid, ['username', 'email'], function(err, user) { + if (err) { + return next(err); + } + data.username = allowLoginWith === 'email' ? user.email : user.username; + res.render('login', data); + }); + } else { + res.render('login', data); + } - res.render('login', data); }; Controllers.register = function(req, res, next) { diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index d09270c3c9..184ea28a78 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -87,7 +87,9 @@ middleware.addHeaders = function (req, res, next) { headers = _.pick(headers, Boolean); // Remove falsy headers for(var key in headers) { - res.setHeader(key, headers[key]); + if (headers.hasOwnProperty(key)) { + res.setHeader(key, headers[key]); + } } next(); @@ -103,6 +105,10 @@ middleware.pluginHooks = function(req, res, next) { }; middleware.redirectToAccountIfLoggedIn = function(req, res, next) { + if (req.session.forceLogin) { + return next(); + } + if (!req.user) { return next(); } @@ -165,10 +171,26 @@ middleware.isAdmin = function(req, res, next) { } user.isAdministrator(req.uid, function (err, isAdmin) { - if (err || isAdmin) { + if (err) { return next(err); } + if (isAdmin) { + var loginTime = req.session.meta ? req.session.meta.datetime : 0; + if (loginTime && parseInt(loginTime, 10) > Date.now() - 3600000) { + return next(); + } + + req.session.returnTo = nconf.get('relative_path') + req.path.replace(/^\/api/, ''); + req.session.forceLogin = 1; + if (res.locals.isAPI) { + res.status(401).json({}); + } else { + res.redirect('/login'); + } + return; + } + if (res.locals.isAPI) { return controllers.helpers.notAllowed(req, res); } From 60c1401a70b86b6c9ae340844028366cd038f206 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 4 May 2016 12:20:53 +0300 Subject: [PATCH 25/62] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 61eaaf7af0..32e74878a1 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", "nodebb-theme-lavender": "3.0.9", - "nodebb-theme-persona": "4.0.128", - "nodebb-theme-vanilla": "5.0.68", + "nodebb-theme-persona": "4.0.129", + "nodebb-theme-vanilla": "5.0.69", "nodebb-widget-essentials": "2.0.9", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From 90446bdc7f42473ea9813130835e88da41414478 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 4 May 2016 12:29:34 +0300 Subject: [PATCH 26/62] closes #4524 --- src/controllers/accounts/profile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index e52b5f4861..74e919d8f5 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -54,7 +54,7 @@ profileController.get = function(req, res, callback) { }, aboutme: function(next) { if (userData.aboutme) { - plugins.fireHook('filter:parse.raw', userData.aboutme, next); + plugins.fireHook('filter:parse.aboutme', userData.aboutme, next); } else { next(); } From a3c197751ea2097e648452c759bdd288135b061d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 4 May 2016 12:31:12 +0300 Subject: [PATCH 27/62] up markdown --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32e74878a1..2b4f699cc4 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "nodebb-plugin-dbsearch": "1.0.1", "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", - "nodebb-plugin-markdown": "5.1.3", + "nodebb-plugin-markdown": "5.1.4", "nodebb-plugin-mentions": "1.0.21", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.6", From f473e03f76c6890068963e99b61584990b67d675 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 4 May 2016 12:54:26 +0300 Subject: [PATCH 28/62] closes #4565 --- public/src/client/topic/posts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 605994c9e6..88d80a3872 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -336,8 +336,9 @@ define('forum/topic/posts', [ Posts.showBottomPostBar = function() { var mainPost = components.get('post', 'index', 0); var posts = $('[component="post"]'); - if (!!mainPost.length && posts.length > 1 && $('.post-bar').length < 2) { + if (!!mainPost.length && posts.length > 1 && $('.post-bar').length < 2 && $('.post-bar-placeholder').length) { $('.post-bar').clone().appendTo(mainPost); + $('.post-bar-placeholder').remove(); } else if (mainPost.length && posts.length < 2) { mainPost.find('.post-bar').remove(); } From a47e0f3384d54c6dfe274d922aa488e9c047eb87 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 4 May 2016 13:00:19 +0300 Subject: [PATCH 29/62] up themes --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2b4f699cc4..79641a4459 100644 --- a/package.json +++ b/package.json @@ -55,9 +55,9 @@ "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", - "nodebb-theme-lavender": "3.0.9", - "nodebb-theme-persona": "4.0.129", - "nodebb-theme-vanilla": "5.0.69", + "nodebb-theme-lavender": "3.0.10", + "nodebb-theme-persona": "4.0.130", + "nodebb-theme-vanilla": "5.0.70", "nodebb-widget-essentials": "2.0.9", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From ddf83202dbb8282391eb273505c8fde222780acb Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Wed, 4 May 2016 09:02:31 -0400 Subject: [PATCH 30/62] Latest translations and fallbacks --- public/language/ar/error.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/language/ar/error.json b/public/language/ar/error.json index 505831c652..73e9b24094 100644 --- a/public/language/ar/error.json +++ b/public/language/ar/error.json @@ -1,5 +1,5 @@ { - "invalid-data": "بيانات غير صالحة", + "invalid-data": "بيانات غير صحيحة", "not-logged-in": "لم تقم بتسجيل الدخول", "account-locked": "تم حظر حسابك مؤقتًا.", "search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل", From b6c6e8c08d487c2b2e8dfb3a28ec6b2d61129221 Mon Sep 17 00:00:00 2001 From: "Paul Westerdale (ABRS Limited)" Date: Wed, 4 May 2016 14:17:31 +0100 Subject: [PATCH 31/62] Added hooks into topic follow and unfollow --- src/topics/follow.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/topics/follow.js b/src/topics/follow.js index c05a796741..6725c3534b 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -12,6 +12,7 @@ var notifications = require('../notifications'); var privileges = require('../privileges'); var meta = require('../meta'); var emailer = require('../emailer'); +var plugins = require('../plugins'); module.exports = function(Topics) { @@ -56,6 +57,7 @@ module.exports = function(Topics) { return next(new Error('[[error:no-topic]]')); } db.setAdd('tid:' + tid + ':followers', uid, next); + plugins.fireHook('action:topic.follow', {uid : uid, tid : tid}); }, function(next) { db.sortedSetAdd('uid:' + uid + ':followed_tids', Date.now(), tid, next); @@ -74,6 +76,7 @@ module.exports = function(Topics) { return next(new Error('[[error:no-topic]]')); } db.setRemove('tid:' + tid + ':followers', uid, next); + plugins.fireHook('action:topic.unfollow', {uid : uid, tid : tid}); }, function(next) { db.sortedSetRemove('uid:' + uid + ':followed_tids', tid, next); From cdf65600126e1b05d3af7b00ed80930890341161 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 4 May 2016 09:53:32 -0400 Subject: [PATCH 32/62] Using async.apply and fixed code style --- src/topics/follow.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/topics/follow.js b/src/topics/follow.js index 6725c3534b..f6f1da7dc5 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -57,8 +57,8 @@ module.exports = function(Topics) { return next(new Error('[[error:no-topic]]')); } db.setAdd('tid:' + tid + ':followers', uid, next); - plugins.fireHook('action:topic.follow', {uid : uid, tid : tid}); }, + async.apply(plugins.fireHook, 'action:topic.follow', { uid: uid, tid: tid }), function(next) { db.sortedSetAdd('uid:' + uid + ':followed_tids', Date.now(), tid, next); } @@ -76,8 +76,8 @@ module.exports = function(Topics) { return next(new Error('[[error:no-topic]]')); } db.setRemove('tid:' + tid + ':followers', uid, next); - plugins.fireHook('action:topic.unfollow', {uid : uid, tid : tid}); }, + async.apply(plugins.fireHook, 'action:topic.unfollow', { uid: uid, tid: tid }), function(next) { db.sortedSetRemove('uid:' + uid + ':followed_tids', tid, next); } From 05a55c7d6560f8f8fa9798df1602bee260d3a739 Mon Sep 17 00:00:00 2001 From: Paul Westerdale Date: Wed, 4 May 2016 14:54:51 +0100 Subject: [PATCH 33/62] add roomId to messaging filter (#4596) --- src/messaging/create.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/messaging/create.js b/src/messaging/create.js index 3b2b5dcf26..cf78affb64 100644 --- a/src/messaging/create.js +++ b/src/messaging/create.js @@ -55,7 +55,8 @@ module.exports = function(Messaging) { message = { content: content, timestamp: timestamp, - fromuid: fromuid + fromuid: fromuid, + roomId: roomId }; plugins.fireHook('filter:messaging.save', message, next); From b12811d21dd2b07bbe4d028ccecab53e5382cdc8 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 4 May 2016 10:08:56 -0400 Subject: [PATCH 34/62] spring cleaning --- src/controllers/authentication.js | 2 - src/plugins/hooks.js | 4 +- src/upgrade.js | 84 +------------------------------ src/user/delete.js | 4 -- 4 files changed, 2 insertions(+), 92 deletions(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 0912d99ac9..bc2369bd62 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -310,8 +310,6 @@ authenticationController.logout = function(req, res, next) { user.setUserField(uid, 'lastonline', Date.now() - 300000); - // action:user.loggedOut deprecated in > v0.9.3 - plugins.fireHook('action:user.loggedOut', {req: req, res: res, uid: uid}); plugins.fireHook('static:user.loggedOut', {req: req, res: res, uid: uid}, function() { res.status(200).send(''); }); diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 3330608154..aad5466005 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -5,9 +5,7 @@ var winston = require('winston'), module.exports = function(Plugins) { Plugins.deprecatedHooks = { - 'filter:user.delete': 'static:user.delete', - 'filter:user.custom_fields': null, - 'action:user.loggedOut': 'static:user.loggedOut' + 'filter:user.custom_fields': null // remove in v1.1.0 }; /* diff --git a/src/upgrade.js b/src/upgrade.js index 87d2dc7c78..1874db129e 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -6,7 +6,7 @@ var db = require('./database'), Upgrade = {}, - minSchemaDate = Date.UTC(2015, 7, 18), // This value gets updated every new MINOR version + minSchemaDate = Date.UTC(2015, 10, 6), // This value gets updated every new MAJOR version schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema @@ -62,88 +62,6 @@ Upgrade.upgrade = function(callback) { } }); }, - function(next) { - thisSchemaDate = Date.UTC(2015, 8, 30); - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2015/09/30] Converting default Gravatar image to default User Avatar'); - - async.waterfall([ - async.apply(db.isObjectField, 'config', 'customGravatarDefaultImage'), - function(keyExists, _next) { - if (keyExists) { - _next(); - } else { - winston.info('[2015/09/30] Converting default Gravatar image to default User Avatar skipped'); - Upgrade.update(thisSchemaDate, next); - next(); - } - }, - async.apply(db.getObjectField, 'config', 'customGravatarDefaultImage'), - async.apply(db.setObjectField, 'config', 'defaultAvatar'), - async.apply(db.deleteObjectField, 'config', 'customGravatarDefaultImage') - ], function(err) { - if (err) { - return next(err); - } - - winston.info('[2015/09/30] Converting default Gravatar image to default User Avatar done'); - Upgrade.update(thisSchemaDate, next); - }); - } else { - winston.info('[2015/09/30] Converting default Gravatar image to default User Avatar skipped'); - next(); - } - }, - function(next) { - thisSchemaDate = Date.UTC(2015, 10, 6); - if (schemaDate < thisSchemaDate) { - updatesMade = true; - winston.info('[2015/11/06] Removing gravatar'); - - db.getSortedSetRange('users:joindate', 0, -1, function(err, uids) { - if (err) { - return next(err); - } - - async.eachLimit(uids, 500, function(uid, next) { - db.getObjectFields('user:' + uid, ['picture', 'gravatarpicture'], function(err, userData) { - if (err) { - return next(err); - } - - if (!userData.picture || !userData.gravatarpicture) { - return next(); - } - - if (userData.gravatarpicture === userData.picture) { - async.series([ - function (next) { - db.setObjectField('user:' + uid, 'picture', '', next); - }, - function (next) { - db.deleteObjectField('user:' + uid, 'gravatarpicture', next); - } - ], next); - } else { - db.deleteObjectField('user:' + uid, 'gravatarpicture', next); - } - }); - }, function(err) { - if (err) { - return next(err); - } - - winston.info('[2015/11/06] Gravatar pictures removed!'); - Upgrade.update(thisSchemaDate, next); - }); - }); - - } else { - winston.info('[2015/11/06] Gravatar removal skipped'); - next(); - } - }, function(next) { thisSchemaDate = Date.UTC(2015, 11, 15); diff --git a/src/user/delete.js b/src/user/delete.js index 3ab5176725..011ed0aa12 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -121,10 +121,6 @@ module.exports = function(User) { }, function(next) { groups.leaveAllGroups(uid, next); - }, - function(next) { - // Deprecated as of v0.7.4, remove in v1.0.0 - plugins.fireHook('filter:user.delete', uid, next); } ], next); }, From 52e4a37df8c58543a5114641a19347507a30904b Mon Sep 17 00:00:00 2001 From: boomzilla Date: Wed, 4 May 2016 10:29:43 -0400 Subject: [PATCH 35/62] When forking a topic, updates user bookmarks in the topic to keep the last read position (#4554) from inadvertently being too far down the topic due to post indices decreasing because some posts were moved to a new topic. --- public/src/client/topic/fork.js | 3 +- src/meta/configs.js | 2 +- src/socket.io/topics.js | 2 +- src/topics.js | 84 +++++++++++++++++++++++++++++ src/topics/fork.js | 5 +- tests/topics.js | 96 +++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 4 deletions(-) diff --git a/public/src/client/topic/fork.js b/public/src/client/topic/fork.js index a55b45220c..50888a9ca3 100644 --- a/public/src/client/topic/fork.js +++ b/public/src/client/topic/fork.js @@ -55,7 +55,8 @@ define('forum/topic/fork', ['components', 'postSelect'], function(components, po forkCommit.attr('disabled', true); socket.emit('topics.createTopicFromPosts', { title: forkModal.find('#fork-title').val(), - pids: postSelect.pids + pids: postSelect.pids, + fromTid: ajaxify.data.tid }, function(err, newTopic) { function fadeOutAndRemove(pid) { components.get('post', 'pid', pid).fadeOut(500, function() { diff --git a/src/meta/configs.js b/src/meta/configs.js index 3ff42f66ae..69cc375b85 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -135,4 +135,4 @@ module.exports = function(Meta) { db.deleteObjectField('config', field); }; -}; \ No newline at end of file +}; diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index c96a77265e..6f5ab296d9 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -69,7 +69,7 @@ SocketTopics.createTopicFromPosts = function(socket, data, callback) { return callback(new Error('[[error:invalid-data]]')); } - topics.createTopicFromPosts(socket.uid, data.title, data.pids, callback); + topics.createTopicFromPosts(socket.uid, data.title, data.pids, data.fromTid, callback); }; SocketTopics.toggleFollow = function(socket, tid, callback) { diff --git a/src/topics.js b/src/topics.js index 385d3ae558..fbdd507d9a 100644 --- a/src/topics.js +++ b/src/topics.js @@ -328,4 +328,88 @@ var social = require('./social'); } }; + Topics.getTopicBookmarks = function( tid, callback ){ + db.getSortedSetRangeWithScores(['tid:' + tid + ':bookmarks'], 0, -1, callback ); + }; + + Topics.updateTopicBookmarks = function( tid, pids, callback ){ + var maxIndex; + var Posts = posts; + async.waterfall([ + function(next){ + Topics.getPostCount( tid, next ); + }, + function(postcount, next){ + maxIndex = postcount; + Topics.getTopicBookmarks( tid, next ); + }, + function(bookmarks, next){ + var uids = bookmarks.map( function( bookmark ){return bookmark.value}); + var forkedPosts = pids.map( function( pid ){ return { pid: pid, tid: tid }; } ); + var uidBookmark = new Object(); + var uidData = bookmarks.map( + function( bookmark ){ + var u = new Object(); + u.uid = bookmark.value; + u.bookmark = bookmark.score; + return u; + } ); + async.map( + uidData, + function( data, mapCallback ){ + Posts.getPostIndices( + forkedPosts, + data.uid, + function( err, indices ){ + if( err ){ + callback( err ); + } + data.postIndices = indices; + mapCallback( null, data ); + } ) + }, + function( err, results ){ + if( err ){ + return callback(); + } + async.map( + results, + function( data, mapCallback ){ + var uid = data.uid; + var bookmark = data.bookmark; + bookmark = bookmark < maxIndex ? bookmark : maxIndex; + var postIndices = data.postIndices; + var i; + for( i = 0; i < postIndices.length && postIndices[i] < data.bookmark; ++i ){ + --bookmark; + } + + if( bookmark != data.bookmark ){ + mapCallback( null, { uid: uid, bookmark: bookmark } ); + } + else{ + mapCallback( null, null ); + } + }, + function( err, results ){ + async.map( results, + function(ui, cb ){ + if( ui && ui.bookmark){ + Topics.setUserBookmark( tid, ui.uid, ui.bookmark, cb ); + } + else{ + return cb( null, null ); + } + }, + function( err, results ){ + next(); + } + ); + } + ); + } + ); + }], + function( err, result ){ callback();} ); + }; }(exports)); diff --git a/src/topics/fork.js b/src/topics/fork.js index 80143e4d46..768b656d2a 100644 --- a/src/topics/fork.js +++ b/src/topics/fork.js @@ -13,7 +13,7 @@ var meta = require('../meta'); module.exports = function(Topics) { - Topics.createTopicFromPosts = function(uid, title, pids, callback) { + Topics.createTopicFromPosts = function(uid, title, pids, fromTid, callback) { if (title) { title = title.trim(); } @@ -55,6 +55,9 @@ module.exports = function(Topics) { } Topics.create({uid: results.postData.uid, title: title, cid: cid}, next); }, + function( results, next) { + Topics.updateTopicBookmarks(fromTid, pids, function(){ next( null, results );} ); + }, function(_tid, next) { function move(pid, next) { privileges.posts.canEdit(pid, uid, function(err, canEdit) { diff --git a/tests/topics.js b/tests/topics.js index fe00db7646..60c71960cd 100644 --- a/tests/topics.js +++ b/tests/topics.js @@ -7,6 +7,8 @@ var db = require('./mocks/databasemock'); var topics = require('../src/topics'); var categories = require('../src/categories'); var User = require('../src/user'); +var groups = require('../src/groups'); +var async = require('async'); describe('Topic\'s', function() { var topic, @@ -188,6 +190,100 @@ describe('Topic\'s', function() { }); + describe('.fork', function(){ + var newTopic; + var replies = new Array(); + var topicPids; + var originalBookmark = 5; + function postReply( next ){ + topics.reply({uid: topic.userId, content: 'test post ' + replies.length, tid: newTopic.tid}, + function(err, result) { + assert.equal(err, null, 'was created with error'); + assert.ok(result); + replies.push( result ); + next(); + } + ); + } + + before( function(done) { + async.waterfall( + [ + function(next){ + groups.join('administrators', topic.userId, next); + }, + function( next ){ + topics.post({uid: topic.userId, title: topic.title, content: topic.content, cid: topic.categoryId}, function(err, result) { + assert.ifError( err ); + newTopic = result.topicData; + next(); + }); + }, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ postReply( next );}, + function( next ){ + topicPids = replies.map( function( reply ){ return reply.pid; } ); + topics.setUserBookmark( newTopic.tid, topic.userId, originalBookmark, next ); + }], + done ); + }); + + it('should have 12 replies', function(done) { + assert.equal( 12, replies.length ); + done(); + }); + + it('should not update the user\'s bookmark', function(done){ + async.waterfall([ + function(next){ + topics.createTopicFromPosts( + topic.userId, + 'Fork test, no bookmark update', + topicPids.slice( -2 ), + newTopic.tid, + next ); + }, + function( forkedTopicData, next){ + topics.getUserBookmark( newTopic.tid, topic.userId, next ); + }, + function( bookmark, next ){ + assert.equal( originalBookmark, bookmark ); + next(); + } + ],done); + }); + + it('should update the user\'s bookmark ', function(done){ + async.waterfall([ + function(next){ + topics.createTopicFromPosts( + topic.userId, + 'Fork test, no bookmark update', + topicPids.slice( 1, 3 ), + newTopic.tid, + next ); + }, + function( forkedTopicData, next){ + topics.getUserBookmark( newTopic.tid, topic.userId, next ); + }, + function( bookmark, next ){ + assert.equal( originalBookmark - 2, bookmark ); + next(); + } + ],done); + }); + }); + after(function() { db.flushdb(); }); From 7540fb54ac026f03a2ac6fba08cad819fc1dc447 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Wed, 4 May 2016 09:53:06 -0500 Subject: [PATCH 36/62] fix avatar upload crash discovered by @AccaliaDeElementia --- src/user/picture.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/user/picture.js b/src/user/picture.js index a55eee2975..385050fcf2 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -63,7 +63,9 @@ module.exports = function(User) { async.series([ async.apply(image.normalise, picture.path, extension), async.apply(fs.rename, picture.path + '.png', picture.path) - ], next); + ], function(err) { + next(err); + }); }, function(next) { User.getUserField(updateUid, 'uploadedpicture', next); From eb0aea6390cd38c3802d070262068c676d796aaf Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 20:24:03 +0300 Subject: [PATCH 37/62] add /user/ and /post/ redirects change notifications to use new redirects --- public/src/modules/notifications.js | 15 ++--- src/controllers/helpers.js | 14 ++-- src/controllers/index.js | 1 + src/controllers/posts.js | 24 +++++++ src/middleware/middleware.js | 15 +++++ src/notifications.js | 5 +- src/posts/topics.js | 51 +++++++++++++- src/routes/accounts.js | 4 +- src/routes/index.js | 38 ++++++----- src/socket.io/helpers.js | 3 +- src/socket.io/notifications.js | 24 ------- src/socket.io/posts/flag.js | 1 + src/socket.io/user.js | 2 +- src/topics/follow.js | 1 + src/user/notifications.js | 100 +++------------------------- 15 files changed, 142 insertions(+), 156 deletions(-) create mode 100644 src/controllers/posts.js diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index 35f1e677dd..f98074ac5b 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -84,14 +84,7 @@ define('notifications', ['sounds', 'translator', 'components'], function(sound, payload.message = notifData.bodyShort; payload.type = 'info'; payload.clickfn = function() { - socket.emit('notifications.generatePath', notifData.nid, function(err, path) { - if (err) { - return app.alertError(err.message); - } - if (path) { - ajaxify.go(path); - } - }); + window.location.href = config.relative_path + '/' + notifData.path; }; } else { payload.message = '[[notifications:you_have_unread_notifications]]'; @@ -104,13 +97,13 @@ define('notifications', ['sounds', 'translator', 'components'], function(sound, if (ajaxify.currentPage === 'notifications') { ajaxify.refresh(); } - + if (!unreadNotifs[notifData.nid]) { incrementNotifCount(1); sound.play('notification'); - unreadNotifs[notifData.nid] = true; - } + unreadNotifs[notifData.nid] = true; + } }); socket.on('event:notifications.updateCount', function(count) { diff --git a/src/controllers/helpers.js b/src/controllers/helpers.js index a558d665dd..8032ed7a0e 100644 --- a/src/controllers/helpers.js +++ b/src/controllers/helpers.js @@ -1,13 +1,13 @@ 'use strict'; -var nconf = require('nconf'), - async = require('async'), - validator = require('validator'), +var nconf = require('nconf'); +var async = require('async'); +var validator = require('validator'); - translator = require('../../public/src/modules/translator'), - categories = require('../categories'), - plugins = require('../plugins'), - meta = require('../meta'); +var translator = require('../../public/src/modules/translator'); +var categories = require('../categories'); +var plugins = require('../plugins'); +var meta = require('../meta'); var helpers = {}; diff --git a/src/controllers/index.js b/src/controllers/index.js index 0a3bf6f67f..76d86cee31 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -12,6 +12,7 @@ var helpers = require('./helpers'); var Controllers = { topics: require('./topics'), + posts: require('./posts'), categories: require('./categories'), category: require('./category'), unread: require('./unread'), diff --git a/src/controllers/posts.js b/src/controllers/posts.js new file mode 100644 index 0000000000..c8b490f65b --- /dev/null +++ b/src/controllers/posts.js @@ -0,0 +1,24 @@ +"use strict"; + +var posts = require('../posts'); +var helpers = require('./helpers'); + +var postsController = {}; + +postsController.redirectToPost = function(req, res, callback) { + var pid = parseInt(req.params.pid, 10); + if (!pid) { + return callback(); + } + + posts.generatePostPath(pid, req.uid, function(err, path) { + if (err) { + return callback(err); + } + + helpers.redirect(res, path); + }); +}; + + +module.exports = postsController; diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 184ea28a78..1dd5eee7bc 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -165,6 +165,21 @@ middleware.checkAccountPermissions = function(req, res, next) { }); }; +middleware.redirectUidToUserslug = function(req, res, next) { + var uid = parseInt(req.params.userslug, 10); + if (!uid) { + return next(); + } + user.getUserField(uid, 'userslug', function(err, userslug) { + if (err || !userslug) { + return next(err); + } + + var path = req.path.replace(/^\/api/, '').replace(uid, function() { return userslug; }); + controllers.helpers.redirect(res, path); + }); +}; + middleware.isAdmin = function(req, res, next) { if (!req.uid) { return controllers.helpers.notAllowed(req, res); diff --git a/src/notifications.js b/src/notifications.js index 8a6b41d303..4f6d73f385 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -12,6 +12,7 @@ var User = require('./user'); var groups = require('./groups'); var meta = require('./meta'); var plugins = require('./plugins'); +var utils = require('../public/src/utils'); (function(Notifications) { @@ -45,6 +46,8 @@ var plugins = require('./plugins'); return next(null, null); } + notification.datetimeISO = utils.toISOString(notification.datetime); + if (notification.bodyLong) { notification.bodyLong = S(notification.bodyLong).escapeHTML().s; } @@ -85,7 +88,7 @@ var plugins = require('./plugins'); // Removes nids that have been pruned db.isSortedSetMembers('notifications', nids, function(err, exists) { if (err) { - return callbacK(err); + return callback(err); } nids = nids.filter(function(notifId, idx) { diff --git a/src/posts/topics.js b/src/posts/topics.js index a9946fdace..7ffd9592ce 100644 --- a/src/posts/topics.js +++ b/src/posts/topics.js @@ -1,8 +1,10 @@ 'use strict'; -var async = require('async'), - topics = require('../topics'); +var async = require('async'); + +var topics = require('../topics'); +var utils = require('../../public/src/utils'); module.exports = function(Posts) { @@ -42,4 +44,49 @@ module.exports = function(Posts) { ], callback); }; + Posts.generatePostPath = function (pid, uid, callback) { + Posts.generatePostPaths([pid], uid, function(err, paths) { + callback(err, Array.isArray(paths) && paths.length ? paths[0] : null); + }); + }; + + Posts.generatePostPaths = function (pids, uid, callback) { + async.waterfall([ + function (next) { + Posts.getPostsFields(pids, ['pid', 'tid'], next); + }, + function (postData, next) { + async.parallel({ + indices: function(next) { + Posts.getPostIndices(postData, uid, next); + }, + topics: function(next) { + var tids = postData.map(function(post) { + return post ? post.tid : null; + }); + + topics.getTopicsFields(tids, ['slug', 'deleted'], next); + } + }, next); + }, + function (results, next) { + var paths = pids.map(function(pid, index) { + if (parseInt(results.topics[index].deleted, 10) === 1) { + return null; + } + + var slug = results.topics[index] ? results.topics[index].slug : null; + var postIndex = utils.isNumber(results.indices[index]) ? parseInt(results.indices[index], 10) + 1 : null; + + if (slug && postIndex) { + return '/topic/' + slug + '/' + postIndex; + } + return null; + }); + + next(null, paths); + } + ], callback); + }; + }; \ No newline at end of file diff --git a/src/routes/accounts.js b/src/routes/accounts.js index 8c7a505d0b..48d23b0ce0 100644 --- a/src/routes/accounts.js +++ b/src/routes/accounts.js @@ -4,8 +4,8 @@ var helpers = require('./helpers'); var setupPageRoute = helpers.setupPageRoute; module.exports = function (app, middleware, controllers) { - var middlewares = [middleware.checkGlobalPrivacySettings]; - var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions]; + var middlewares = [middleware.checkGlobalPrivacySettings, middleware.redirectUidToUserslug]; + var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, middleware.redirectUidToUserslug]; setupPageRoute(app, '/user/:userslug', middleware, middlewares, controllers.accounts.profile.get); setupPageRoute(app, '/user/:userslug/following', middleware, middlewares, controllers.accounts.follow.getFollowing); diff --git a/src/routes/index.js b/src/routes/index.js index 8efdc1f2a7..86198b56dd 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,23 +1,23 @@ "use strict"; -var nconf = require('nconf'), - path = require('path'), - async = require('async'), - winston = require('winston'), - controllers = require('../controllers'), - plugins = require('../plugins'), - express = require('express'), - validator = require('validator'), +var nconf = require('nconf'); +var path = require('path'); +var async = require('async'); +var winston = require('winston'); +var controllers = require('../controllers'); +var plugins = require('../plugins'); +var express = require('express'); +var validator = require('validator'); - accountRoutes = require('./accounts'), +var accountRoutes = require('./accounts'); - metaRoutes = require('./meta'), - apiRoutes = require('./api'), - adminRoutes = require('./admin'), - feedRoutes = require('./feeds'), - pluginRoutes = require('./plugins'), - authRoutes = require('./authentication'), - helpers = require('./helpers'); +var metaRoutes = require('./meta'); +var apiRoutes = require('./api'); +var adminRoutes = require('./admin'); +var feedRoutes = require('./feeds'); +var pluginRoutes = require('./plugins'); +var authRoutes = require('./authentication'); +var helpers = require('./helpers'); var setupPageRoute = helpers.setupPageRoute; @@ -46,6 +46,10 @@ function topicRoutes(app, middleware, controllers) { setupPageRoute(app, '/topic/:topic_id/:slug?', middleware, [], controllers.topics.get); } +function postRoutes(app, middleware, controllers) { + setupPageRoute(app, '/post/:pid', middleware, [], controllers.posts.redirectToPost); +} + function tagRoutes(app, middleware, controllers) { setupPageRoute(app, '/tags/:tag', middleware, [middleware.privateTagListing], controllers.tags.getTag); setupPageRoute(app, '/tags', middleware, [middleware.privateTagListing], controllers.tags.getTags); @@ -71,7 +75,6 @@ function userRoutes(app, middleware, controllers) { setupPageRoute(app, '/users/banned', middleware, middlewares, controllers.users.getBannedUsers); } - function groupRoutes(app, middleware, controllers) { var middlewares = [middleware.checkGlobalPrivacySettings, middleware.exposeGroupName]; @@ -124,6 +127,7 @@ module.exports = function(app, middleware, hotswapIds) { mainRoutes(router, middleware, controllers); topicRoutes(router, middleware, controllers); + postRoutes(router, middleware, controllers); globalModRoutes(router, middleware, controllers); tagRoutes(router, middleware, controllers); categoryRoutes(router, middleware, controllers); diff --git a/src/socket.io/helpers.js b/src/socket.io/helpers.js index e4621811ca..b39bf7360a 100644 --- a/src/socket.io/helpers.js +++ b/src/socket.io/helpers.js @@ -78,6 +78,7 @@ SocketHelpers.sendNotificationToPostOwner = function(pid, fromuid, notification) bodyShort: '[[' + notification + ', ' + results.username + ', ' + titleEscaped + ']]', bodyLong: results.postObj.content, pid: pid, + path: '/post/' + pid, nid: 'post:' + pid + ':uid:' + fromuid, from: fromuid, mergeId: notification + '|' + pid, @@ -110,7 +111,7 @@ SocketHelpers.sendNotificationToTopicOwner = function(tid, fromuid, notification notifications.create({ bodyShort: '[[' + notification + ', ' + results.username + ', ' + titleEscaped + ']]', - path: nconf.get('relative_path') + '/topic/' + results.topicData.slug, + path: '/topic/' + results.topicData.slug, nid: 'tid:' + tid + ':uid:' + fromuid, from: fromuid }, function(err, notification) { diff --git a/src/socket.io/notifications.js b/src/socket.io/notifications.js index 121ede2a96..db6753bc40 100644 --- a/src/socket.io/notifications.js +++ b/src/socket.io/notifications.js @@ -56,28 +56,4 @@ SocketNotifs.markAllRead = function(socket, data, callback) { notifications.markAllRead(socket.uid, callback); }; -SocketNotifs.generatePath = function(socket, nid, callback) { - if (!socket.uid) { - return callback(new Error('[[error:no-privileges]]'));; - } - async.waterfall([ - function (next) { - notifications.get(nid, next); - }, - function (notification, next) { - if (!notification) { - return next(null, ''); - } - user.notifications.generateNotificationPaths([notification], socket.uid, next); - }, - function (notificationsData, next) { - if (notificationsData && notificationsData.length) { - next(null, notificationsData[0].path); - } else { - next(); - } - } - ], callback); -}; - module.exports = SocketNotifs; diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js index bbf7a4721c..e9d378d2be 100644 --- a/src/socket.io/posts/flag.js +++ b/src/socket.io/posts/flag.js @@ -90,6 +90,7 @@ module.exports = function(SocketPosts) { bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + titleEscaped + ']]', bodyLong: post.content, pid: data.pid, + path: '/post/' + pid, nid: 'post_flag:' + data.pid + ':uid:' + socket.uid, from: socket.uid, mergeId: 'notifications:user_flagged_post_in|' + data.pid, diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 75ecd949fd..59672c006b 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -155,7 +155,7 @@ SocketUser.follow = function(socket, data, callback) { bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', nid: 'follow:' + data.uid + ':uid:' + socket.uid, from: socket.uid, - path: '/user/' + userData.userslug, + path: '/user/' + socket.uid, mergeId: 'notifications:user_started_following_you' }, next); }, diff --git a/src/topics/follow.js b/src/topics/follow.js index f6f1da7dc5..90708ba50a 100644 --- a/src/topics/follow.js +++ b/src/topics/follow.js @@ -141,6 +141,7 @@ module.exports = function(Topics) { bodyShort: '[[notifications:user_posted_to, ' + postData.user.username + ', ' + titleEscaped + ']]', bodyLong: postData.content, pid: postData.pid, + path: '/post/' + postData.pid, nid: 'new_post:tid:' + postData.topic.tid + ':pid:' + postData.pid + ':uid:' + exceptUid, tid: postData.topic.tid, from: exceptUid, diff --git a/src/user/notifications.js b/src/user/notifications.js index 2f766a72de..852f9f0924 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -1,19 +1,14 @@ 'use strict'; -var async = require('async'), - nconf = require('nconf'), - winston = require('winston'), - S = require('string'), +var async = require('async'); +var winston = require('winston'); +var S = require('string'); - user = require('../user'), - db = require('../database'), - meta = require('../meta'), - notifications = require('../notifications'), - posts = require('../posts'), - topics = require('../topics'), - privileges = require('../privileges'), - utils = require('../../public/src/utils'); +var db = require('../database'); +var meta = require('../meta'); +var notifications = require('../notifications'); +var privileges = require('../privileges'); (function(UserNotifications) { @@ -103,89 +98,13 @@ var async = require('async'), if (err) { return callback(err); } - - UserNotifications.generateNotificationPaths(notifications, uid, callback); - }); - }; - - UserNotifications.generateNotificationPaths = function (notifications, uid, callback) { - var pids = notifications.map(function(notification) { - return notification ? notification.pid : null; - }); - - generatePostPaths(pids, uid, function(err, pidToPaths) { - if (err) { - return callback(err); - } - - notifications = notifications.map(function(notification, index) { - if (!notification) { - return null; - } - - notification.path = pidToPaths[notification.pid] || notification.path || null; - - if (notification.nid.startsWith('follow')) { - notification.path = '/user/' + notification.user.userslug; - } - - notification.datetimeISO = utils.toISOString(notification.datetime); - return notification; - }).filter(function(notification) { - // Remove notifications that do not resolve to a path - return notification && notification.path !== null; + notifications = notifications.filter(function(notification) { + return notification && notification.path; }); - callback(null, notifications); }); }; - function generatePostPaths(pids, uid, callback) { - pids = pids.filter(Boolean); - var postKeys = pids.map(function(pid) { - return 'post:' + pid; - }); - - db.getObjectsFields(postKeys, ['pid', 'tid'], function(err, postData) { - if (err) { - return callback(err); - } - - var topicKeys = postData.map(function(post) { - return post ? 'topic:' + post.tid : null; - }); - - async.parallel({ - indices: function(next) { - posts.getPostIndices(postData, uid, next); - }, - topics: function(next) { - db.getObjectsFields(topicKeys, ['slug', 'deleted'], next); - } - }, function(err, results) { - if (err) { - return callback(err); - } - - var pidToPaths = {}; - pids.forEach(function(pid, index) { - if (parseInt(results.topics[index].deleted, 10) === 1) { - pidToPaths[pid] = null; - return; - } - - var slug = results.topics[index] ? results.topics[index].slug : null; - var postIndex = utils.isNumber(results.indices[index]) ? parseInt(results.indices[index], 10) + 1 : null; - - if (slug && postIndex) { - pidToPaths[pid] = '/topic/' + slug + '/' + postIndex; - } - }); - - callback(null, pidToPaths); - }); - }); - } UserNotifications.getDailyUnread = function(uid, callback) { var yesterday = Date.now() - (1000 * 60 * 60 * 24); // Approximate, can be more or less depending on time changes, makes no difference really. @@ -300,6 +219,7 @@ var async = require('async'), bodyShort: '[[notifications:user_posted_topic, ' + postData.user.username + ', ' + title + ']]', bodyLong: postData.content, pid: postData.pid, + path: '/post/' + postData.pid, nid: 'tid:' + postData.tid + ':uid:' + uid, tid: postData.tid, from: uid From 78e9c81de4079d99281ee2a94da8c1002a81bbbb Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 20:29:31 +0300 Subject: [PATCH 38/62] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79641a4459..eb0da69ac6 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", "nodebb-plugin-markdown": "5.1.4", - "nodebb-plugin-mentions": "1.0.21", + "nodebb-plugin-mentions": "1.0.22", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", From 22e7f8356434621a6fe11a1af66489e5e7e5317b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 20:52:49 +0300 Subject: [PATCH 39/62] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb0da69ac6..2dd99993a2 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "3.0.28", + "nodebb-plugin-composer-default": "3.0.29", "nodebb-plugin-dbsearch": "1.0.1", "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", From b493b81dbdeaaad4443ec67e1745224024e49d54 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 21:00:15 +0300 Subject: [PATCH 40/62] fix crash if category or children is invalid --- public/src/modules/helpers.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index fda87711fb..7250fab3b6 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -103,7 +103,9 @@ helpers.generateChildrenCategories = function(category) { var html = ''; var relative_path = (typeof config !== 'undefined' ? config.relative_path : require('nconf').get('relative_path')); - + if (!category || !category.children) { + return html; + } category.children.forEach(function(child) { if (!child) { return; From 19b9242934ca8e63c85b8ad58641925c85565580 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 22:08:15 +0300 Subject: [PATCH 41/62] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2dd99993a2..8f8ce4227e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", "nodebb-plugin-markdown": "5.1.4", - "nodebb-plugin-mentions": "1.0.22", + "nodebb-plugin-mentions": "1.0.23", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", From decd8535071b9f40ab6effc6d6ac851941296f83 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 22:13:52 +0300 Subject: [PATCH 42/62] closes #4602 --- public/src/modules/notifications.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/src/modules/notifications.js b/public/src/modules/notifications.js index f98074ac5b..86723dc3e5 100644 --- a/public/src/modules/notifications.js +++ b/public/src/modules/notifications.js @@ -84,7 +84,11 @@ define('notifications', ['sounds', 'translator', 'components'], function(sound, payload.message = notifData.bodyShort; payload.type = 'info'; payload.clickfn = function() { - window.location.href = config.relative_path + '/' + notifData.path; + if (notifData.path.startsWith('http') && notifData.path.startsWith('https')) { + window.location.href = notifData.path; + } else { + window.location.href = window.location.protocol + '//' + window.location.host + config.relative_path + notifData.path; + } }; } else { payload.message = '[[notifications:you_have_unread_notifications]]'; From 2ea57e41104284106f1538db79e9d238ec87fb82 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 5 May 2016 23:03:21 +0300 Subject: [PATCH 43/62] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f8ce4227e..84e5e936ec 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "3.0.29", + "nodebb-plugin-composer-default": "3.0.30", "nodebb-plugin-dbsearch": "1.0.1", "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", From 4cdeae33e5f25f84108a7663680a513f84ac7513 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Thu, 5 May 2016 22:44:14 -0400 Subject: [PATCH 44/62] don't add "page-xxx-" class if trailing slash exists --- src/middleware/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/render.js b/src/middleware/render.js index 0dd0543600..aacc20c281 100644 --- a/src/middleware/render.js +++ b/src/middleware/render.js @@ -98,7 +98,7 @@ module.exports = function(middleware) { }; function buildBodyClass(req) { - var clean = req.path.replace(/^\/api/, '').replace(/^\//, ''); + var clean = req.path.replace(/^\/api/, '').replace(/^\/|\/$/g, ''); var parts = clean.split('/').slice(0, 3); parts.forEach(function(p, index) { parts[index] = index ? parts[0] + '-' + p : 'page-' + (p || 'home'); From 458d4996a62c4ff43c3108f7710e063f5214b3e5 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 6 May 2016 00:05:58 -0400 Subject: [PATCH 45/62] filter:categories.get --- src/controllers/categories.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 7e3e4b0efe..12c94954b2 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -73,7 +73,9 @@ categoriesController.list = function(req, res, next) { } }); - res.render('categories', data); + plugins.fireHook('filter:categories.get', data, function(err, data) { + res.render('categories', data); + }); }); }; From 0287703047ea134ff2c8a1261be0fae5b455aa1a Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 6 May 2016 00:07:05 -0400 Subject: [PATCH 46/62] missed plugins req --- src/controllers/categories.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 12c94954b2..7429cae666 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -6,6 +6,7 @@ var validator = require('validator'); var categories = require('../categories'); var meta = require('../meta'); +var plugins = require('../plugins'); var helpers = require('./helpers'); var categoriesController = {}; From 702597d759ba21e982e3cf42253112fd52a135c3 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 6 May 2016 09:41:30 +0300 Subject: [PATCH 47/62] change /user/uid to /uid/:uid so it doesn't conflict with actual user routes was causing incorrect redirects if a user had a numeric userslug @julianlam --- src/middleware/middleware.js | 6 ++++-- src/routes/accounts.js | 6 ++++-- src/socket.io/user.js | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index 1dd5eee7bc..bc07719ade 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -166,7 +166,7 @@ middleware.checkAccountPermissions = function(req, res, next) { }; middleware.redirectUidToUserslug = function(req, res, next) { - var uid = parseInt(req.params.userslug, 10); + var uid = parseInt(req.params.uid, 10); if (!uid) { return next(); } @@ -175,7 +175,9 @@ middleware.redirectUidToUserslug = function(req, res, next) { return next(err); } - var path = req.path.replace(/^\/api/, '').replace(uid, function() { return userslug; }); + var path = req.path.replace(/^\/api/, '') + .replace('uid', 'user') + .replace(uid, function() { return userslug; }); controllers.helpers.redirect(res, path); }); }; diff --git a/src/routes/accounts.js b/src/routes/accounts.js index 48d23b0ce0..9ee4b3af20 100644 --- a/src/routes/accounts.js +++ b/src/routes/accounts.js @@ -4,8 +4,10 @@ var helpers = require('./helpers'); var setupPageRoute = helpers.setupPageRoute; module.exports = function (app, middleware, controllers) { - var middlewares = [middleware.checkGlobalPrivacySettings, middleware.redirectUidToUserslug]; - var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, middleware.redirectUidToUserslug]; + var middlewares = [middleware.checkGlobalPrivacySettings]; + var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions]; + + setupPageRoute(app, '/uid/:uid/:section?', middleware, [], middleware.redirectUidToUserslug); setupPageRoute(app, '/user/:userslug', middleware, middlewares, controllers.accounts.profile.get); setupPageRoute(app, '/user/:userslug/following', middleware, middlewares, controllers.accounts.follow.getFollowing); diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 59672c006b..c38bcc5bbf 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -155,7 +155,7 @@ SocketUser.follow = function(socket, data, callback) { bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', nid: 'follow:' + data.uid + ':uid:' + socket.uid, from: socket.uid, - path: '/user/' + socket.uid, + path: '/uid/' + socket.uid, mergeId: 'notifications:user_started_following_you' }, next); }, From bafbcad163e0016fda40b8202c542fa5a89bb9b5 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 6 May 2016 03:54:13 -0400 Subject: [PATCH 48/62] use filter:categories.build instead of .get --- src/controllers/categories.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 7429cae666..7e3e4b0efe 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -6,7 +6,6 @@ var validator = require('validator'); var categories = require('../categories'); var meta = require('../meta'); -var plugins = require('../plugins'); var helpers = require('./helpers'); var categoriesController = {}; @@ -74,9 +73,7 @@ categoriesController.list = function(req, res, next) { } }); - plugins.fireHook('filter:categories.get', data, function(err, data) { - res.render('categories', data); - }); + res.render('categories', data); }); }; From d85a8d068d91009a1ee7e6f32e24e8fa028a8799 Mon Sep 17 00:00:00 2001 From: psychobunny Date: Fri, 6 May 2016 04:42:59 -0400 Subject: [PATCH 49/62] consolidate allowed image types in ACP --- src/controllers/admin/uploads.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/admin/uploads.js b/src/controllers/admin/uploads.js index aea79e3934..0affec8f6b 100644 --- a/src/controllers/admin/uploads.js +++ b/src/controllers/admin/uploads.js @@ -9,13 +9,13 @@ var fs = require('fs'), image = require('../../image'), plugins = require('../../plugins'); +var allowedImageTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif', 'image/svg+xml']; var uploadsController = {}; uploadsController.uploadCategoryPicture = function(req, res, next) { var uploadedFile = req.files.files[0]; - var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/svg+xml'], - params = null; + var params = null; try { params = JSON.parse(req.body.params); @@ -28,7 +28,7 @@ uploadsController.uploadCategoryPicture = function(req, res, next) { return next(e); } - if (validateUpload(req, res, next, uploadedFile, allowedTypes)) { + if (validateUpload(req, res, next, uploadedFile, allowedImageTypes)) { var filename = 'category-' + params.cid + path.extname(uploadedFile.name); uploadImage(filename, 'category', uploadedFile, req, res, next); } @@ -126,8 +126,8 @@ uploadsController.uploadDefaultAvatar = function(req, res, next) { function upload(name, req, res, next) { var uploadedFile = req.files.files[0]; - var allowedTypes = ['image/png', 'image/jpeg', 'image/pjpeg', 'image/jpg', 'image/gif']; - if (validateUpload(req, res, next, uploadedFile, allowedTypes)) { + + if (validateUpload(req, res, next, uploadedFile, allowedImageTypes)) { var filename = name + path.extname(uploadedFile.name); uploadImage(filename, 'system', uploadedFile, req, res, next); } From 1501eda31140d94ceb018c8b2d6461394ad0d0e6 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 6 May 2016 12:23:44 +0300 Subject: [PATCH 50/62] up mentions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84e5e936ec..2c7f00a2ea 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "nodebb-plugin-emoji-one": "1.1.3", "nodebb-plugin-emoji-extended": "1.1.0", "nodebb-plugin-markdown": "5.1.4", - "nodebb-plugin-mentions": "1.0.23", + "nodebb-plugin-mentions": "1.0.24", "nodebb-plugin-soundpack-default": "0.1.6", "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", From b446ff42c3a1bc26f49cffb32e2503a868e107ef Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 6 May 2016 13:47:10 +0300 Subject: [PATCH 51/62] style changes --- src/socket.io/helpers.js | 97 ++++++++++++++++++++++----------------- src/user/notifications.js | 37 +++++++++------ 2 files changed, 79 insertions(+), 55 deletions(-) diff --git a/src/socket.io/helpers.js b/src/socket.io/helpers.js index b39bf7360a..69a4a26b54 100644 --- a/src/socket.io/helpers.js +++ b/src/socket.io/helpers.js @@ -3,7 +3,6 @@ var async = require('async'); var winston = require('winston'); var S = require('string'); -var nconf = require('nconf'); var websockets = require('./index'); var user = require('../user'); @@ -53,24 +52,24 @@ SocketHelpers.sendNotificationToPostOwner = function(pid, fromuid, notification) if (!pid || !fromuid || !notification) { return; } - posts.getPostFields(pid, ['tid', 'uid', 'content'], function(err, postData) { - if (err) { - return; - } - - if (!postData.uid || fromuid === parseInt(postData.uid, 10)) { - return; - } - - async.parallel({ - username: async.apply(user.getUserField, fromuid, 'username'), - topicTitle: async.apply(topics.getTopicField, postData.tid, 'title'), - postObj: async.apply(posts.parsePost, postData) - }, function(err, results) { - if (err) { + fromuid = parseInt(fromuid, 10); + var postData; + async.waterfall([ + function (next) { + posts.getPostFields(pid, ['tid', 'uid', 'content'], next); + }, + function (_postData, next) { + postData = _postData; + if (!postData.uid || fromuid === parseInt(postData.uid, 10)) { return; } - + async.parallel({ + username: async.apply(user.getUserField, fromuid, 'username'), + topicTitle: async.apply(topics.getTopicField, postData.tid, 'title'), + postObj: async.apply(posts.parsePost, postData) + }, next); + }, + function (results, next) { var title = S(results.topicTitle).decodeHTMLEntities().s; var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); @@ -83,12 +82,15 @@ SocketHelpers.sendNotificationToPostOwner = function(pid, fromuid, notification) from: fromuid, mergeId: notification + '|' + pid, topicTitle: results.topicTitle - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, [postData.uid]); - } - }); - }); + }, next); + } + ], function(err, notification) { + if (err) { + return winston.error(err); + } + if (notification) { + notifications.push(notification, [postData.uid]); + } }); }; @@ -98,27 +100,38 @@ SocketHelpers.sendNotificationToTopicOwner = function(tid, fromuid, notification return; } - async.parallel({ - username: async.apply(user.getUserField, fromuid, 'username'), - topicData: async.apply(topics.getTopicFields, tid, ['uid', 'slug', 'title']), - }, function(err, results) { - if (err || fromuid === parseInt(results.topicData.uid, 10)) { - return; - } + fromuid = parseInt(fromuid, 10); - var title = S(results.topicData.title).decodeHTMLEntities().s; - var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); - - notifications.create({ - bodyShort: '[[' + notification + ', ' + results.username + ', ' + titleEscaped + ']]', - path: '/topic/' + results.topicData.slug, - nid: 'tid:' + tid + ':uid:' + fromuid, - from: fromuid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, [results.topicData.uid]); + var ownerUid; + async.waterfall([ + function (next) { + async.parallel({ + username: async.apply(user.getUserField, fromuid, 'username'), + topicData: async.apply(topics.getTopicFields, tid, ['uid', 'slug', 'title']), + }, next); + }, + function (results, next) { + if (fromuid === parseInt(results.topicData.uid, 10)) { + return; } - }); + ownerUid = results.topicData.uid; + var title = S(results.topicData.title).decodeHTMLEntities().s; + var titleEscaped = title.replace(/%/g, '%').replace(/,/g, ','); + + notifications.create({ + bodyShort: '[[' + notification + ', ' + results.username + ', ' + titleEscaped + ']]', + path: '/topic/' + results.topicData.slug, + nid: 'tid:' + tid + ':uid:' + fromuid, + from: fromuid + }, next); + } + ], function(err, notification) { + if (err) { + return winston.error(err); + } + if (notification && parseInt(ownerUid, 10)) { + notifications.push(notification, [ownerUid]); + } }); }; diff --git a/src/user/notifications.js b/src/user/notifications.js index 852f9f0924..b08cb6aaff 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -200,13 +200,20 @@ var privileges = require('../privileges'); }; UserNotifications.sendTopicNotificationToFollowers = function(uid, topicData, postData) { - db.getSortedSetRange('followers:' + uid, 0, -1, function(err, followers) { - if (err || !Array.isArray(followers) || !followers.length) { - return; - } - - privileges.categories.filterUids('read', topicData.cid, followers, function(err, followers) { - if (err || !followers.length) { + var followers; + async.waterfall([ + function (next) { + db.getSortedSetRange('followers:' + uid, 0, -1, next); + }, + function (followers, next) { + if (!Array.isArray(followers) || !followers.length) { + return; + } + privileges.categories.filterUids('read', topicData.cid, followers, next); + }, + function (_followers, next) { + followers = _followers; + if (!followers.length) { return; } @@ -223,12 +230,16 @@ var privileges = require('../privileges'); nid: 'tid:' + postData.tid + ':uid:' + uid, tid: postData.tid, from: uid - }, function(err, notification) { - if (!err && notification) { - notifications.push(notification, followers); - } - }); - }); + }, next); + } + ], function(err, notification) { + if (err) { + return winston.error(err); + } + + if (notification) { + notifications.push(notification, followers); + } }); }; From a919d40e51a398f7d5e4b4a824010b53782658d6 Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Fri, 6 May 2016 09:02:30 -0400 Subject: [PATCH 52/62] Latest translations and fallbacks --- public/language/zh_TW/category.json | 10 ++++----- public/language/zh_TW/email.json | 18 +++++++-------- public/language/zh_TW/global.json | 34 ++++++++++++++--------------- public/language/zh_TW/register.json | 2 +- public/language/zh_TW/tags.json | 2 +- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/public/language/zh_TW/category.json b/public/language/zh_TW/category.json index c1ab395ea5..341f400cd8 100644 --- a/public/language/zh_TW/category.json +++ b/public/language/zh_TW/category.json @@ -1,16 +1,16 @@ { - "category": "Category", - "subcategories": "Subcategories", + "category": "類別", + "subcategories": "子類別", "new_topic_button": "新主題", - "guest-login-post": "登錄後才能發表", + "guest-login-post": "登入後才能張貼", "no_topics": "這個類別還沒有任何主題。
為何不來發點東西呢?", "browsing": "正在瀏覽", "no_replies": "還沒有回覆", - "no_new_posts": "No new posts.", + "no_new_posts": "沒有新的張貼", "share_this_category": "分享這類別", "watch": "觀看", "ignore": "忽略", "watch.message": "您正觀看著此類別的更新", "ignore.message": "您已忽略此類別的更新", - "watched-categories": "Watched categories" + "watched-categories": "看過的類別" } \ No newline at end of file diff --git a/public/language/zh_TW/email.json b/public/language/zh_TW/email.json index 8361ff52d0..6fb33ade9c 100644 --- a/public/language/zh_TW/email.json +++ b/public/language/zh_TW/email.json @@ -1,15 +1,15 @@ { "password-reset-requested": "已要求重設密碼 - %1!", "welcome-to": "歡迎來到%1", - "invite": "Invitation from %1", + "invite": "邀請來自 %1", "greeting_no_name": "您好", "greeting_with_name": "您好,%1", - "welcome.text1": "多謝登記%1!", + "welcome.text1": "感謝您註冊 %1!", "welcome.text2": "要啟用你的帳戶,我們先要驗證你用作登記的電郵地址", - "welcome.text3": "An administrator has accepted your registration application. You can login with your username/password now.", + "welcome.text3": "管理者已經批準您的註冊申請。你現在可以使用你的帳號/密碼進行登入。", "welcome.cta": "請點擊此處確認您的電郵地址", - "invitation.text1": "%1 has invited you to join %2", - "invitation.ctr": "Click here to create your account.", + "invitation.text1": "%1 邀請您加入 %2", + "invitation.ctr": "點擊這裡來建立你的帳號", "reset.text1": "我們收到一個重設密碼的請求,你忘掉了密碼嗎?如果不是,請忽略這封郵件。", "reset.text2": "要繼續重置密碼,請點擊以下鏈接:", "reset.cta": "點擊這裡重置密碼", @@ -21,10 +21,10 @@ "digest.cta": "點擊這裡訪問%1", "digest.unsub.info": "本摘要按您的訂閱設置發送給您。", "digest.no_topics": "在過去%1沒有活躍的話題", - "digest.day": "day", - "digest.week": "week", - "digest.month": "month", - "digest.subject": "Digest for %1", + "digest.day": "日", + "digest.week": "週", + "digest.month": "月", + "digest.subject": "摘要於 %1", "notif.chat.subject": "收到來自$1的聊天消息", "notif.chat.cta": "點擊此處繼續對話", "notif.chat.unsub.info": "本聊天通知按您的訂閱設置發送給您。", diff --git a/public/language/zh_TW/global.json b/public/language/zh_TW/global.json index 10e4169e07..57791619b2 100644 --- a/public/language/zh_TW/global.json +++ b/public/language/zh_TW/global.json @@ -22,7 +22,7 @@ "pagination.out_of": "%1 out of %2", "pagination.enter_index": "輸入Index", "header.admin": "管理", - "header.categories": "Categories", + "header.categories": "類別", "header.recent": "最近", "header.unread": "未讀", "header.tags": "標籤", @@ -33,7 +33,7 @@ "header.notifications": "通知", "header.search": "搜尋", "header.profile": "設置", - "header.navigation": "Navigation", + "header.navigation": "導覽", "notifications.loading": "消息載入中", "chats.loading": "聊天載入中···", "motd.welcome": "歡迎來到 NodeBB,一個未來的討論平台。", @@ -49,29 +49,29 @@ "users": "使用者", "topics": "主題", "posts": "文章", - "best": "Best", - "upvoted": "Upvoted", - "downvoted": "Downvoted", + "best": "最棒", + "upvoted": "讚", + "downvoted": "爛", "views": "Views", "reputation": "聲譽", "read_more": "閱讀更多...", - "more": "More", + "more": "更多", "posted_ago_by_guest": "posted %1 by Guest", "posted_ago_by": "posted %1 by %2", "posted_ago": "posted %1", - "posted_in": "posted in %1", - "posted_in_by": "posted in %1 by %2", + "posted_in": "張貼於 %1", + "posted_in_by": "張貼於 %1 由 %2", "posted_in_ago": "posted in %1", "posted_in_ago_by": "posted in %1 %2 by %3", "user_posted_ago": "%1 posted %2", "guest_posted_ago": "Guest posted %1", - "last_edited_by": "last edited by %1", + "last_edited_by": "最後編輯由 %1", "norecentposts": "最近沒新文章", "norecenttopics": "最近沒新主題", "recentposts": "最近的文章", "recentips": "最近登錄的 IP 來源位址", "away": "離開", - "dnd": "Do not disturb", + "dnd": "不要打擾", "invisible": "隱藏", "offline": "離線", "email": "Email", @@ -84,11 +84,11 @@ "follow": "追蹤", "unfollow": "取消追蹤", "delete_all": "全部刪除", - "map": "Map", - "sessions": "Login Sessions", - "ip_address": "IP Address", - "enter_page_number": "Enter page number", - "upload_file": "Upload file", - "upload": "Upload", - "allowed-file-types": "Allowed file types are %1" + "map": "地圖", + "sessions": "登入連線階段", + "ip_address": "IP地址", + "enter_page_number": "輸入頁碼", + "upload_file": "上傳檔案", + "upload": "上傳", + "allowed-file-types": "允許的檔案類型是 %1" } \ No newline at end of file diff --git a/public/language/zh_TW/register.json b/public/language/zh_TW/register.json index e690779ae8..d61934f70d 100644 --- a/public/language/zh_TW/register.json +++ b/public/language/zh_TW/register.json @@ -15,5 +15,5 @@ "alternative_registration": "其他註冊方式", "terms_of_use": "使用條款", "agree_to_terms_of_use": "同意遵守使用條款", - "registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator." + "registration-added-to-queue": "您的註冊已經被加入到審核序列中。您將會在管理者批準後收到一封電子郵件。" } \ No newline at end of file diff --git a/public/language/zh_TW/tags.json b/public/language/zh_TW/tags.json index ce9de5c88b..8b2046b293 100644 --- a/public/language/zh_TW/tags.json +++ b/public/language/zh_TW/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "沒有此標籤的主題。", "tags": "標籤", - "enter_tags_here": "Enter tags here, between %1 and %2 characters each.", + "enter_tags_here": "在這裡輸入標籤,每個介於 %1 到 %2 字元。 ", "enter_tags_here_short": "輸入標籤...", "no_tags": "還沒有標籤呢。" } \ No newline at end of file From a1d2fbefe7d8ec59f9b5b26b560cf7e7702a928e Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Sat, 7 May 2016 09:02:27 -0400 Subject: [PATCH 53/62] Latest translations and fallbacks --- public/language/zh_TW/error.json | 54 +++++++++++------------ public/language/zh_TW/groups.json | 44 +++++++++--------- public/language/zh_TW/modules.json | 36 +++++++-------- public/language/zh_TW/pages.json | 40 ++++++++--------- public/language/zh_TW/recent.json | 16 +++---- public/language/zh_TW/reset_password.json | 6 +-- public/language/zh_TW/search.json | 2 +- public/language/zh_TW/topic.json | 34 +++++++------- public/language/zh_TW/unread.json | 8 ++-- public/language/zh_TW/uploads.json | 8 ++-- public/language/zh_TW/users.json | 20 ++++----- 11 files changed, 134 insertions(+), 134 deletions(-) diff --git a/public/language/zh_TW/error.json b/public/language/zh_TW/error.json index 5deae59892..0376a3c3de 100644 --- a/public/language/zh_TW/error.json +++ b/public/language/zh_TW/error.json @@ -2,7 +2,7 @@ "invalid-data": "無效的資料", "not-logged-in": "您似乎還沒有登入喔!", "account-locked": "您的帳戶暫時被鎖定!", - "search-requires-login": "Searching requires an account - please login or register.", + "search-requires-login": "進行搜尋需要有帳號 - 請先註冊或登入", "invalid-cid": "無效的類別 ID", "invalid-tid": "無效的主題 ID", "invalid-pid": "無效的文章 ID", @@ -14,17 +14,17 @@ "invalid-password": "無效的密碼", "invalid-username-or-password": "請指定用戶名和密碼", "invalid-search-term": "無效的搜索字詞", - "invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2", + "invalid-pagination-value": "無效的分頁數值, 必需是至少 %1 與最多 %2", "username-taken": "該使用者名稱已被使用", "email-taken": "該信箱已被使用", "email-not-confirmed": "您的電郵尚未得到確認,請點擊此處確認您的電子郵件。", "email-not-confirmed-chat": "You are unable to chat until your email is confirmed, please click here to confirm your email.", "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "email-confirm-failed": "我們無法確認你的Email,請之後再重試。", "confirm-email-already-sent": "Confirmation email already sent, please wait %1 minute(s) to send another one.", - "username-too-short": "使用者名稱太簡短", - "username-too-long": "使用者名稱太長", - "password-too-long": "Password too long", + "username-too-short": "帳號太短", + "username-too-long": "帳號太長", + "password-too-long": "密碼太長", "user-banned": "該使用者已被停用", "user-too-new": "抱歉,發表您第一篇文章須要等待 %1 秒", "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", @@ -50,9 +50,9 @@ "too-many-tags": "Too many tags. Topics can't have more than %1 tag(s)", "still-uploading": "請等待上傳完成。", "file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file", - "guest-upload-disabled": "Guest uploading has been disabled", - "already-favourited": "You have already bookmarked this post", - "already-unfavourited": "You have already unbookmarked this post", + "guest-upload-disabled": "訪客上傳是被禁止的", + "already-favourited": "你已經將這篇張貼加入書籤", + "already-unfavourited": "你已經將這篇張貼移除書籤", "cant-ban-other-admins": "您無法禁止其他的管理員!", "cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin", "invalid-image-type": "無效的圖像類型。允許的類型:%1", @@ -61,11 +61,11 @@ "group-name-too-short": "群組名稱太短了", "group-already-exists": "群組名稱已存在", "group-name-change-not-allowed": "變更群組名稱不被允許", - "group-already-member": "Already part of this group", - "group-not-member": "Not a member of this group", - "group-needs-owner": "This group requires at least one owner", - "group-already-invited": "This user has already been invited", - "group-already-requested": "Your membership request has already been submitted", + "group-already-member": "已經加入這個群組", + "group-not-member": "不是這個群組的一員", + "group-needs-owner": "這個群組需要至少一個擁有者", + "group-already-invited": "這個使用者已經被邀請", + "group-already-requested": "你的會員申請已經被提交", "post-already-deleted": "此文章已經被刪除", "post-already-restored": "此文章已還原", "topic-already-deleted": "此主題已經被刪除", @@ -78,27 +78,27 @@ "about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).", "cant-chat-with-yourself": "你不能與自己聊天!", "chat-restricted": "此用戶已限制了他的聊天功能。你要在他關注你之後,才能跟他聊天", - "chat-disabled": "Chat system disabled", + "chat-disabled": "聊天系統被禁止", "too-many-messages": "You have sent too many messages, please wait awhile.", - "invalid-chat-message": "Invalid chat message", - "chat-message-too-long": "Chat message is too long", - "cant-edit-chat-message": "You are not allowed to edit this message", - "cant-remove-last-user": "You can't remove the last user", - "cant-delete-chat-message": "You are not allowed to delete this message", - "already-voting-for-this-post": "You have already voted for this post.", + "invalid-chat-message": "無效的聊天訊息", + "chat-message-too-long": "聊天訊息太長", + "cant-edit-chat-message": "你不被允許編輯這條訊息", + "cant-remove-last-user": "你不能移除最後的使用者", + "cant-delete-chat-message": "你不被允許刪除這條訊息", + "already-voting-for-this-post": "你已經對這個張貼投過票了", "reputation-system-disabled": "信譽系統已停用。", "downvoting-disabled": "Downvoting已停用", "not-enough-reputation-to-downvote": "你沒有足夠的信譽downvote這個帖子", "not-enough-reputation-to-flag": "你沒有足夠的信譽來舉報這個帖子", - "already-flagged": "You have already flagged this post", + "already-flagged": "你已經對這個張貼標記過了", "reload-failed": "NodeBB重載\"%1\"時遇到了問題。 NodeBB將繼續提供現有的客戶端資源,但請你撤消重載前的動作。", "registration-error": "註冊錯誤", - "parse-error": "Something went wrong while parsing server response", + "parse-error": "當剖析伺服器回應時發生了某個錯誤", "wrong-login-type-email": "請使用您的電子郵件進行登錄", "wrong-login-type-username": "請使用您的使用者名稱進行登錄", "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).", - "no-session-found": "No login session found!", - "not-in-room": "User not in room", - "no-users-in-room": "No users in this room", - "cant-kick-self": "You can't kick yourself from the group" + "no-session-found": "沒有找到登入的連線階段!", + "not-in-room": "使用者沒有在聊天室中", + "no-users-in-room": "沒有使用者在聊天室中", + "cant-kick-self": "你不能把自己從群組中踢出" } \ No newline at end of file diff --git a/public/language/zh_TW/groups.json b/public/language/zh_TW/groups.json index d562fb5ccd..511331e301 100644 --- a/public/language/zh_TW/groups.json +++ b/public/language/zh_TW/groups.json @@ -6,25 +6,25 @@ "no_groups_found": "這裡看不到任何群組", "pending.accept": "接受", "pending.reject": "拒絕", - "pending.accept_all": "Accept All", - "pending.reject_all": "Reject All", - "pending.none": "There are no pending members at this time", - "invited.none": "There are no invited members at this time", - "invited.uninvite": "Rescind Invitation", - "invited.search": "Search for a user to invite to this group", - "invited.notification_title": "You have been invited to join %1", - "request.notification_title": "Group Membership Request from %1", - "request.notification_text": "%1 has requested to become a member of %2", + "pending.accept_all": "同意所有", + "pending.reject_all": "拒絕所有", + "pending.none": "目前沒有等待中的會員", + "invited.none": "目前沒有邀請的會員", + "invited.uninvite": "撤銷邀請", + "invited.search": "搜尋要邀請加入這個群組的用戶", + "invited.notification_title": "你已被邀請加入%1", + "request.notification_title": "群組會員要求,來自%1", + "request.notification_text": "%1已經要求成為%2群組的會員", "cover-save": "儲存", "cover-saving": "儲存中", "details.title": "群組詳細信息", "details.members": "成員列表", "details.pending": "待審成員", - "details.invited": "Invited Members", + "details.invited": "邀請會員", "details.has_no_posts": "這個群組的成員還未發出任何帖子。", "details.latest_posts": "最新文章", "details.private": "私人", - "details.disableJoinRequests": "Disable join requests", + "details.disableJoinRequests": "禁止加入要求", "details.grant": "准許/撤銷 所有權", "details.kick": "剔除", "details.owner_options": "群組管理員", @@ -37,18 +37,18 @@ "details.change_colour": "變更顏色", "details.badge_text": "徽章字串", "details.userTitleEnabled": "顯示徽章", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", + "details.private_help": "如果開啟,加入群組需要經過群組擁有者批準", "details.hidden": "隱藏", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "details.delete_group": "Delete Group", - "details.private_system_help": "Private groups is disabled at system level, this option does not do anything", + "details.hidden_help": "如果開啟的話,群組將不會在群組列表中被看到,而且用戶將需要手動邀請", + "details.delete_group": "刪除群組", + "details.private_system_help": "私有群組在系統層級被禁用,這個選項沒有任何作用", "event.updated": "群組詳細訊息已被更新", "event.deleted": "此 \"%1\" 群組已被刪除了", - "membership.accept-invitation": "Accept Invitation", - "membership.invitation-pending": "Invitation Pending", - "membership.join-group": "Join Group", - "membership.leave-group": "Leave Group", - "membership.reject": "Reject", - "new-group.group_name": "Group Name:", - "upload-group-cover": "Upload group cover" + "membership.accept-invitation": "同意邀請", + "membership.invitation-pending": "邀請等待中", + "membership.join-group": "加入群組", + "membership.leave-group": "離開群組", + "membership.reject": "拒絕", + "new-group.group_name": "群組名稱:", + "upload-group-cover": "上傳群組封面圖" } \ No newline at end of file diff --git a/public/language/zh_TW/modules.json b/public/language/zh_TW/modules.json index c34132769a..109139f9c2 100644 --- a/public/language/zh_TW/modules.json +++ b/public/language/zh_TW/modules.json @@ -5,10 +5,10 @@ "chat.no_active": "暫無聊天", "chat.user_typing": "%1 正在輸入中...", "chat.user_has_messaged_you": "%1 已傳送訊息給你了", - "chat.see_all": "See all chats", - "chat.mark_all_read": "Mark all chats read", + "chat.see_all": "顯示全部聊天", + "chat.mark_all_read": "所有訊息標為已讀", "chat.no-messages": "請選擇收件人來查看聊天記錄", - "chat.no-users-in-room": "No users in this room", + "chat.no-users-in-room": "沒有用戶在聊天室中", "chat.recent-chats": "最近的聊天記錄", "chat.contacts": "通訊錄", "chat.message-history": "消息記錄", @@ -17,22 +17,22 @@ "chat.seven_days": "7日", "chat.thirty_days": "30日", "chat.three_months": "3個月", - "chat.delete_message_confirm": "Are you sure you wish to delete this message?", - "chat.roomname": "Chat Room %1", - "chat.add-users-to-room": "Add users to room", - "composer.compose": "Compose", - "composer.show_preview": "Show Preview", - "composer.hide_preview": "Hide Preview", + "chat.delete_message_confirm": "你確定要刪除這個訊息?", + "chat.roomname": "聊天室 %1", + "chat.add-users-to-room": "將用戶加入聊天室中", + "composer.compose": "撰寫", + "composer.show_preview": "顯示預覽", + "composer.hide_preview": "隱藏預覽", "composer.user_said_in": "%1在%2裡說:", "composer.user_said": "%1說:", "composer.discard": "你確定要放棄這帖子嗎?", - "composer.submit_and_lock": "Submit and Lock", - "composer.toggle_dropdown": "Toggle Dropdown", - "composer.uploading": "Uploading %1", - "bootbox.ok": "OK", - "bootbox.cancel": "Cancel", - "bootbox.confirm": "Confirm", - "cover.dragging_title": "Cover Photo Positioning", - "cover.dragging_message": "Drag the cover photo to the desired position and click \"Save\"", - "cover.saved": "Cover photo image and position saved" + "composer.submit_and_lock": "提交然後鎖定", + "composer.toggle_dropdown": "切換下拉選單", + "composer.uploading": "上傳中 %1", + "bootbox.ok": "好", + "bootbox.cancel": "取消", + "bootbox.confirm": "確認", + "cover.dragging_title": "封面照片位置", + "cover.dragging_message": "拖拉封面照片到想要的位置,然後按下\"儲存\"", + "cover.saved": "封面照片與位置已儲存" } \ No newline at end of file diff --git a/public/language/zh_TW/pages.json b/public/language/zh_TW/pages.json index 121be06593..431b71a11d 100644 --- a/public/language/zh_TW/pages.json +++ b/public/language/zh_TW/pages.json @@ -1,30 +1,30 @@ { "home": "首頁", "unread": "未讀的主題", - "popular-day": "Popular topics today", - "popular-week": "Popular topics this week", - "popular-month": "Popular topics this month", - "popular-alltime": "All time popular topics", + "popular-day": "今天受歡迎的主題", + "popular-week": "本週受歡迎的主題", + "popular-month": "本月受歡迎的主題", + "popular-alltime": "所有時間受歡迎的主題", "recent": "近期的主題", - "flagged-posts": "Flagged Posts", - "users/online": "Online Users", - "users/latest": "Latest Users", + "flagged-posts": "標記的張貼", + "users/online": "線上用戶", + "users/latest": "最近用戶", "users/sort-posts": "Users with the most posts", "users/sort-reputation": "Users with the most reputation", - "users/banned": "Banned Users", - "users/search": "User Search", + "users/banned": "已封鎖用戶", + "users/search": "用戶搜尋", "notifications": "新訊息通知", "tags": "標籤", "tag": "Topics tagged under \"%1\"", - "register": "Register an account", - "login": "Login to your account", + "register": "註冊帳號", + "login": "登入帳號", "reset": "Reset your account password", - "categories": "Categories", - "groups": "Groups", - "group": "%1 group", - "chats": "Chats", - "chat": "Chatting with %1", - "account/edit": "Editing \"%1\"", + "categories": "類別", + "groups": "群組", + "group": "%1 群組", + "chats": "聊天", + "chat": "與 %1 聊天", + "account/edit": "編輯 \"%1\"", "account/edit/password": "Editing password of \"%1\"", "account/edit/username": "Editing username of \"%1\"", "account/edit/email": "Editing email of \"%1\"", @@ -32,14 +32,14 @@ "account/followers": "People who follow %1", "account/posts": "Posts made by %1", "account/topics": "Topics created by %1", - "account/groups": "%1's Groups", + "account/groups": "%1 的群組", "account/favourites": "%1's Bookmarked Posts", - "account/settings": "User Settings", + "account/settings": "用戶設定", "account/watched": "Topics watched by %1", "account/upvoted": "Posts upvoted by %1", "account/downvoted": "Posts downvoted by %1", "account/best": "Best posts made by %1", - "confirm": "Email Confirmed", + "confirm": "已確認電子郵件", "maintenance.text": "目前 %1 正在進行維修。請稍後再來。", "maintenance.messageIntro": "此外,管理員有以下訊息:", "throttled.text": "%1 is currently unavailable due to excessive load. Please come back another time." diff --git a/public/language/zh_TW/recent.json b/public/language/zh_TW/recent.json index cf46a366d3..6076b0acb6 100644 --- a/public/language/zh_TW/recent.json +++ b/public/language/zh_TW/recent.json @@ -7,13 +7,13 @@ "alltime": "所有時間", "no_recent_topics": "最近沒有新的主題。", "no_popular_topics": "最近沒有受歡迎的主題。", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", + "there-is-a-new-topic": "有一篇新主題", + "there-is-a-new-topic-and-a-new-post": "有一篇新主題與一篇新張貼", + "there-is-a-new-topic-and-new-posts": "有一篇新主題與 %1 篇新張貼", + "there-are-new-topics": "有 %1 篇新主題", + "there-are-new-topics-and-a-new-post": "有 %1 篇新主題與一篇新張貼", + "there-are-new-topics-and-new-posts": "有 %1 篇新主題與 %2 篇新張貼", + "there-is-a-new-post": "有一篇新張貼", + "there-are-new-posts": "有 %1 篇新張貼", "click-here-to-reload": "點擊這裡進行重整。" } \ No newline at end of file diff --git a/public/language/zh_TW/reset_password.json b/public/language/zh_TW/reset_password.json index 5c2964579c..85ec4482e8 100644 --- a/public/language/zh_TW/reset_password.json +++ b/public/language/zh_TW/reset_password.json @@ -11,7 +11,7 @@ "enter_email_address": "輸入電子郵件地址", "password_reset_sent": "密碼重設郵件已發送。", "invalid_email": "無效的電子郵件 / 電子郵件不存在!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match.", - "password_expired": "Your password has expired, please choose a new password" + "password_too_short": "輸入的密碼太短,請使用另一個不同的密碼", + "passwords_do_not_match": "你已經輸入的兩個密碼不一樣", + "password_expired": "你的密碼已過期,請選擇一組新密碼" } \ No newline at end of file diff --git a/public/language/zh_TW/search.json b/public/language/zh_TW/search.json index 9eadd12f6d..457f1bfb7b 100644 --- a/public/language/zh_TW/search.json +++ b/public/language/zh_TW/search.json @@ -1,7 +1,7 @@ { "results_matching": "有%1個跟\"%2\"相符的結果(%3秒)", "no-matches": "沒有找到相符的主題", - "advanced-search": "Advanced Search", + "advanced-search": "進階搜尋", "in": "在", "titles": "標題", "titles-posts": "標題與發布", diff --git a/public/language/zh_TW/topic.json b/public/language/zh_TW/topic.json index 4b344c6a31..e5718a0040 100644 --- a/public/language/zh_TW/topic.json +++ b/public/language/zh_TW/topic.json @@ -13,7 +13,7 @@ "notify_me": "該主題有新回覆時通知我", "quote": "引用", "reply": "回覆", - "reply-as-topic": "Reply as topic", + "reply-as-topic": "回復為另一個新主題", "guest-login-reply": "登入以回覆", "edit": "編輯", "delete": "刪除", @@ -26,7 +26,7 @@ "tools": "工具", "flag": "檢舉", "locked": "已鎖定", - "bookmark_instructions": "Click here to return to the last read post in this thread.", + "bookmark_instructions": "點擊這裡返回到這個討論串的最後一篇張貼文", "flag_title": "檢舉這篇文章, 交給仲裁者來審閱.", "flag_success": "這文章已經被檢舉要求仲裁.", "deleted_message": "此主題已被刪除。只有具有主題管理權限的用戶才能看到它。", @@ -34,8 +34,8 @@ "not_following_topic.message": "有人貼文回覆主題時, 你將不會收到通知.", "login_to_subscribe": "請先註冊或登錄, 才可訂閱此主題.", "markAsUnreadForAll.success": "將全部的主題設為未讀.", - "mark_unread": "Mark unread", - "mark_unread.success": "Topic marked as unread.", + "mark_unread": "標為未讀", + "mark_unread.success": "標記主題為未讀", "watch": "關注", "unwatch": "取消關注", "watch.title": "當主題有新回覆時將收到通知", @@ -51,7 +51,7 @@ "thread_tools.move_all": "移動全部", "thread_tools.fork": "Fork 主題", "thread_tools.delete": "刪除主題", - "thread_tools.delete-posts": "Delete Posts", + "thread_tools.delete-posts": "刪除張貼", "thread_tools.delete_confirm": "你確定要刪除這個主題?", "thread_tools.restore": "還原刪除的主題", "thread_tools.restore_confirm": "你確定你要恢復這個主題嗎?", @@ -65,9 +65,9 @@ "disabled_categories_note": "停用的版面為灰色", "confirm_move": "移動", "confirm_fork": "作為主題", - "favourite": "Bookmark", - "favourites": "Bookmarks", - "favourites.has_no_favourites": "You haven't bookmarked any posts yet.", + "favourite": "書籤", + "favourites": "書籤", + "favourites.has_no_favourites": "你尚未將任何張貼加入書籤", "loading_more_posts": "載入更多文章", "move_topic": "移動主題", "move_topics": "移動主題", @@ -78,7 +78,7 @@ "fork_topic_instruction": "點擊要作為主題的文章", "fork_no_pids": "尚未選擇文章!", "fork_success": "成功分叉成新的主題!點擊這裡進入新的主題。", - "delete_posts_instruction": "Click the posts you want to delete/purge", + "delete_posts_instruction": "點擊你想要刪除/清除的張貼", "composer.title_placeholder": "輸入標題...", "composer.handle_placeholder": "名字", "composer.discard": "放棄", @@ -101,12 +101,12 @@ "newest_to_oldest": "從新到舊", "most_votes": "得票最多", "most_posts": "最多post", - "stale.title": "Create new topic instead?", - "stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?", - "stale.create": "Create a new topic", - "stale.reply_anyway": "Reply to this topic anyway", - "link_back": "Re: [%1](%2)", - "spam": "Spam", - "offensive": "Offensive", - "custom-flag-reason": "Enter a flagging reason" + "stale.title": "改為建立新的主題?", + "stale.warning": "你正回覆的主題是非常舊的一篇。你想要改為建立一個新主題,然後參考到這篇你回覆的?", + "stale.create": "建立新主題", + "stale.reply_anyway": "無論如何都回覆這個主題", + "link_back": "回覆: [%1](%2)", + "spam": "灌水", + "offensive": "攻擊", + "custom-flag-reason": "輸入標記的理由" } \ No newline at end of file diff --git a/public/language/zh_TW/unread.json b/public/language/zh_TW/unread.json index cf5b8d662c..b11d2ee762 100644 --- a/public/language/zh_TW/unread.json +++ b/public/language/zh_TW/unread.json @@ -5,9 +5,9 @@ "mark_as_read": "標記成已讀", "selected": "已選擇", "all": "全部", - "all_categories": "All categories", + "all_categories": "所有類別", "topics_marked_as_read.success": "標記主題成已讀!", - "all-topics": "All Topics", - "new-topics": "New Topics", - "watched-topics": "Watched Topics" + "all-topics": "所有主題", + "new-topics": "新主題", + "watched-topics": "已觀看主題" } \ No newline at end of file diff --git a/public/language/zh_TW/uploads.json b/public/language/zh_TW/uploads.json index 1622cb5693..aaef40f16c 100644 --- a/public/language/zh_TW/uploads.json +++ b/public/language/zh_TW/uploads.json @@ -1,6 +1,6 @@ { - "uploading-file": "Uploading the file...", - "select-file-to-upload": "Select a file to upload!", - "upload-success": "File uploaded successfully!", - "maximum-file-size": "Maximum %1 kb" + "uploading-file": "檔案上傳中...", + "select-file-to-upload": "選擇要上傳的檔案!", + "upload-success": "檔案已成功上傳!", + "maximum-file-size": "最大大小 %1 kb" } \ No newline at end of file diff --git a/public/language/zh_TW/users.json b/public/language/zh_TW/users.json index 9d42c05416..b3e5acd9cb 100644 --- a/public/language/zh_TW/users.json +++ b/public/language/zh_TW/users.json @@ -6,15 +6,15 @@ "enter_username": "輸入想找的使用者帳號", "load_more": "載入更多", "users-found-search-took": "發現 %1 用戶!搜尋只用 %2 秒。", - "filter-by": "Filter By", + "filter-by": "過濾依照", "online-only": "線上僅有", - "invite": "Invite", - "invitation-email-sent": "An invitation email has been sent to %1", - "user_list": "User List", - "recent_topics": "Recent Topics", - "popular_topics": "Popular Topics", - "unread_topics": "Unread Topics", - "categories": "Categories", - "tags": "Tags", - "no-users-found": "No users found!" + "invite": "邀請", + "invitation-email-sent": "所有邀請Email已經被寄送到 %1", + "user_list": "用戶列表", + "recent_topics": "最新的主題", + "popular_topics": "受歡迎的主題", + "unread_topics": "未讀的主題", + "categories": "類別", + "tags": "標籤", + "no-users-found": "沒有找到用戶!" } \ No newline at end of file From 1f70f886b5d4f3f1e2ab16a107d62dbe270ac70b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 8 May 2016 20:05:16 -0400 Subject: [PATCH 54/62] fixed reference to undefined variable /cc @BenLubar --- src/socket.io/posts/flag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.io/posts/flag.js b/src/socket.io/posts/flag.js index e9d378d2be..dbb3f7f551 100644 --- a/src/socket.io/posts/flag.js +++ b/src/socket.io/posts/flag.js @@ -90,7 +90,7 @@ module.exports = function(SocketPosts) { bodyShort: '[[notifications:user_flagged_post_in, ' + flaggingUser.username + ', ' + titleEscaped + ']]', bodyLong: post.content, pid: data.pid, - path: '/post/' + pid, + path: '/post/' + data.pid, nid: 'post_flag:' + data.pid + ':uid:' + socket.uid, from: socket.uid, mergeId: 'notifications:user_flagged_post_in|' + data.pid, From 39d9be787edaf2895d0635ba4adb2a0a3d870d40 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 May 2016 10:22:37 -0400 Subject: [PATCH 55/62] fixes #4614 --- public/src/modules/uploader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/src/modules/uploader.js b/public/src/modules/uploader.js index 6a0d7f161a..dc17484452 100644 --- a/public/src/modules/uploader.js +++ b/public/src/modules/uploader.js @@ -23,7 +23,7 @@ define('uploader', ['csrf', 'translator'], function(csrf, translator) { title: data.title || '[[global:upload_file]]', description: data.description || '', button: data.button || '[[global:upload]]', - accept: data.accept ? data.accept.replace(/,/g, ',') : '' + accept: data.accept ? data.accept.replace(/,/g, ', ') : '' }, function(uploadModal) { uploadModal = $(uploadModal); From f08a9c4def241787ef4904a69b6665262c0f5bfa Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 May 2016 10:31:24 -0400 Subject: [PATCH 56/62] fixes #4613 --- package.json | 4 ++-- public/src/client/topic/threadTools.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2c7f00a2ea..4d9dbb5d47 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,8 @@ "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", "nodebb-theme-lavender": "3.0.10", - "nodebb-theme-persona": "4.0.130", - "nodebb-theme-vanilla": "5.0.70", + "nodebb-theme-persona": "4.0.131", + "nodebb-theme-vanilla": "5.0.71", "nodebb-widget-essentials": "2.0.9", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index 7b5b9605f7..433c12e575 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -162,7 +162,7 @@ define('forum/topic/threadTools', [ components.get('topic/lock').toggleClass('hidden', data.isLocked); components.get('topic/unlock').toggleClass('hidden', !data.isLocked); - components.get('topic/reply').toggleClass('hidden', isLocked); + components.get('topic/reply/container').toggleClass('hidden', isLocked); components.get('topic/reply/locked').toggleClass('hidden', !isLocked); threadEl.find('[component="post/reply"], [component="post/quote"], [component="post/edit"], [component="post/delete"]').toggleClass('hidden', isLocked); From a507768a8fc41577c94a71ac36b523d32f39a1a0 Mon Sep 17 00:00:00 2001 From: pichalite Date: Mon, 9 May 2016 07:59:47 -0700 Subject: [PATCH 57/62] fix ACP->General->Sounds layout on mobile (#4606) * fix ACP->General->Sounds layout on mobile * just to make @juluanlam happy --- src/views/admin/general/sounds.tpl | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/views/admin/general/sounds.tpl b/src/views/admin/general/sounds.tpl index dfdbae512d..6fcd83bfe7 100644 --- a/src/views/admin/general/sounds.tpl +++ b/src/views/admin/general/sounds.tpl @@ -1,5 +1,5 @@
-
+
Notifications
@@ -53,22 +53,17 @@
+ +
+ + + +
-
-
-
-
- - - -
-
-
-
+ @@ -35,7 +35,7 @@
- +
@@ -50,7 +50,7 @@
- +
From d8c21cc09df820d323c9d9740f40808ad01fe235 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 May 2016 11:40:42 -0400 Subject: [PATCH 59/62] fixes #4593 --- package.json | 2 +- public/language/en_GB/error.json | 1 + public/language/en_GB/login.json | 2 +- public/src/client/login.js | 10 +++++++--- public/src/client/register.js | 10 +++++++--- src/controllers/index.js | 14 ++++++++++++-- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 0ab698c631..34bbfc76cd 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "nodebb-plugin-spam-be-gone": "0.4.6", "nodebb-rewards-essentials": "0.0.8", "nodebb-theme-lavender": "3.0.10", - "nodebb-theme-persona": "4.0.131", + "nodebb-theme-persona": "4.0.132", "nodebb-theme-vanilla": "5.0.71", "nodebb-widget-essentials": "2.0.9", "nodemailer": "2.0.0", diff --git a/public/language/en_GB/error.json b/public/language/en_GB/error.json index a77cf6cb06..a159a0e42c 100644 --- a/public/language/en_GB/error.json +++ b/public/language/en_GB/error.json @@ -17,6 +17,7 @@ "invalid-password": "Invalid Password", "invalid-username-or-password": "Please specify both a username and password", "invalid-search-term": "Invalid search term", + "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/login.json b/public/language/en_GB/login.json index 02abac6371..e76658fe3d 100644 --- a/public/language/en_GB/login.json +++ b/public/language/en_GB/login.json @@ -5,7 +5,7 @@ "remember_me": "Remember Me?", "forgot_password": "Forgot Password?", "alternative_logins": "Alternative Logins", - "failed_login_attempt": "Failed login attempt, please try again.", + "failed_login_attempt": "Login Unsuccessful", "login_successful": "You have successfully logged in!", "dont_have_account": "Don't have an account?" } diff --git a/public/src/client/login.js b/public/src/client/login.js index 85aeb68be5..eda34c8add 100644 --- a/public/src/client/login.js +++ b/public/src/client/login.js @@ -31,9 +31,13 @@ define('forum/login', ['csrf', 'translator'], function(csrf, translator) { window.location.href = data + '?loggedin'; }, error: function(data, status) { - errorEl.find('p').translateText(data.responseText); - errorEl.show(); - submitEl.removeClass('disabled'); + if (data.status === 403 && data.statusText === 'Forbidden') { + window.location.href = config.relative_path + '/login?error=csrf-invalid'; + } else { + errorEl.find('p').translateText(data.responseText); + errorEl.show(); + submitEl.removeClass('disabled'); + } } }); } diff --git a/public/src/client/register.js b/public/src/client/register.js index da86c0231d..fdf4b3ab7e 100644 --- a/public/src/client/register.js +++ b/public/src/client/register.js @@ -99,9 +99,13 @@ define('forum/register', ['csrf', 'translator'], function(csrf, translator) { }, error: function(data) { translator.translate(data.responseText, config.defaultLang, function(translated) { - errorEl.find('p').text(translated); - errorEl.removeClass('hidden'); - registerBtn.removeClass('disabled'); + if (data.status === 403 && data.statusText === 'Forbidden') { + window.location.href = config.relative_path + '/register?error=csrf-invalid'; + } else { + errorEl.find('p').text(translated); + errorEl.removeClass('hidden'); + registerBtn.removeClass('disabled'); + } }); } }); diff --git a/src/controllers/index.js b/src/controllers/index.js index 76d86cee31..f0a6e8d023 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -102,13 +102,18 @@ Controllers.login = function(req, res, next) { var allowLoginWith = (meta.config.allowLoginWith || 'username-email'); + var errorText; + if (req.query.error === 'csrf-invalid') { + errorText = '[[error:csrf-invalid]]'; + } + data.alternate_logins = loginStrategies.length > 0; data.authentication = loginStrategies; data.allowLocalLogin = parseInt(meta.config.allowLocalLogin, 10) === 1 || parseInt(req.query.local, 10) === 1; data.allowRegistration = registrationType === 'normal' || registrationType === 'admin-approval'; data.allowLoginWith = '[[login:' + allowLoginWith + ']]'; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:login]]'}]); - data.error = req.flash('error')[0]; + data.error = req.flash('error')[0] || errorText; data.title = '[[pages:login]]'; if (!data.allowLocalLogin && !data.allowRegistration && data.alternate_logins && data.authentication.length === 1) { @@ -137,6 +142,11 @@ Controllers.register = function(req, res, next) { return next(); } + var errorText; + if (req.query.error === 'csrf-invalid') { + errorText = '[[error:csrf-invalid]]'; + } + async.waterfall([ function(next) { if (registrationType === 'invite-only' || registrationType === 'admin-invite-only') { @@ -166,7 +176,7 @@ Controllers.register = function(req, res, next) { data.termsOfUse = termsOfUse.postData.content; data.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[register:register]]'}]); data.regFormEntry = []; - data.error = req.flash('error')[0]; + data.error = req.flash('error')[0] || errorText; data.title = '[[pages:register]]'; res.render('register', data); From bc0359475c8f897c2133ffe1658599a194da6195 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 9 May 2016 13:04:52 -0400 Subject: [PATCH 60/62] fixed link to documentation, @akhoury --- src/plugins/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/hooks.js b/src/plugins/hooks.js index 35eed900c3..1b186857f4 100644 --- a/src/plugins/hooks.js +++ b/src/plugins/hooks.js @@ -63,7 +63,7 @@ module.exports = function(Plugins) { if (Plugins.deprecatedHooksParams[_hook]) { winston.warn('[plugins/' + id + '] Hook `' + _hook + '` parameters: `' + Plugins.deprecatedHooksParams[_hook] + '`, are being deprecated, ' + 'all plugins should now use the `middleware/cls` module instead of hook\'s arguments to get a reference to the `req`, `res` and/or `socket` object(s) (from which you can get the current `uid` if you need to.) ' - + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-each-request-from-within-any-plugin-hook'); + + '- for more info, visit https://docs.nodebb.org/en/latest/plugins/create.html#getting-a-reference-to-req-res-socket-and-uid-within-any-plugin-hook'); delete Plugins.deprecatedHooksParams[_hook]; } } From acc030e6da538d78c77627ba0582aa0729862faf Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 9 May 2016 22:25:51 +0300 Subject: [PATCH 61/62] added filter:image.size fix uploading of gifs if imagemagick plugin is installed but no image upload plugins are present. --- src/controllers/uploads.js | 2 +- src/image.js | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index d096ecf0de..087889d340 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -83,7 +83,7 @@ function resizeImage(fileObj, callback) { function(next) { fullPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), '..', fileObj.url); - image.load(fullPath, next); + image.size(fullPath, next); }, function (imageData, next) { if (imageData.width < parseInt(meta.config.maximumImageWidth, 10) || 760) { diff --git a/src/image.js b/src/image.js index 7efe86c6ee..fca120d1e5 100644 --- a/src/image.js +++ b/src/image.js @@ -94,11 +94,19 @@ image.normalise = function(path, extension, callback) { } }; -image.load = function(path, callback) { - new Jimp(path, function(err, data) { - callback(err, data ? data.bitmap : null); - }); -}; +image.size = function(path, callback) { + if (plugins.hasListeners('filter:image.size')) { + plugins.fireHook('filter:image.size', { + path: path, + }, function(err, image) { + callback(err, image); + }); + } else { + new Jimp(path, function(err, data) { + callback(err, data ? data.bitmap : null); + }); + } +} image.convertImageToBase64 = function(path, callback) { fs.readFile(path, function(err, data) { From 6392cd31df3b311fbbfdf2ff09a1a7509ef62139 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Mon, 9 May 2016 22:37:03 +0300 Subject: [PATCH 62/62] closes #4618 dont need deleted check --- src/posts/topics.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/posts/topics.js b/src/posts/topics.js index 7ffd9592ce..a653e69b03 100644 --- a/src/posts/topics.js +++ b/src/posts/topics.js @@ -65,16 +65,12 @@ module.exports = function(Posts) { return post ? post.tid : null; }); - topics.getTopicsFields(tids, ['slug', 'deleted'], next); + topics.getTopicsFields(tids, ['slug'], next); } }, next); }, function (results, next) { var paths = pids.map(function(pid, index) { - if (parseInt(results.topics[index].deleted, 10) === 1) { - return null; - } - var slug = results.topics[index] ? results.topics[index].slug : null; var postIndex = utils.isNumber(results.indices[index]) ? parseInt(results.indices[index], 10) + 1 : null;