From 46358a3503cb44c334c7ed9ce0c078eb6393ab93 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sat, 4 Feb 2023 14:51:50 +1100 Subject: [PATCH] Add i18n to bumplock/lock/sticky/cycle, change board/globalsettings, changepassword, and create board pages Update a bunch of middleware, pages and libs to destructure i18n funcs if used more than once to not repeat res.locals ref #396 (going to try and remember this from now on) --- lib/middleware/captcha/blockbypass.js | 13 +++++++------ lib/middleware/captcha/verify.js | 5 +++-- lib/middleware/file/filemiddlewares.js | 17 ++++++++++------- lib/middleware/input/paramconverter.js | 9 +++++---- lib/middleware/ip/dnsbl.js | 5 +++-- lib/middleware/ip/processip.js | 5 +++-- lib/middleware/misc/referrercheck.js | 5 +++-- lib/middleware/permission/haspermsmiddleware.js | 15 +++++++++------ lib/post/filteractions.js | 9 +++++---- models/forms/actionhandler.js | 8 ++++---- models/forms/bumplockposts.js | 8 +++++--- models/forms/changeboardsettings.js | 5 +++-- models/forms/changeglobalsettings.js | 5 +++-- models/forms/changepassword.js | 17 +++++++++-------- models/forms/create.js | 9 +++++---- models/forms/cycleposts.js | 8 +++++--- models/forms/lockposts.js | 8 +++++--- models/forms/stickyposts.js | 8 +++++--- models/pages/twofactor.js | 5 +++-- 19 files changed, 95 insertions(+), 69 deletions(-) diff --git a/lib/middleware/captcha/blockbypass.js b/lib/middleware/captcha/blockbypass.js index dce3e265..523cfae0 100644 --- a/lib/middleware/captcha/blockbypass.js +++ b/lib/middleware/captcha/blockbypass.js @@ -12,6 +12,7 @@ module.exports = { check: async (req, res, next) => { + const { __ } = res.locals; const { secureCookies, blockBypass } = config.get; //bypass captcha permission @@ -25,12 +26,12 @@ module.exports = { if (!res.locals.solvedCaptcha && (!bypassId || bypassId.length !== 24)) { deleteTempFiles(req).catch(console.error); return dynamicResponse(req, res, 403, 'message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('Please complete a block bypass to continue'), + 'title': __('Forbidden'), + 'message': __('Please complete a block bypass to continue'), 'frame': '/bypass_minimal.html', 'link': { 'href': '/bypass.html', - 'text': res.locals.__('Get block bypass'), + 'text': __('Get block bypass'), }, }); } @@ -69,12 +70,12 @@ module.exports = { deleteTempFiles(req).catch(console.error); res.clearCookie('bypassid'); return dynamicResponse(req, res, 403, 'message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('Block bypass expired or exceeded max uses'), + 'title': __('Forbidden'), + 'message': __('Block bypass expired or exceeded max uses'), 'frame': '/bypass_minimal.html', 'link': { 'href': '/bypass.html', - 'text': res.locals.__('Get block bypass'), + 'text': __('Get block bypass'), }, }); diff --git a/lib/middleware/captcha/verify.js b/lib/middleware/captcha/verify.js index f3ecfe42..bf247550 100644 --- a/lib/middleware/captcha/verify.js +++ b/lib/middleware/captcha/verify.js @@ -37,9 +37,10 @@ module.exports = async (req, res, next) => { return next(err); } const page = (req.body.minimal || req.path === '/blockbypass' ? 'bypass' : 'message'); + const { __ } = res.locals; return dynamicResponse(req, res, 403, page, { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__(err), + 'title': __('Forbidden'), + 'message': __(err), 'redirect': req.headers.referer, }); } diff --git a/lib/middleware/file/filemiddlewares.js b/lib/middleware/file/filemiddlewares.js index 0eb3de8c..e521739f 100644 --- a/lib/middleware/file/filemiddlewares.js +++ b/lib/middleware/file/filemiddlewares.js @@ -6,16 +6,18 @@ const { debugLogs } = require(__dirname+'/../../../configs/secrets.js') , upload = require('@fatchan/express-fileupload') , fileHandlers = {} , fileSizeLimitFunction = (req, res) => { + const { __ } = res.locals; return dynamicResponse(req, res, 413, 'message', { - 'title': res.locals.__('Payload Too Large'), - 'message': res.locals.__('Your upload was too large'), + 'title': __('Payload Too Large'), + 'message': __('Your upload was too large'), 'redirect': req.headers.referer }); } , missingExtensionLimitFunction = (req, res) => { + const { __ } = res.locals; return dynamicResponse(req, res, 400, 'message', { - 'title': res.locals.__('Bad Request'), - 'message': res.locals.__('Missing file extensions'), + 'title': __('Bad Request'), + 'message': __('Missing file extensions'), 'redirect': req.headers.referer }); } @@ -25,12 +27,13 @@ const { debugLogs } = require(__dirname+'/../../../configs/secrets.js') const fileSizeLimit = globalLimits[`${fileType}FilesSize`]; const fileNumLimit = globalLimits[`${fileType}Files`]; const fileNumLimitFunction = (req, res) => { + const { __ } = res.locals; const isPostform = req.path.endsWith('/post') || req.path.endsWith('/modpost'); const message = (isPostform && res.locals.board) - ? res.locals.__(`Max files per post ${res.locals.board.settings.maxFiles < globalLimits.postFiles.max ? 'on this board ' : ''}is %s`, res.locals.board.settings.maxFiles) - : res.locals.__('Max files per request is %s', fileNumLimit.max); + ? __(`Max files per post ${res.locals.board.settings.maxFiles < globalLimits.postFiles.max ? 'on this board ' : ''}is %s`, res.locals.board.settings.maxFiles) + : __('Max files per request is %s', fileNumLimit.max); return dynamicResponse(req, res, 400, 'message', { - 'title': res.locals.__('Too many files'), + 'title': __('Too many files'), 'message': message, 'redirect': req.headers.referer }); diff --git a/lib/middleware/input/paramconverter.js b/lib/middleware/input/paramconverter.js index 62a329fc..f0f26cbc 100644 --- a/lib/middleware/input/paramconverter.js +++ b/lib/middleware/input/paramconverter.js @@ -27,6 +27,7 @@ module.exports = (options) => { return (req, res, next) => { + const { __ } = res.locals; const { timeFields, trimFields, allowedArrays, processThreadIdParam, processDateParam, processMessageLength, numberFields, numberArrays, objectIdParams, objectIdFields, objectIdArrays } = options; @@ -39,8 +40,8 @@ module.exports = (options) => { const val = req.body[key]; if (!allowedArrays.includes(key) && Array.isArray(val)) { return dynamicResponse(req, res, 400, 'message', { - 'title': res.locals.__('Bad request'), - 'message': res.locals.__('Malformed input'), + 'title': __('Bad request'), + 'message': __('Malformed input'), }); } else if (allowedArrays.includes(key) && !Array.isArray(val)) { req.body[key] = makeArrayIfSingle(req.body[key]); //convert to arrays with single item for simpler case batch handling later @@ -134,8 +135,8 @@ module.exports = (options) => { } } catch (e) { return dynamicResponse(req, res, 400, 'message', { - 'title': res.locals.__('Bad request'), - 'message': res.locals.__('Malformed input'), + 'title': __('Bad request'), + 'message': __('Malformed input'), }); } diff --git a/lib/middleware/ip/dnsbl.js b/lib/middleware/ip/dnsbl.js index 07a53f76..52be59f8 100644 --- a/lib/middleware/ip/dnsbl.js +++ b/lib/middleware/ip/dnsbl.js @@ -29,9 +29,10 @@ module.exports = async (req, res, next) => { } //otherwise dnsbl cant be bypassed deleteTempFiles(req).catch(console.error); + const { __ } = res.locals; return dynamicResponse(req, res, 403, 'message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('Your request was blocked because your IP address is listed on a blacklist.'), + 'title': __('Forbidden'), + 'message': __('Your request was blocked because your IP address is listed on a blacklist.'), 'redirect': req.headers.referer || '/', }); } diff --git a/lib/middleware/ip/processip.js b/lib/middleware/ip/processip.js index eb0abaef..744e8898 100644 --- a/lib/middleware/ip/processip.js +++ b/lib/middleware/ip/processip.js @@ -53,9 +53,10 @@ module.exports = (req, res, next) => { } catch(e) { //should never get here console.error('Ip parse failed', e); + const { __ } = res.locals; return res.status(400).render('message', { - 'title': res.locals.__('Bad request'), - 'message': res.locals.__('Malformed IP'), + 'title': __('Bad request'), + 'message': __('Malformed IP'), }); } diff --git a/lib/middleware/misc/referrercheck.js b/lib/middleware/misc/referrercheck.js index 9ace1c3b..b130fd36 100644 --- a/lib/middleware/misc/referrercheck.js +++ b/lib/middleware/misc/referrercheck.js @@ -24,9 +24,10 @@ module.exports = (req, res, next) => { //referrer is invalid url } if (refererCheck === true && (!req.headers.referer || !validReferer)) { + const { __ } = res.locals; return dynamicResponse(req, res, 403, 'message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('Invalid or missing "Referer" header. Are you posting from the correct URL?'), + 'title': __('Forbidden'), + 'message': __('Invalid or missing "Referer" header. Are you posting from the correct URL?'), }); } next(); diff --git a/lib/middleware/permission/haspermsmiddleware.js b/lib/middleware/permission/haspermsmiddleware.js index ff19241c..41d27e02 100644 --- a/lib/middleware/permission/haspermsmiddleware.js +++ b/lib/middleware/permission/haspermsmiddleware.js @@ -10,9 +10,10 @@ module.exports = { one: (requiredPermission) => { return cache.one[requiredPermission] || (cache.one[requiredPermission] = function(req, res, next) { if (!res.locals.permissions.get(requiredPermission)) { + const { __ } = res.locals; return res.status(403).render('message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('No Permission'), + 'title': __('Forbidden'), + 'message': __('No Permission'), 'redirect': req.headers.referer || '/', }); } @@ -24,9 +25,10 @@ module.exports = { //these caches working as intended with arrays? return cache.all[requiredPermissions] || (cache.all[requiredPermissions] = function(req, res, next) { if (!res.locals.permissions.hasAll(...requiredPermissions)) { + const { __ } = res.locals; return res.status(403).render('message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('No Permission'), + 'title': __('Forbidden'), + 'message': __('No Permission'), 'redirect': req.headers.referer || '/', }); } @@ -38,9 +40,10 @@ module.exports = { //these caches working as intended with arrays? return cache.any[requiredPermissions] || (cache.any[requiredPermissions] = function(req, res, next) { if (!res.locals.permissions.hasAny(...requiredPermissions)) { + const { __ } = res.locals; return res.status(403).render('message', { - 'title': res.locals.__('Forbidden'), - 'message': res.locals.__('No Permission'), + 'title': __('Forbidden'), + 'message': __('No Permission'), 'redirect': req.headers.referer || '/', }); } diff --git a/lib/post/filteractions.js b/lib/post/filteractions.js index 1401165a..03a94f5a 100644 --- a/lib/post/filteractions.js +++ b/lib/post/filteractions.js @@ -7,13 +7,14 @@ const { Bans } = require(__dirname+'/../../db/') module.exports = async (req, res, hitGlobalFilter, hitLocalFilter, boardFilterMode, globalFilterMode, boardFilterBanDuration, globalFilterBanDuration, filterBanAppealable, redirect) => { + const { __ } = res.locals; //global filter mode takes prio const useFilterMode = hitGlobalFilter ? globalFilterMode : boardFilterMode; if (useFilterMode === 1) { return dynamicResponse(req, res, 400, 'message', { - 'title': res.locals.__('Bad request'), - 'message': res.locals.__('Your post was blocked by a word filter'), + 'title': __('Bad request'), + 'message': __('Your post was blocked by a word filter'), 'redirect': redirect }); } else { @@ -28,7 +29,7 @@ module.exports = async (req, res, hitGlobalFilter, hitLocalFilter, boardFilterMo 'type': res.locals.ip.type, }, 'range': 0, - 'reason': res.locals.__(`${hitGlobalFilter ? 'global ' :''}word filter auto ban`), + 'reason': __(`${hitGlobalFilter ? 'global ' :''}word filter auto ban`), 'board': banBoard, 'posts': null, 'issuer': 'system', //todo: make a "system" property instead? @@ -36,7 +37,7 @@ module.exports = async (req, res, hitGlobalFilter, hitLocalFilter, boardFilterMo 'expireAt': banExpiry, 'allowAppeal': hitGlobalFilter ? filterBanAppealable : true, 'showUser': true, - 'note': res.locals.__(`${hitGlobalFilter ? 'global ' :''}filter hit: "%s"`, (hitGlobalFilter || hitLocalFilter)), + 'note': __(`${hitGlobalFilter ? 'global ' :''}filter hit: "%s"`, (hitGlobalFilter || hitLocalFilter)), 'seen': true, }; const insertedResult = await Bans.insertOne(ban); diff --git a/models/forms/actionhandler.js b/models/forms/actionhandler.js index f783cb58..f61f2f58 100644 --- a/models/forms/actionhandler.js +++ b/models/forms/actionhandler.js @@ -231,7 +231,7 @@ module.exports = async (req, res, next) => { } //lock, sticky, bumplock, cyclic if (req.body.bumplock) { - const { message, action, query } = bumplockPosts(res.locals.posts); + const { message, action, query } = bumplockPosts(res.locals); if (action) { modlogActions.push('Bumplock'); combinedQuery[action] = { ...combinedQuery[action], ...query}; @@ -239,7 +239,7 @@ module.exports = async (req, res, next) => { messages.push(message); } if (req.body.lock) { - const { message, action, query } = lockPosts(res.locals.posts); + const { message, action, query } = lockPosts(res.locals); if (action) { modlogActions.push('Lock'); combinedQuery[action] = { ...combinedQuery[action], ...query}; @@ -247,7 +247,7 @@ module.exports = async (req, res, next) => { messages.push(message); } if (req.body.sticky != null) { - const { message, action, query } = stickyPosts(res.locals.posts, req.body.sticky); + const { message, action, query } = stickyPosts(res.locals, req.body.sticky); if (action) { modlogActions.push('Sticky'); combinedQuery[action] = { ...combinedQuery[action], ...query}; @@ -255,7 +255,7 @@ module.exports = async (req, res, next) => { messages.push(message); } if (req.body.cyclic) { - const { message, action, query } = cyclePosts(res.locals.posts); + const { message, action, query } = cyclePosts(res.locals); if (action) { modlogActions.push('Cycle'); combinedQuery[action] = { ...combinedQuery[action], ...query}; diff --git a/models/forms/bumplockposts.js b/models/forms/bumplockposts.js index 501133e0..c23182e1 100644 --- a/models/forms/bumplockposts.js +++ b/models/forms/bumplockposts.js @@ -2,7 +2,9 @@ const { NumberInt } = require(__dirname+'/../../db/db.js'); -module.exports = (posts) => { +module.exports = (locals) => { + + const { posts, __, __n } = locals; const filteredposts = posts.filter(post => { return !post.thread; @@ -10,12 +12,12 @@ module.exports = (posts) => { if (filteredposts.length === 0) { return { - message: 'No threads to bumplock', + message: __('No threads selected to Bumplock'), }; } return { - message: `Toggled bumplock for ${filteredposts.length} thread(s)`, + message: __n('Toggled Bumplock for %s threads', filteredposts.length), action: '$bit', query: { 'bumplocked': { diff --git a/models/forms/changeboardsettings.js b/models/forms/changeboardsettings.js index 9d8721d1..d62c7502 100644 --- a/models/forms/changeboardsettings.js +++ b/models/forms/changeboardsettings.js @@ -35,6 +35,7 @@ const { Boards, Posts } = require(__dirname+'/../../db/') module.exports = async (req, res) => { + const { __ } = res.locals; const { globalLimits } = config.get; //oldsettings before changes @@ -232,8 +233,8 @@ module.exports = async (req, res) => { debugLogs && console.log(req.params.board, 'board settings changed'); return dynamicResponse(req, res, 200, 'message', { - 'title': res.locals.__('Success'), - 'message': 'Updated settings.', + 'title': __('Success'), + 'message': __('Updated settings.'), 'redirect': `/${req.params.board}/manage/settings.html` }); diff --git a/models/forms/changeglobalsettings.js b/models/forms/changeglobalsettings.js index 61da3050..91c2612c 100644 --- a/models/forms/changeglobalsettings.js +++ b/models/forms/changeglobalsettings.js @@ -40,6 +40,7 @@ const { Boards } = require(__dirname+'/../../db/') module.exports = async (req, res) => { + const { __ } = res.locals; const promises = []; const oldSettings = config.get; @@ -371,8 +372,8 @@ module.exports = async (req, res) => { }); return dynamicResponse(req, res, 200, 'message', { - 'title': res.locals.__('Success'), - 'message': 'Updated settings.', + 'title': __('Success'), + 'message': __('Updated settings.'), 'redirect': '/globalmanage/settings.html' }); diff --git a/models/forms/changepassword.js b/models/forms/changepassword.js index cc4c8451..a60ed4d5 100644 --- a/models/forms/changepassword.js +++ b/models/forms/changepassword.js @@ -8,6 +8,7 @@ const bcrypt = require('bcrypt') module.exports = async (req, res) => { + const { __ } = res.locals; const username = req.body.username.toLowerCase(); const password = req.body.password; const newPassword = req.body.newpassword; @@ -18,8 +19,8 @@ module.exports = async (req, res) => { //if the account doesnt exist, reject if (!account) { return dynamicResponse(req, res, 403, 'message', { - 'title': 'Forbidden', - 'message': 'Incorrect account credentials', + 'title': __('Forbidden'), + 'message': __('Incorrect account credentials'), 'redirect': '/changepassword.html' }); } @@ -30,8 +31,8 @@ module.exports = async (req, res) => { //if hashes matched if (passwordMatch === false) { return dynamicResponse(req, res, 403, 'message', { - 'title': 'Forbidden', - 'message': 'Incorrect account credentials', + 'title': __('Forbidden'), + 'message': __('Incorrect account credentials'), 'redirect': '/changepassword.html' }); } @@ -40,8 +41,8 @@ module.exports = async (req, res) => { const delta = await doTwoFactor(username, account.twofactor, req.body.twofactor); if (delta === null) { return dynamicResponse(req, res, 403, 'message', { - 'title': 'Forbidden', - 'message': 'Incorrect account credentials', + 'title': __('Forbidden'), + 'message': __('Incorrect account credentials'), 'redirect': '/changepassword.html' }); } @@ -54,8 +55,8 @@ module.exports = async (req, res) => { ]); return dynamicResponse(req, res, 200, 'message', { - 'title': res.locals.__('Success'), - 'message': 'Changed password', + 'title': __('Success'), + 'message': __('Password updated successfully'), 'redirect': '/login.html' }); diff --git a/models/forms/create.js b/models/forms/create.js index 75ab656c..aebaf56d 100644 --- a/models/forms/create.js +++ b/models/forms/create.js @@ -11,6 +11,7 @@ const { Boards, Accounts } = require(__dirname+'/../../db/') module.exports = async (req, res) => { + const { __ } = res.locals; const { boardDefaults } = config.get; const { name, description } = req.body @@ -20,8 +21,8 @@ module.exports = async (req, res) => { if (restrictedURIs.has(uri)) { return dynamicResponse(req, res, 400, 'message', { - 'title': 'Bad Request', - 'message': 'That URI is not available for board creation', + 'title': __('Bad Request'), + 'message': __('URI "%s" is reserved', uri), 'redirect': '/create.html' }); } @@ -31,8 +32,8 @@ module.exports = async (req, res) => { // if board exists reject if (board != null) { return dynamicResponse(req, res, 409, 'message', { - 'title': 'Conflict', - 'message': 'Board with this URI already exists', + 'title': __('Conflict'), + 'message': __('Board with this URI already exists'), 'redirect': '/create.html' }); } diff --git a/models/forms/cycleposts.js b/models/forms/cycleposts.js index 8160b1a8..21abe20c 100644 --- a/models/forms/cycleposts.js +++ b/models/forms/cycleposts.js @@ -2,7 +2,9 @@ const { NumberInt } = require(__dirname+'/../../db/db.js'); -module.exports = (posts) => { +module.exports = (locals) => { + + const { posts, __, __n } = locals; const filteredposts = posts.filter(post => { return !post.thread; @@ -10,12 +12,12 @@ module.exports = (posts) => { if (filteredposts.length === 0) { return { - message: 'No thread(s) to cycle', + message: __('No threads selected to make Cyclical'), }; } return { - message: `Toggled Cyclical mode for ${filteredposts.length} thread(s)`, + message: __n('Toggled Cyclical mode for %s threads', filteredposts.length), action: '$bit', query: { 'cyclic': { diff --git a/models/forms/lockposts.js b/models/forms/lockposts.js index 04037b01..2583ae5a 100644 --- a/models/forms/lockposts.js +++ b/models/forms/lockposts.js @@ -2,7 +2,9 @@ const { NumberInt } = require(__dirname+'/../../db/db.js'); -module.exports = (posts) => { +module.exports = (locals) => { + + const { posts, __, __n } = locals; const filteredposts = posts.filter(post => { return !post.thread; @@ -10,12 +12,12 @@ module.exports = (posts) => { if (filteredposts.length === 0) { return { - message: 'No thread(s) to lock', + message: __('No threads selected to Lock'), }; } return { - message: `Toggled Lock for ${filteredposts.length} thread(s)`, + message: __n('Toggled Lock for %s threads', filteredposts.length), action: '$bit', query: { 'locked': { diff --git a/models/forms/stickyposts.js b/models/forms/stickyposts.js index b5766d82..ff20ee4b 100644 --- a/models/forms/stickyposts.js +++ b/models/forms/stickyposts.js @@ -2,7 +2,9 @@ const { NumberInt } = require(__dirname+'/../../db/db.js'); -module.exports = (posts, sticky) => { +module.exports = (locals, sticky) => { + + const { posts, __, __n } = locals; const filteredposts = posts.filter(post => { return !post.thread; @@ -10,14 +12,14 @@ module.exports = (posts, sticky) => { if (filteredposts.length === 0) { return { - message: 'No thread(s) to sticky', + message: __('No threads selected to Sticky'), }; } const stickyValue = NumberInt(sticky); return { - message: `Set sticky for ${filteredposts.length} thread(s) to ${sticky}`, + message: __n('Set Sticky level for %s threads to %s', filteredposts.length, sticky), action: '$set', query: { 'sticky': stickyValue, diff --git a/models/pages/twofactor.js b/models/pages/twofactor.js index 561ee562..eb3b0546 100644 --- a/models/pages/twofactor.js +++ b/models/pages/twofactor.js @@ -18,9 +18,10 @@ module.exports = async (req, res, next) => { const username = res.locals.user.username; const ratelimit = await Ratelimits.incrmentQuota(username, '2fa', 50); if (ratelimit > 100) { + const { __ } = res.locals; return dynamicResponse(req, res, 429, 'message', { - 'title': res.locals.__('Ratelimited'), - 'message': res.locals.__('Please wait before generating another 2FA QR code.'), + 'title': __('Ratelimited'), + 'message': __('Please wait before generating another 2FA QR code.'), }); }