diff --git a/CHANGELOG.md b/CHANGELOG.md index 98011b68..25f4b070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,20 @@ +### 1.0.4 + - Translation improvements. + - Fix Tor hidden service country name not displaying correctly. + - Make global disabling of anonymizer file posting apply to board banners, flags, custompages. + +### 1.0.3 + - Translation improvements. + - Add back missing "Lock+Unlist" mode for handling ianctive boards. + - Npm audit. + ### 1.0.2 - Translation improvements. ### 1.0.1 - Translation improvements. - Add a link to board in mod view e.g. /test/ on globalmanage recent posts to give some context. - - Fix dockerignore for docker testing/development, was broken due to translations + - Fix dockerignore for docker testing/development, was broken due to translations. ### 1.0.0 Version 1.0.0 is here. jschan is stable and mature enough and has been for a while. There's no need to go to version 0.9999.9999 and I am comfortable enough to call it "Version 1". diff --git a/controllers/forms.js b/controllers/forms.js index b504acbe..9c753f32 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -20,7 +20,7 @@ const express = require('express') , dnsblCheck = require(__dirname+'/../lib/middleware/ip/dnsbl.js') , blockBypass = require(__dirname+'/../lib/middleware/captcha/blockbypass.js') , fileMiddlewares = require(__dirname+'/../lib/middleware/file/filemiddlewares.js') - , { setBoardLanguage } = require(__dirname+'/../lib/middleware/locale/locale.js') + , { setBoardLanguage, setQueryLanguage } = require(__dirname+'/../lib/middleware/locale/locale.js') //controllers , { deleteBoardController, editBansController, appealController, globalActionController, twofactorController, actionController, addCustomPageController, deleteCustomPageController, addNewsController, @@ -62,15 +62,15 @@ router.post('/board/:board/deleteboard', useSession, sessionRefresh, csrf, Board hasPerms.any(Permissions.MANAGE_BOARD_OWNER, Permissions.MANAGE_GLOBAL_BOARDS), deleteBoardController.controller); //delete board //board crud banners, flags, assets, custompages -router.post('/board/:board/addbanners', useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.banner, csrf, calcPerms, isLoggedIn, +router.post('/board/:board/addbanners', geoIp, useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.banner, csrf, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), numFiles, uploadBannersController.controller); //add banners router.post('/board/:board/deletebanners', useSession, sessionRefresh, csrf, Boards.exists, setBoardLanguage, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), deleteBannersController.paramConverter, deleteBannersController.controller); //delete banners -router.post('/board/:board/addassets', useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.asset, csrf, calcPerms, isLoggedIn, +router.post('/board/:board/addassets', geoIp, useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.asset, csrf, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), numFiles, addAssetsController.controller); //add assets router.post('/board/:board/deleteassets', useSession, sessionRefresh, csrf, Boards.exists, setBoardLanguage, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), deleteAssetsController.paramConverter, deleteAssetsController.controller); //delete assets -router.post('/board/:board/addflags', useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.flag, csrf, calcPerms, isLoggedIn, +router.post('/board/:board/addflags', geoIp, useSession, sessionRefresh, Boards.exists, setBoardLanguage, fileMiddlewares.flag, csrf, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), numFiles, addFlagsController.controller); //add flags router.post('/board/:board/deleteflags', useSession, sessionRefresh, csrf, Boards.exists, setBoardLanguage, calcPerms, isLoggedIn, hasPerms.one(Permissions.MANAGE_BOARD_CUSTOMISATION), deleteFlagsController.paramConverter, deleteFlagsController.controller); //delete flags @@ -123,7 +123,7 @@ router.post('/deletesessions', useSession, sessionRefresh, csrf, calcPerms, isLo //removes captcha cookie, for refreshing for noscript users router.post('/newcaptcha', newCaptchaForm); //solve captcha for block bypass -router.post('/blockbypass', geoIp, processIp, useSession, sessionRefresh, calcPerms, verifyCaptcha, blockBypassForm); +router.post('/blockbypass', geoIp, processIp, useSession, sessionRefresh, calcPerms, setQueryLanguage, verifyCaptcha, blockBypassForm); module.exports = router; diff --git a/controllers/forms/addassets.js b/controllers/forms/addassets.js index 4fab249d..245a8ee4 100644 --- a/controllers/forms/addassets.js +++ b/controllers/forms/addassets.js @@ -14,10 +14,11 @@ module.exports = { const { __ } = res.locals; - const { globalLimits } = config.get; + const { globalLimits, disableAnonymizerFilePosting } = config.get; const errors = await checkSchema([ { result: res.locals.numFiles === 0, expected: false, blocking: true, error: __('Must provide a file') }, + { result: (res.locals.anonymizer && disableAnonymizerFilePosting), expected: false, error: __('Posting files through anonymizers has been disabled globally') }, { result: numberBody(res.locals.numFiles, 0, globalLimits.assetFiles.max), expected: true, error: __('Exceeded max asset uploads in one request of %s', globalLimits.assetFiles.max) }, { result: numberBody(res.locals.board.assets.length+res.locals.numFiles, 0, globalLimits.assetFiles.total), expected: true, error: __('Total number of assets would exceed global limit of %s', globalLimits.assetFiles.total) }, ]); diff --git a/controllers/forms/addflags.js b/controllers/forms/addflags.js index a6d85b2b..480c33b2 100644 --- a/controllers/forms/addflags.js +++ b/controllers/forms/addflags.js @@ -14,10 +14,11 @@ module.exports = { const { __ } = res.locals; - const { globalLimits } = config.get; + const { globalLimits, disableAnonymizerFilePosting } = config.get; const errors = await checkSchema([ { result: res.locals.numFiles === 0, expected: false, blocking: true, error: __('Must provide a file') }, + { result: (res.locals.anonymizer && disableAnonymizerFilePosting), expected: false, error: __('Posting files through anonymizers has been disabled globally') }, { result: numberBody(res.locals.numFiles, 0, globalLimits.flagFiles.max), expected: true, error: __('Exceeded max flag uploads in one request of %s', globalLimits.flagFiles.max) }, { result: numberBody(Object.keys(res.locals.board.flags).length+res.locals.numFiles, 0, globalLimits.flagFiles.total), expected: true, error: __('Total number of flags would exceed global limit of %s', globalLimits.flagFiles.total) }, ]); diff --git a/controllers/forms/makepost.js b/controllers/forms/makepost.js index fdcaafbd..10812ef2 100644 --- a/controllers/forms/makepost.js +++ b/controllers/forms/makepost.js @@ -1,6 +1,7 @@ 'use strict'; const makePost = require(__dirname+'/../../models/forms/makepost.js') + , { Permissions } = require(__dirname+'/../../lib/permission/permissions.js') , deleteTempFiles = require(__dirname+'/../../lib/file/deletetempfiles.js') , dynamicResponse = require(__dirname+'/../../lib/misc/dynamic.js') , { func: pruneFiles } = require(__dirname+'/../../schedules/tasks/prune.js') @@ -25,11 +26,11 @@ module.exports = { const { globalLimits, disableAnonymizerFilePosting } = config.get; const hasNoMandatoryFile = globalLimits.postFiles.max !== 0 && res.locals.board.settings.maxFiles !== 0 && res.locals.numFiles === 0; - //maybe add more duplicates here? + const disableBoardAnonymizerFilePosting = res.locals.board.settings.disableAnonymizerFilePosting && !res.locals.permissions.get(Permissions.MANAGE_BOARD_GENERAL); const errors = await checkSchema([ { result: (lengthBody(req.body.message, 1) && res.locals.numFiles === 0), expected: false, error: __('Posts must include a message or file') }, - { result: (res.locals.anonymizer && (disableAnonymizerFilePosting || res.locals.board.settings.disableAnonymizerFilePosting) + { result: (res.locals.anonymizer && (disableAnonymizerFilePosting || disableBoardAnonymizerFilePosting) && res.locals.numFiles > 0), expected: false, error: __(`Posting files through anonymizers has been disabled ${disableAnonymizerFilePosting ? 'globally' : 'on this board'}`) }, { result: res.locals.numFiles > res.locals.board.settings.maxFiles, blocking: true, expected: false, error: __(`Too many files. Max files per post ${res.locals.board.settings.maxFiles < globalLimits.postFiles.max ? 'on this board ' : ''}is %s`, res.locals.board.settings.maxFiles) }, { result: (lengthBody(req.body.subject, 1) && (!existsBody(req.body.thread) diff --git a/controllers/forms/uploadbanners.js b/controllers/forms/uploadbanners.js index b5723ab9..cf720d6f 100644 --- a/controllers/forms/uploadbanners.js +++ b/controllers/forms/uploadbanners.js @@ -14,10 +14,11 @@ module.exports = { const { __ } = res.locals; - const { globalLimits } = config.get; + const { globalLimits, disableAnonymizerFilePosting } = config.get; const errors = await checkSchema([ { result: res.locals.numFiles === 0, expected: false, blocking: true, error: __('Must provide a file') }, + { result: (res.locals.anonymizer && disableAnonymizerFilePosting), expected: false, error: __('Posting files through anonymizers has been disabled globally') }, { result: numberBody(res.locals.numFiles, 0, globalLimits.bannerFiles.max), expected: true, error: __('Exceeded max banner uploads in one request of %s', globalLimits.bannerFiles.max) }, { result: numberBody(res.locals.board.banners.length+res.locals.numFiles, 0, globalLimits.bannerFiles.total), expected: true, error: __('Total number of banners would exceed global limit of %s', globalLimits.bannerFiles.total) }, ]); diff --git a/gulp/res/js/forms.js b/gulp/res/js/forms.js index d86b0b05..c4d2112d 100644 --- a/gulp/res/js/forms.js +++ b/gulp/res/js/forms.js @@ -1,4 +1,4 @@ -/* globals __n modal Tegaki grecaptcha hcaptcha captchaController appendLocalStorageArray socket isThread setLocalStorage forceUpdate captchaController uploaditem */ +/* globals __ __n modal Tegaki grecaptcha hcaptcha captchaController appendLocalStorageArray socket isThread setLocalStorage forceUpdate captchaController uploaditem */ async function videoThumbnail(file) { return new Promise((resolve, reject) => { const hiddenVideo = document.createElement('video'); @@ -334,12 +334,12 @@ class postFormHandler { } else { //not a 200 so probably error - if (!this.captchaField && json.message === 'Incorrect captcha answer') { + if (!this.captchaField && json.message === __('Incorrect captcha answer')) { /* add missing captcha field if we got an error about it and the form has no captcha field (must have been enabeld after we loaded the page) */ captchaController.addMissingCaptcha(); this.captchaField = true; - } else if (json.message === 'Captcha expired') { + } else if (json.message === __('Captcha expired')) { //if captcha is expired, just refresh the captcha const captcha = this.form.querySelector('.captcharefresh'); if (captcha) { diff --git a/lib/misc/countries.js b/lib/misc/countries.js index c6bcb150..5d0632a9 100644 --- a/lib/misc/countries.js +++ b/lib/misc/countries.js @@ -15,10 +15,15 @@ const countries = require('i18n-iso-countries') 'LOKI': 'Lokinet SNApp', }))); +//Monkey patch until https://github.com/michaelwittig/node-i18n-iso-countries/issues/322 +const alpha3s = countries.getAlpha3Codes(); +alpha3s['TOR'] = 'Tor Hidden Service'; +alpha3s['LOKI'] = 'Lokinet SNApp'; + i18n.getLocales() .forEach(locale => { const localeExtraCodesMap = { ...extraCountryNames }; - for (let code in localeExtraCodesMap) { + for (const code in localeExtraCodesMap) { localeExtraCodesMap[code] = i18n.__({ phrase: localeExtraCodesMap[code], locale: locale, diff --git a/locales/en-GB.json b/locales/en-GB.json index df29e3d6..82306141 100644 --- a/locales/en-GB.json +++ b/locales/en-GB.json @@ -1051,7 +1051,7 @@ "Raw IPs": "Raw IPs", "Reason": "Reason", "Recent": "Recent", - "Recent Posts": "Recept Posts", + "Recent Posts": "Recent Posts", "Recently bumped threads from multiple boards": "Recently bumped threads from multiple boards", "Recording replay": "Recording replay", "Recursive post hide": "Recursive post hide", @@ -1118,7 +1118,7 @@ "Secure Cookies": "Secure Cookies", "Security": "Security", "See Global Post History": "See Global Post History", - "Seen?": "Visto?", + "Seen?": "Seen?", "Select/Drop/Paste files": { "one": "Select/Drop/Paste file", "other": "Select/Drop/Paste files" @@ -1392,5 +1392,6 @@ "Message too long.": "Message too long.", "View the full text": "View the full text", "View the full thread": "View the full thread", - "%s earlier": "%s earlier" + "%s earlier": "%s earlier", + "Lock+Unlist board": "Lock+Unlist board" } diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 53b582bb..3fcf69cc 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -1392,5 +1392,6 @@ "Message too long.": "Mensagem demasiado longa.", "View the full text": "Ver texto completo", "View the full thread": "Ver fio completo", - "%s earlier": "%s anteriores" + "%s earlier": "%s anteriores", + "Lock+Unlist board": "Trancar+Remover da lista" } diff --git a/locales/ru-RU.json b/locales/ru-RU.json index 8211eea5..6f30d5ed 100644 --- a/locales/ru-RU.json +++ b/locales/ru-RU.json @@ -1392,5 +1392,6 @@ "Message too long.": "Сообщение слишком длинное.", "View the full text": "Просмотреть полный текст", "View the full thread": "Просмотреть полную ветку", - "%s earlier": "%s выше" + "%s earlier": "%s выше", + "Lock+Unlist board": "замок+исключить из списка" } diff --git a/package-lock.json b/package-lock.json index 7164ee1f..56600a48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jschan", - "version": "1.0.2", + "version": "1.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jschan", - "version": "1.0.2", + "version": "1.0.4", "license": "AGPL-3.0-only", "dependencies": { "@fatchan/express-fileupload": "^1.4.2", @@ -15432,9 +15432,9 @@ } }, "node_modules/vm2": { - "version": "3.9.14", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.14.tgz", - "integrity": "sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==", + "version": "3.9.16", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.16.tgz", + "integrity": "sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==", "dependencies": { "acorn": "^8.7.0", "acorn-walk": "^8.2.0" diff --git a/package.json b/package.json index f2dcbf2c..3730db1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jschan", - "version": "1.0.2", + "version": "1.0.4", "migrateVersion": "1.0.0", "description": "", "main": "server.js", diff --git a/views/pages/bypass.pug b/views/pages/bypass.pug index 52791e8c..6df940f3 100644 --- a/views/pages/bypass.pug +++ b/views/pages/bypass.pug @@ -6,7 +6,7 @@ block head block content h1.board-title #{__('Block Bypass')} .form-wrapper.flex-center.mv-10 - form.form-post(action='/forms/blockbypass' method='POST' data-captcha-preload='true') + form.form-post(action=`/forms/blockbypass?language=${encodeURIComponent(pageLanguage)}` method='POST' data-captcha-preload='true') .row .col include ../includes/captcha.pug diff --git a/views/pages/globalmanagesettings.pug b/views/pages/globalmanagesettings.pug index 2b3f8355..4888644c 100644 --- a/views/pages/globalmanagesettings.pug +++ b/views/pages/globalmanagesettings.pug @@ -208,6 +208,7 @@ block content select(name='abandoned_board_action') option(value='0', selected=settings.abandonedBoardAction === 0) #{__('Do nothing')} option(value='1', selected=settings.abandonedBoardAction === 1) #{__('Lock board')} + option(value='2', selected=settings.abandonedBoardAction === 2) #{__('Lock+Unlist board')} option(value='3', selected=settings.abandonedBoardAction === 3) #{__('Delete board')} .row h4.mv-5 #{__('Global Limits')}