From c4c2a1543ddd6d94b014c95222ebb62cb0325d44 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 09:53:26 +0000 Subject: [PATCH 01/19] start on adding custom banners models and controllers arent done, things wont work yet. added a migration and updated the template.js with some new needed values, changed "banners" in manage to "assets" since it will hold both now refactor the banners file form into a mixin since its basically repeated for flags, and make it a tad more customisable --- configs/template.js.example | 4 ++++ controllers/forms.js | 22 ++++++++++-------- controllers/pages.js | 4 ++-- gulp/res/js/forms.js | 1 + gulp/res/js/hidefileinput.js | 7 +++--- gulp/res/js/uploaditem.js | 5 ++++- migrations/0.1.1.js | 18 +++++++++++++++ models/forms/create.js | 1 + models/pages/manage/assets.js | 11 +++++++++ package.json | 6 ++--- views/includes/filelabel.pug | 2 -- views/includes/postform.pug | 5 +++-- views/mixins/fileform.pug | 33 +++++++++++++++++++++++++++ views/mixins/filelabel.pug | 3 +++ views/mixins/managenav.pug | 2 +- views/mixins/uploaditem.pug | 7 +++--- views/pages/account.pug | 2 +- views/pages/manageassets.pug | 22 ++++++++++++++++++ views/pages/managebanners.pug | 42 ----------------------------------- 19 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 migrations/0.1.1.js create mode 100644 models/pages/manage/assets.js delete mode 100644 views/includes/filelabel.pug create mode 100644 views/mixins/fileform.pug create mode 100644 views/mixins/filelabel.pug create mode 100644 views/pages/manageassets.pug delete mode 100644 views/pages/managebanners.pug diff --git a/configs/template.js.example b/configs/template.js.example index deebda02..ba502933 100644 --- a/configs/template.js.example +++ b/configs/template.js.example @@ -264,6 +264,10 @@ module.exports = { max: 10, //number of banners uploadable in one request total: 100, //max number of banners for a board }, + flagFiles: { + max: 10, //number of banners uploadable in one request + total: 100, //max number of flags for a board + }, bannerFilesSize: { //in bytes, 10MB default max: 10485760 }, diff --git a/controllers/forms.js b/controllers/forms.js index 5363ca69..6af1e0b6 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -35,6 +35,8 @@ const express = require('express') , deleteNewsController = require(__dirname+'/forms/deletenews.js') , uploadBannersController = require(__dirname+'/forms/uploadbanners.js') , deleteBannersController = require(__dirname+'/forms/deletebanners.js') + , addFlagsController = require(__dirname+'/forms/addflags.js') + , deleteFlagsController = require(__dirname+'/forms/deleteflags.js') , boardSettingsController = require(__dirname+'/forms/boardsettings.js') , transferController = require(__dirname+'/forms/transfer.js') , resignController = require(__dirname+'/forms/resign.js') @@ -67,14 +69,16 @@ router.post('/appeal', geoAndTor, torPreBypassCheck, processIp, useSession, sess router.post('/editpost', geoAndTor, torPreBypassCheck, processIp, useSession, sessionRefresh, csrf, paramConverter, Boards.bodyExists, calcPerms, hasPerms(3), editPostController); //board management forms -router.post('/board/:board/transfer', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, transferController); -router.post('/board/:board/settings', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, boardSettingsController); -router.post('/board/:board/addbanners', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, uploadBannersController); //add banners -router.post('/board/:board/deletebanners', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteBannersController); //delete banners -router.post('/board/:board/addcustompages', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, addCustomPageController); //add banners -router.post('/board/:board/deletecustompages', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteCustomPageController); //delete banners -router.post('/board/:board/editbans', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(3), paramConverter, editBansController); //edit bans -router.post('/board/:board/deleteboard', /*geoAndTor, torPreBypassCheck, processIp,*/ useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(config.get.deleteBoardPermLevel), deleteBoardController); //delete board +router.post('/board/:board/transfer', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, transferController); +router.post('/board/:board/settings', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, boardSettingsController); +router.post('/board/:board/addbanners', useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, uploadBannersController); //add banners +router.post('/board/:board/deletebanners', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteBannersController); //delete banners +router.post('/board/:board/addflags', useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, addFlagsController); //add flags +router.post('/board/:board/deleteflags', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteFlagsController); //delete flags +router.post('/board/:board/addcustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, addCustomPageController); //add banners +router.post('/board/:board/deletecustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteCustomPageController); //delete banners +router.post('/board/:board/editbans', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(3), paramConverter, editBansController); //edit bans +router.post('/board/:board/deleteboard', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(config.get.deleteBoardPermLevel), deleteBoardController); //delete board //global management forms router.post('/global/editbans', useSession, sessionRefresh, csrf, calcPerms, isLoggedIn, hasPerms(1), paramConverter, editBansController); //remove bans @@ -98,7 +102,7 @@ router.post('/deleteaccount', useSession, sessionRefresh, csrf, calcPerms, isLog //removes captcha cookie, for refreshing for noscript users router.post('/newcaptcha', newCaptcha); //solve captcha for block bypass -router.post('/blockbypass', geoAndTor, /*torPreBypassCheck,*/ processIp, verifyCaptcha, blockBypass); +router.post('/blockbypass', geoAndTor, processIp, verifyCaptcha, blockBypass); module.exports = router; diff --git a/controllers/pages.js b/controllers/pages.js index 69620a5a..00a3b5d0 100644 --- a/controllers/pages.js +++ b/controllers/pages.js @@ -16,7 +16,7 @@ const express = require('express') , csrf = require(__dirname+'/../helpers/checks/csrfmiddleware.js') , setMinimal = require(__dirname+'/../helpers/setminimal.js') //page models - , { manageRecent, manageReports, manageBanners, manageSettings, manageBans, + , { manageRecent, manageReports, manageAssets, manageSettings, manageBans, manageBoard, manageThread, manageLogs, manageCatalog, manageCustomPages } = require(__dirname+'/../models/pages/manage/') , { globalManageSettings, globalManageReports, globalManageBans, globalManageBoards, globalManageRecent, globalManageAccounts, globalManageNews, globalManageLogs } = require(__dirname+'/../models/pages/globalmanage/') @@ -52,7 +52,7 @@ router.get('/:board/manage/recent.(html|json)', useSession, sessionRefresh, isLo router.get('/:board/manage/bans.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(3), csrf, manageBans); router.get('/:board/manage/logs.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(3), csrf, manageLogs); router.get('/:board/manage/settings.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(2), csrf, manageSettings); -router.get('/:board/manage/banners.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(2), csrf, manageBanners); +router.get('/:board/manage/assets.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(2), csrf, manageAssets); router.get('/:board/manage/custompages.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(2), csrf, manageCustomPages); router.get('/:board/manage/catalog.html', useSession, sessionRefresh, isLoggedIn, Boards.exists, calcPerms, hasPerms(3), csrf, manageCatalog); router.get('/:board/manage/:page(1[0-9]{1,}|[2-9][0-9]{0,}|index).html', useSession, sessionRefresh, isLoggedIn, Boards.exists, paramConverter, calcPerms, hasPerms(3), csrf, manageBoard); diff --git a/gulp/res/js/forms.js b/gulp/res/js/forms.js index 4dd5b996..919822cb 100644 --- a/gulp/res/js/forms.js +++ b/gulp/res/js/forms.js @@ -324,6 +324,7 @@ class formHandler { } const item = { spoilers: this.fileUploadList.dataset.spoilers === 'true', + stripFilenames: this.fileUploadList.dataset.stripFilenames === 'true', name: file.name, hash: fileHash, } diff --git a/gulp/res/js/hidefileinput.js b/gulp/res/js/hidefileinput.js index 58822ffb..c37e75de 100644 --- a/gulp/res/js/hidefileinput.js +++ b/gulp/res/js/hidefileinput.js @@ -1,9 +1,8 @@ -const fileInput = document.getElementById('file'); -if (fileInput) { +document.querySelectorAll('input[type="file"]').forEach(fileInput => { //not using display: none because we still want to show the browser prompt for a "required" file fileInput.style.position = 'absolute'; fileInput.style.border = 'none'; fileInput.style.height = '1px'; fileInput.style.width = '1px'; -// fileInput.style.opacity = '0'; -} +// fileInput.style.opacity = '0'; // same effect as display:none in some browsers, ugh... +}); diff --git a/gulp/res/js/uploaditem.js b/gulp/res/js/uploaditem.js index 927e9924..aacd407e 100644 --- a/gulp/res/js/uploaditem.js +++ b/gulp/res/js/uploaditem.js @@ -12,7 +12,10 @@ pug_html = pug_html + "\u003Cdiv class=\"row sb\"\u003E"; if (item.spoilers) { pug_html = pug_html + "\u003Clabel\u003E\u003Cinput" + (" type=\"checkbox\" name=\"spoiler\""+pug_attr("value", item.hash, true, false)) + "\u002F\u003ESpoiler\u003C\u002Flabel\u003E"; } -pug_html = pug_html + "\u003Clabel\u003E\u003Cinput" + (" type=\"checkbox\" name=\"strip_filename\""+pug_attr("value", item.hash, true, false)) + "\u002F\u003EStrip Filename\u003C\u002Flabel\u003E\u003C\u002Fdiv\u003E"; +if (item.stripFilenames) { +pug_html = pug_html + "\u003Clabel\u003E\u003Cinput" + (" type=\"checkbox\" name=\"strip_filename\""+pug_attr("value", item.hash, true, false)) + "\u002F\u003EStrip Filename\u003C\u002Flabel\u003E"; +} +pug_html = pug_html + "\u003C\u002Fdiv\u003E"; } pug_html = pug_html + "\u003C\u002Fdiv\u003E"; }; diff --git a/migrations/0.1.1.js b/migrations/0.1.1.js new file mode 100644 index 00000000..9d064063 --- /dev/null +++ b/migrations/0.1.1.js @@ -0,0 +1,18 @@ +'use strict'; + +const fs = require('fs-extra'); + +module.exports = async(db, redis) => { + console.log('adding flags customisation db entries'); + const template = require(__dirname+'/../configs/template.js.example'); + await db.collection('globalsettings').updateOne({ _id: 'globalsettings' }, { + '$set': { + 'globalLimits.flagFiles': template.globalLimits.flagFiles, + } + }); + await db.collection('boards').updateMany({}, { + '$set': { + 'flags': [], + } + }); +}; diff --git a/models/forms/create.js b/models/forms/create.js index c088d26d..0763efbc 100644 --- a/models/forms/create.js +++ b/models/forms/create.js @@ -41,6 +41,7 @@ module.exports = async (req, res, next) => { '_id': uri, owner, 'banners': [], + 'flags': [], 'sequence_value': 1, 'pph': 0, 'ppd': 0, diff --git a/models/pages/manage/assets.js b/models/pages/manage/assets.js new file mode 100644 index 00000000..50ffad8c --- /dev/null +++ b/models/pages/manage/assets.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = async (req, res, next) => { + + res + .set('Cache-Control', 'private, max-age=5') + .render('manageassets', { + csrf: req.csrfToken(), + }); + +} diff --git a/package.json b/package.json index 83826cb3..fd90eb31 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "jschan", - "version": "0.1.0", - "migrateVersion": "0.1.0", + "version": "0.1.1", + "migrateVersion": "0.1.1", "description": "", "main": "server.js", "dependencies": { + "@fatchan/gulp-pug": "^4.0.1", "bcrypt": "^5.0.1", "bull": "^3.20.1", "cache-pug-templates": "^2.0.3", @@ -25,7 +26,6 @@ "gulp-clean-css": "^4.3.0", "gulp-concat": "^2.6.1", "gulp-less": "^4.0.1", - "@fatchan/gulp-pug": "^4.0.1", "gulp-replace": "^1.0.0", "gulp-uglify-es": "^2.0.0", "highlight.js": "^10.6.0", diff --git a/views/includes/filelabel.pug b/views/includes/filelabel.pug deleted file mode 100644 index 8a42aad9..00000000 --- a/views/includes/filelabel.pug +++ /dev/null @@ -1,2 +0,0 @@ -label.jsonly.postform-style.filelabel(for='file') - | Select/Drop/Paste file#{maxFiles > 1 ? 's' : ''} diff --git a/views/includes/postform.pug b/views/includes/postform.pug index 338a570d..84b420d5 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -1,3 +1,4 @@ +include ../mixins/filelabel.pug - const isThread = thread != null; - const subjectRequired = (!isThread && board.settings.forceThreadSubject); - const messageRequired = (!isThread && board.settings.forceThreadMessage) || (isThread && board.settings.forceReplyMessage); @@ -52,9 +53,9 @@ section.form-wrapper.flex-center small Max #{maxFiles} files small #{postFilesSize} total span.col - include ./filelabel.pug + +filelabel('file', maxFiles) input#file(type='file', name='file' multiple required=fileRequired ) - .upload-list(data-spoilers=(board.settings.userPostSpoiler ? 'true' : 'false')) + .upload-list(data-spoilers=(board.settings.userPostSpoiler ? 'true' : 'false') data-strip-filenames='false') if board.settings.userPostSpoiler noscript label.postform-style.ph-5.ml-1.fh diff --git a/views/mixins/fileform.pug b/views/mixins/fileform.pug new file mode 100644 index 00000000..1c600299 --- /dev/null +++ b/views/mixins/fileform.pug @@ -0,0 +1,33 @@ +include ./filelabel.pug + +mixin fileform(name, max, total, addpath, deletepath, checkname, fileList) + - const capitalName = `${name.charAt(0).toUpperCase()}${name.substring(1)}`; + h4.no-m-p Add #{capitalName}s (Max #{globalLimits.bannerFiles.total}) + .form-wrapper.flexleft.mt-10 + form.form-post(action=addpath, enctype='multipart/form-data', method='POST') + input(type='hidden' name='_csrf' value=csrf) + .row + .label + span #{capitalName}#{max > 1 ? 's' : ''} + span.required * + if max > 1 + | + | + small (Max #{max}) + span.col + +filelabel(name, max) + input(id=name type='file', name='file' multiple required) + .upload-list(data-spoilers='false' data-strip-filenames='false') + input(type='submit', value='submit') + if fileList.length > 0 + hr(size=1) + h4.no-m-p Delete #{capitalName}s: + .form-wrapper.flexleft.mt-10 + form.form-post(action=deletepath, enctype='application/x-www-form-urlencoded', method='POST') + input(type='hidden' name='_csrf' value=csrf) + .catalog + each file in fileList + label.banner-check + input(type='checkbox' name=checkname value=file) + img.board-banner(src=`${filepath}/${file}` loading='lazy') + input(type='submit', value='delete') diff --git a/views/mixins/filelabel.pug b/views/mixins/filelabel.pug new file mode 100644 index 00000000..d9878840 --- /dev/null +++ b/views/mixins/filelabel.pug @@ -0,0 +1,3 @@ +mixin filelabel(id, max) + label.jsonly.postform-style.filelabel(for=id) + | Select/Drop/Paste file#{max > 1 ? 's' : ''} diff --git a/views/mixins/managenav.pug b/views/mixins/managenav.pug index a3c5bfa3..031726fe 100644 --- a/views/mixins/managenav.pug +++ b/views/mixins/managenav.pug @@ -19,6 +19,6 @@ mixin managenav(selected, upLevel) if permLevel < 3 a(href=`${upLevel ? '../' : ''}settings.html` class=(selected === 'settings' ? 'bold' : '')) [Settings] | - a(href=`${upLevel ? '../' : ''}banners.html` class=(selected === 'banners' ? 'bold' : '')) [Banners] + a(href=`${upLevel ? '../' : ''}assets.html` class=(selected === 'assets' ? 'bold' : '')) [Assets] | a(href=`${upLevel ? '../' : ''}custompages.html` class=(selected === 'custompages' ? 'bold' : '')) [Custom Pages] diff --git a/views/mixins/uploaditem.pug b/views/mixins/uploaditem.pug index e20207f1..a6da14ec 100644 --- a/views/mixins/uploaditem.pug +++ b/views/mixins/uploaditem.pug @@ -10,6 +10,7 @@ mixin uploaditem(item) label input(type='checkbox', name='spoiler', value=item.hash) | Spoiler - label - input(type='checkbox', name='strip_filename', value=item.hash) - | Strip Filename + if item.stripFilenames + label + input(type='checkbox', name='strip_filename', value=item.hash) + | Strip Filename diff --git a/views/pages/account.pug b/views/pages/account.pug index e4232c78..00067d59 100644 --- a/views/pages/account.pug +++ b/views/pages/account.pug @@ -64,7 +64,7 @@ block content | , a(href=`/${b}/manage/settings.html`) Settings | , - a(href=`/${b}/manage/banners.html`) Banners + a(href=`/${b}/manage/assets.html`) Assets | , a(href=`/${b}/manage/custompages.html`) Custom Pages else diff --git a/views/pages/manageassets.pug b/views/pages/manageassets.pug new file mode 100644 index 00000000..e5d47d5c --- /dev/null +++ b/views/pages/manageassets.pug @@ -0,0 +1,22 @@ +extends ../layout.pug +include ../mixins/managenav.pug +include ../mixins/boardheader.pug +include ../mixins/fileform.pug + +block head + title /#{board._id}/ - Manage Assets + +block content + +boardheader('Assets') + br + +managenav('assets') + hr(size=1) + +fileform('banner', globalLimits.bannerFiles.max, globalLimits.bannerFiles.total, + `/forms/board/${board._id}/addbanners`, `/forms/board/${board._id}/deletebanners`, + 'checkedbanners', board.banners) + hr(size=1) + +fileform('flag', globalLimits.flagFiles.max, globalLimits.flagFiles.total, + `/forms/board/${board._id}/addflags`, `/forms/board/${board._id}/deleteflags`, + 'checkedflags', board.flags) + hr(size=1) + p todo: custom other files diff --git a/views/pages/managebanners.pug b/views/pages/managebanners.pug deleted file mode 100644 index 4aad33cd..00000000 --- a/views/pages/managebanners.pug +++ /dev/null @@ -1,42 +0,0 @@ -extends ../layout.pug -include ../mixins/managenav.pug -include ../mixins/boardheader.pug - -block head - title /#{board._id}/ - Manage Banners - -block content - +boardheader('Banners') - br - +managenav('banners') - hr(size=1) - h4.no-m-p Add Banners (Max #{globalLimits.bannerFiles.total}) - .form-wrapper.flexleft.mt-10 - form.form-post(action=`/forms/board/${board._id}/addbanners`, enctype='multipart/form-data', method='POST') - input(type='hidden' name='_csrf' value=csrf) - .row - - const maxFiles = globalLimits.bannerFiles.max; - .label - span Banner#{maxFiles > 1 ? 's' : ''} - span.required * - if maxFiles > 1 - | - | - small (Max #{maxFiles}) - span.col - include ../includes/filelabel.pug - input#file(type='file', name='file' multiple required) - .upload-list - input(type='submit', value='submit') - if board.banners.length > 0 - hr(size=1) - h4.no-m-p Delete Banners: - .form-wrapper.flexleft.mt-10 - form.form-post(action=`/forms/board/${board._id}/deletebanners`, enctype='application/x-www-form-urlencoded', method='POST') - input(type='hidden' name='_csrf' value=csrf) - .catalog - each banner in board.banners - label.banner-check - input(type='checkbox' name='checkedbanners' value=banner) - img.board-banner(src=`/banner/${board._id}/${banner}` loading='lazy') - input(type='submit', value='delete') From 05f1353205d35f0168867b360cccb30076df2fd9 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 09:57:57 +0000 Subject: [PATCH 02/19] package lock --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ea30823e..4bbdad28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "jschan", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { From cc7919bac273f9bc546652fca4503b4f446a631c Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 09:58:06 +0000 Subject: [PATCH 03/19] banners->assets --- models/pages/manage/banners.js | 11 ----------- models/pages/manage/index.js | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 models/pages/manage/banners.js diff --git a/models/pages/manage/banners.js b/models/pages/manage/banners.js deleted file mode 100644 index a7d179f3..00000000 --- a/models/pages/manage/banners.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -module.exports = async (req, res, next) => { - - res - .set('Cache-Control', 'private, max-age=5') - .render('managebanners', { - csrf: req.csrfToken(), - }); - -} diff --git a/models/pages/manage/index.js b/models/pages/manage/index.js index e439617b..1d321cba 100644 --- a/models/pages/manage/index.js +++ b/models/pages/manage/index.js @@ -6,7 +6,7 @@ module.exports = { manageSettings: require(__dirname+'/settings.js'), manageBans: require(__dirname+'/bans.js'), manageLogs: require(__dirname+'/logs.js'), - manageBanners: require(__dirname+'/banners.js'), + manageAssets: require(__dirname+'/assets.js'), manageBoard: require(__dirname+'/board.js'), manageCatalog: require(__dirname+'/catalog.js'), manageThread: require(__dirname+'/thread.js'), From 0cce1026fbaed8cb0d12d5ecdf09c1c4eb9321a4 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 10:48:55 +0000 Subject: [PATCH 04/19] correct migration with something else --- configs/template.js.example | 3 +++ migrations/0.1.1.js | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/configs/template.js.example b/configs/template.js.example index ba502933..6ac56b9c 100644 --- a/configs/template.js.example +++ b/configs/template.js.example @@ -271,6 +271,9 @@ module.exports = { bannerFilesSize: { //in bytes, 10MB default max: 10485760 }, + flagFilesSize: { //in bytes, 1MB default + max: 1048576 + }, /* NOTE: postFilesSize and bannerFilesSize counts in bytes the amount of total data in form submission including other fields like message, name, etc. Therefore a very long message would reduce the space left for files very slightly. To counteract this, consider increasing postFilesSize and bannerFilesSize beyond your desired max filesize by a small margin */ diff --git a/migrations/0.1.1.js b/migrations/0.1.1.js index 9d064063..27a94000 100644 --- a/migrations/0.1.1.js +++ b/migrations/0.1.1.js @@ -3,11 +3,13 @@ const fs = require('fs-extra'); module.exports = async(db, redis) => { - console.log('adding flags customisation db entries'); + console.log('adding flags customisation'); + await fs.ensureDir(`${uploadDirectory}/flag/`); const template = require(__dirname+'/../configs/template.js.example'); await db.collection('globalsettings').updateOne({ _id: 'globalsettings' }, { '$set': { 'globalLimits.flagFiles': template.globalLimits.flagFiles, + 'globalLimits.flagFilesSize': template.globalLimits.flagFilesSize, } }); await db.collection('boards').updateMany({}, { From 5cce251d9b1f553118729a93c416f1d7f7a58efd Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 10:49:21 +0000 Subject: [PATCH 05/19] make the controllers and models exist and do something --- controllers/forms/addflags.js | 37 +++++++++++ controllers/forms/deletebanners.js | 4 +- controllers/forms/deleteflags.js | 39 +++++++++++ controllers/forms/uploadbanners.js | 2 +- db/boards.js | 43 ++++++++---- models/forms/addflags.js | 101 +++++++++++++++++++++++++++++ models/forms/create.js | 3 +- models/forms/deletebanners.js | 2 +- models/forms/deleteboard.js | 3 +- models/forms/deleteflags.js | 25 +++++++ models/forms/uploadbanners.js | 2 +- 11 files changed, 242 insertions(+), 19 deletions(-) create mode 100644 controllers/forms/addflags.js create mode 100644 controllers/forms/deleteflags.js create mode 100644 models/forms/addflags.js create mode 100644 models/forms/deleteflags.js diff --git a/controllers/forms/addflags.js b/controllers/forms/addflags.js new file mode 100644 index 00000000..c5885b77 --- /dev/null +++ b/controllers/forms/addflags.js @@ -0,0 +1,37 @@ +'use strict'; + +const addFlags = require(__dirname+'/../../models/forms/addflags.js') + , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js') + , deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js') + , config = require(__dirname+'/../../config.js'); + +module.exports = async (req, res, next) => { + + const { globalLimits } = config.get; + const errors = []; + + if (res.locals.numFiles === 0) { + errors.push('Must provide a file'); + } else if (res.locals.numFiles > globalLimits.flagFiles.max) { + errors.push(`Exceeded max flag uploads in one request of ${globalLimits.flagFiles.max}`); + } else if (res.locals.board.flags.length+res.locals.numFiles > globalLimits.flagFiles.total) { + errors.push(`Total number of flags would exceed global limit of ${globalLimits.flagFiles.total}`); + } + + if (errors.length > 0) { + await deleteTempFiles(req).catch(e => console.error); + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': `/${req.params.board}/manage/flags.html` + }) + } + + try { + await addFlags(req, res, next); + } catch (err) { + await deleteTempFiles(req).catch(e => console.error); + return next(err); + } + +} diff --git a/controllers/forms/deletebanners.js b/controllers/forms/deletebanners.js index 14494c3a..cdad66f6 100644 --- a/controllers/forms/deletebanners.js +++ b/controllers/forms/deletebanners.js @@ -15,7 +15,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'errors': errors, - 'redirect': `/${req.params.board}/manage/banners.html` + 'redirect': `/${req.params.board}/manage/assets.html` }) } @@ -24,7 +24,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'message': 'Invalid banners selected', - 'redirect': `/${req.params.board}/manage/banners.html` + 'redirect': `/${req.params.board}/manage/assets.html` }) } } diff --git a/controllers/forms/deleteflags.js b/controllers/forms/deleteflags.js new file mode 100644 index 00000000..367ec237 --- /dev/null +++ b/controllers/forms/deleteflags.js @@ -0,0 +1,39 @@ +'use strict'; + +const deleteFlags = require(__dirname+'/../../models/forms/deleteflags.js') + , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js'); + +module.exports = async (req, res, next) => { + + const errors = []; + + if (!req.body.checkedflags || req.body.checkedflags.length === 0) { + errors.push('Must select at least one flag to delete'); + } + + if (errors.length > 0) { + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'errors': errors, + 'redirect': `/${req.params.board}/manage/assets.html` + }) + } + + for (let i = 0; i < req.body.checkedflags.length; i++) { + if (!res.locals.board.flags.includes(req.body.checkedflags[i])) { + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'message': 'Invalid flags selected', + 'redirect': `/${req.params.board}/manage/assets.html` + }) + } + } + + try { + await deleteFlags(req, res, next); + } catch (err) { + console.error(err); + return next(err); + } + +} diff --git a/controllers/forms/uploadbanners.js b/controllers/forms/uploadbanners.js index 16ddb9ae..5429c189 100644 --- a/controllers/forms/uploadbanners.js +++ b/controllers/forms/uploadbanners.js @@ -23,7 +23,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'errors': errors, - 'redirect': `/${req.params.board}/manage/banners.html` + 'redirect': `/${req.params.board}/manage/assets.html` }) } diff --git a/db/boards.js b/db/boards.js index 1f4137da..1a0ddef2 100644 --- a/db/boards.js +++ b/db/boards.js @@ -98,36 +98,55 @@ module.exports = { ); }, - removeBanners: (board, filenames) => { - cache.del(`board:${board}`); - cache.del(`banners:${board}`); + addToArray: (board, key, list) => { return db.updateOne( { '_id': board, }, { - '$pullAll': { - 'banners': filenames + '$push': { + [key]: { + '$each': list + } } } ); + }, - addBanners: (board, filenames) => { - cache.del(`board:${board}`); - cache.del(`banners:${board}`); + removeFromArray: (board, key, list) => { return db.updateOne( { '_id': board, }, { - '$push': { - 'banners': { - '$each': filenames - } + '$pullAll': { + [key]: list } } ); }, + removeBanners: (board, filenames) => { + cache.del(`board:${board}`); + cache.del(`banners:${board}`); + return module.exports.removeFromArray(board, 'banners', filenames); + }, + + addBanners: (board, filenames) => { + cache.del(`board:${board}`); + cache.del(`banners:${board}`); + return module.exports.addToArray(board, 'banners', filenames) + }, + + removeFlags: (board, filenames) => { + cache.del(`board:${board}`); + return module.exports.removeFromArray(board, 'flags', filenames); + }, + + addFlags: (board, filenames) => { + cache.del(`board:${board}`); + return module.exports.addToArray(board, 'flags', filenames) + }, + getLocalListed: async () => { let cachedListed = await cache.sgetall('boards:listed'); if (cachedListed && cachedListed.length > 0) { diff --git a/models/forms/addflags.js b/models/forms/addflags.js new file mode 100644 index 00000000..6cc4bcf9 --- /dev/null +++ b/models/forms/addflags.js @@ -0,0 +1,101 @@ +'use strict'; + +const path = require('path') + , { remove, pathExists } = require('fs-extra') + , config = require(__dirname+'/../../config.js') + , uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js') + , moveUpload = require(__dirname+'/../../helpers/files/moveupload.js') + , mimeTypes = require(__dirname+'/../../helpers/files/mimetypes.js') + , imageIdentify = require(__dirname+'/../../helpers/files/imageidentify.js') + , deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js') + , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js') + , { Boards } = require(__dirname+'/../../db/') + , buildQueue = require(__dirname+'/../../queue.js'); + +module.exports = async (req, res, next) => { + + const { globalLimits, checkRealMimeTypes } = config.get; + const redirect = `/${req.params.board}/manage/assets.html`; + + // check all mime types before we try saving anything + for (let i = 0; i < res.locals.numFiles; i++) { + if (!mimeTypes.allowed(req.files.file[i].mimetype, { + image: true, + animatedImage: true, //gif flags? i guess lol + video: false, + audio: false, + other: false + })) { + await deleteTempFiles(req).catch(e => console.error); + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'message': `Invalid file type for ${req.files.file[i].name}. Mimetype ${req.files.file[i].mimetype} not allowed.`, + 'redirect': redirect + }); + } + } + + // check for any mismatching supposed mimetypes from the actual file mimetype + if (checkRealMimeTypes) { + for (let i = 0; i < res.locals.numFiles; i++) { + if (!(await mimeTypes.realMimeCheck(req.files.file[i]))) { + deleteTempFiles(req).catch(e => console.error); + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'message': `Mime type mismatch for file "${req.files.file[i].name}"`, + 'redirect': redirect + }); + } + } + } + + const filenames = []; + for (let i = 0; i < res.locals.numFiles; i++) { + const file = req.files.file[i]; + const filename = file.sha256 + path.extname(file.name); + file.filename = filename; + + //check if already exists + const exists = await pathExists(`${uploadDirectory}/flag/${req.params.board}/${filename}`); + + if (exists) { + await remove(file.tempFilePath); + continue; + } + + //add to list after checking it doesnt already exist + filenames.push(filename); + + //then upload it + await moveUpload(file, filename, `flag/${req.params.board}`); + + //and delete the temp file + await remove(file.tempFilePath); + + } + + deleteTempFiles(req).catch(e => console.error); + + // no new flags added, so they all must already existed + if (filenames.length === 0) { + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Bad request', + 'message': `Flag${res.locals.numFiles > 1 ? 's' : ''} already exist${res.locals.numFiles > 1 ? '' : 's'}`, + 'redirect': redirect + }); + } + + // add flags in db + await Boards.addFlags(req.params.board, filenames); + + /* + should we rebuild here if (overwriting country flag){}? + */ + + return dynamicResponse(req, res, 200, 'message', { + 'title': 'Success', + 'message': `Uploaded ${filenames.length} new flags.`, + 'redirect': redirect + }); + +} diff --git a/models/forms/create.js b/models/forms/create.js index 0763efbc..66095c52 100644 --- a/models/forms/create.js +++ b/models/forms/create.js @@ -61,7 +61,8 @@ module.exports = async (req, res, next) => { Accounts.addOwnedBoard(owner, uri), ensureDir(`${uploadDirectory}/html/${uri}`), ensureDir(`${uploadDirectory}/json/${uri}`), - ensureDir(`${uploadDirectory}/banner/${uri}`) + ensureDir(`${uploadDirectory}/banner/${uri}`), + ensureDir(`${uploadDirectory}/flag/${uri}`), ]); return res.redirect(`/${uri}/index.html`); diff --git a/models/forms/deletebanners.js b/models/forms/deletebanners.js index ccb2a066..bb6852da 100644 --- a/models/forms/deletebanners.js +++ b/models/forms/deletebanners.js @@ -9,7 +9,7 @@ const { remove } = require('fs-extra') module.exports = async (req, res, next) => { - const redirect = `/${req.params.board}/manage/banners.html`; + const redirect = `/${req.params.board}/manage/assets.html`; //delete file of all selected banners await Promise.all(req.body.checkedbanners.map(async filename => { diff --git a/models/forms/deleteboard.js b/models/forms/deleteboard.js index a86cd688..ece87227 100644 --- a/models/forms/deleteboard.js +++ b/models/forms/deleteboard.js @@ -24,7 +24,8 @@ module.exports = async (uri, board) => { CustomPages.deleteBoard(uri), //custom pages for the board remove(`${uploadDirectory}/html/${uri}/`), //html remove(`${uploadDirectory}/json/${uri}/`), //json - remove(`${uploadDirectory}/banner/${uri}/`) //banners + remove(`${uploadDirectory}/banner/${uri}/`), //banners + remove(`${uploadDirectory}/flag/${uri}/`), //flags ]); } diff --git a/models/forms/deleteflags.js b/models/forms/deleteflags.js new file mode 100644 index 00000000..905845e2 --- /dev/null +++ b/models/forms/deleteflags.js @@ -0,0 +1,25 @@ +'use strict'; + +const { remove } = require('fs-extra') + , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js') + , uploadDirectory = require(__dirname+'/../../helpers/files/uploadDirectory.js') + , { Boards } = require(__dirname+'/../../db/') + +module.exports = async (req, res, next) => { + + const redirect = `/${req.params.board}/manage/assets.html`; + + //delete file of all selected flags + await Promise.all(req.body.checkedflags.map(async filename => { + remove(`${uploadDirectory}/flag/${req.params.board}/${filename}`); + })); + + //remove from db + const amount = await Boards.removeFlags(req.params.board, req.body.checkedflags); + + return dynamicResponse(req, res, 200, 'message', { + 'title': 'Success', + 'message': `Deleted flags.`, + 'redirect': redirect + }); +} diff --git a/models/forms/uploadbanners.js b/models/forms/uploadbanners.js index 70f88202..2b013fd3 100644 --- a/models/forms/uploadbanners.js +++ b/models/forms/uploadbanners.js @@ -15,7 +15,7 @@ const path = require('path') module.exports = async (req, res, next) => { const { globalLimits, checkRealMimeTypes } = config.get; - const redirect = `/${req.params.board}/manage/banners.html`; + const redirect = `/${req.params.board}/manage/assets.html`; // check all mime types before we try saving anything for (let i = 0; i < res.locals.numFiles; i++) { From da266a5d0cc9346869af16d90dd586b09c6b82c8 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 10:50:02 +0000 Subject: [PATCH 06/19] add a file middleware, and fix the file paths for images show in fileform --- gulp/res/css/style.css | 8 ++++++++ helpers/filemiddlewares.js | 28 ++++++++++++++++++++++++++++ helpers/paramconverter.js | 2 +- views/mixins/fileform.pug | 10 +++++----- views/pages/manageassets.pug | 4 ++-- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index ac9bd0f9..1630b724 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -896,6 +896,14 @@ input:invalid, textarea:invalid { min-height: 100px; } +.board-flag { + margin: 5px; + max-width: 100%; + border: 1px solid var(--post-outline-color); + width: 32px; + height: 22px; +} + .board-description { text-align:center; margin: 0; diff --git a/helpers/filemiddlewares.js b/helpers/filemiddlewares.js index faa377d2..1fdd0e8b 100644 --- a/helpers/filemiddlewares.js +++ b/helpers/filemiddlewares.js @@ -8,10 +8,13 @@ const { debugLogs } = require(__dirname+'/../configs/secrets.js') let postFiles, uploadLimitFunction, handleBannerFiles, + handleFlagFiles, numFilesUploadLimitFunction, + numFlagsUploadLimitFunction, numBannersUploadLimitFunction; const updateHandlers = () => { + //this thing is kinda gross const { globalLimits, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../config.js').get uploadLimitFunction = (req, res, next) => { return dynamicResponse(req, res, 413, 'message', { @@ -35,6 +38,13 @@ const updateHandlers = () => { 'redirect': req.headers.referer }); }; + numFlagsUploadLimitFunction = (req, res, next) => { + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Too many files', + 'message': `Max banners per request is ${globalLimits.flagFiles.max}`, + 'redirect': req.headers.referer + }); + }; handleBannerFiles = upload({ debug: debugLogs, createParentPath: true, @@ -52,6 +62,23 @@ const updateHandlers = () => { tempFileDir: __dirname+'/../tmp/' }); module.exports.handleBannerFiles = handleBannerFiles; + handleFlagFiles = upload({ + debug: debugLogs, + createParentPath: true, + safeFileNames: filterFileNames, + spaceFileNameReplacement, + preserveExtension: 4, + limits: { + totalSize: globalLimits.flagFilesSize.max, + fileSize: globalLimits.flagFilesSize.max, + files: globalLimits.flagFiles.max + }, + numFilesLimitHandler: numFlagsUploadLimitFunction, + limitHandler: uploadLimitFunction, + useTempFiles: true, + tempFileDir: __dirname+'/../tmp/' + }); + module.exports.handleFlagFiles = handleFlagFiles; postFiles = upload({ debug: debugLogs, createParentPath: true, @@ -76,6 +103,7 @@ addCallback('config', updateHandlers); module.exports = { handleBannerFiles, + handleFlagFiles, handlePostFilesEarlyTor: (req, res, next) => { if (res.locals.anonymizer) { diff --git a/helpers/paramconverter.js b/helpers/paramconverter.js index cbbe0309..c94b81f5 100644 --- a/helpers/paramconverter.js +++ b/helpers/paramconverter.js @@ -3,7 +3,7 @@ const { ObjectId } = require(__dirname+'/../db/db.js') //todo: separate these into a schema/set for differ ent routes and inject it before the controller, to prevent checkign a bunch of other shit for every post , allowedArrays = new Set(['captcha', 'checkedcustompages', 'checkednews', 'checkedposts', 'globalcheckedposts', 'spoiler', 'strip_filename', - 'checkedreports', 'checkedbans', 'checkedbanners', 'checkedaccounts', 'countries']) + 'checkedreports', 'checkedbans', 'checkedbanners', 'checkedaccounts', 'checkedflags', 'countries']) , trimFields = ['allowed_hosts', 'dnsbl_blacklists', 'other_mime_types', 'highlight_options_language_subset', 'themes', 'code_themes', 'global_limits_custom_css_filters', 'board_defaults_filters', 'filters', 'tags', 'uri', 'moderators', 'announcement', 'description', 'message', 'name', 'subject', 'email', 'postpassword', 'password', 'default_name', 'report_reason', 'ban_reason', 'log_message', 'custom_css'] //trim if we dont want filed with whitespace diff --git a/views/mixins/fileform.pug b/views/mixins/fileform.pug index 1c600299..551196ac 100644 --- a/views/mixins/fileform.pug +++ b/views/mixins/fileform.pug @@ -1,10 +1,10 @@ include ./filelabel.pug -mixin fileform(name, max, total, addpath, deletepath, checkname, fileList) +mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, filePath, imageClass) - const capitalName = `${name.charAt(0).toUpperCase()}${name.substring(1)}`; h4.no-m-p Add #{capitalName}s (Max #{globalLimits.bannerFiles.total}) .form-wrapper.flexleft.mt-10 - form.form-post(action=addpath, enctype='multipart/form-data', method='POST') + form.form-post(action=addPath, enctype='multipart/form-data', method='POST') input(type='hidden' name='_csrf' value=csrf) .row .label @@ -23,11 +23,11 @@ mixin fileform(name, max, total, addpath, deletepath, checkname, fileList) hr(size=1) h4.no-m-p Delete #{capitalName}s: .form-wrapper.flexleft.mt-10 - form.form-post(action=deletepath, enctype='application/x-www-form-urlencoded', method='POST') + form.form-post(action=deletePath, enctype='application/x-www-form-urlencoded', method='POST') input(type='hidden' name='_csrf' value=csrf) .catalog each file in fileList label.banner-check - input(type='checkbox' name=checkname value=file) - img.board-banner(src=`${filepath}/${file}` loading='lazy') + input(type='checkbox' name=checkName value=file) + img(class=imageClass src=`${filePath}/${file}` loading='lazy') input(type='submit', value='delete') diff --git a/views/pages/manageassets.pug b/views/pages/manageassets.pug index e5d47d5c..15a7443c 100644 --- a/views/pages/manageassets.pug +++ b/views/pages/manageassets.pug @@ -13,10 +13,10 @@ block content hr(size=1) +fileform('banner', globalLimits.bannerFiles.max, globalLimits.bannerFiles.total, `/forms/board/${board._id}/addbanners`, `/forms/board/${board._id}/deletebanners`, - 'checkedbanners', board.banners) + 'checkedbanners', board.banners, `/banners/${board._id}`, 'board-banner') hr(size=1) +fileform('flag', globalLimits.flagFiles.max, globalLimits.flagFiles.total, `/forms/board/${board._id}/addflags`, `/forms/board/${board._id}/deleteflags`, - 'checkedflags', board.flags) + 'checkedflags', board.flags, `/flag/${board._id}`, 'board-flag') hr(size=1) p todo: custom other files From 3d1f2bd0160c9da604c2899dbd17c037cd58b382 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 11:18:08 +0000 Subject: [PATCH 07/19] export country name set --- helpers/countries.js | 1 + 1 file changed, 1 insertion(+) diff --git a/helpers/countries.js b/helpers/countries.js index 645d8cd5..8b837636 100644 --- a/helpers/countries.js +++ b/helpers/countries.js @@ -22,6 +22,7 @@ countryNamesMap['LOKI'] = 'Lokinet SNApp'; module.exports = { countryNamesMap, countryCodes, + countryCodesSet: new Set(countryCodes), isAnonymizer: (code) => { return anonymizerCountryCodesSet.has(code); }, From f5c834a5d649e7123d3ff9f1c9b7ffeefc167b7f Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Fri, 12 Mar 2021 11:22:41 +0000 Subject: [PATCH 08/19] make adding flags upcase existing country names and simple check for existing --- models/forms/addflags.js | 16 +++++++++++----- views/mixins/fileform.pug | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/models/forms/addflags.js b/models/forms/addflags.js index 6cc4bcf9..9c2f63bb 100644 --- a/models/forms/addflags.js +++ b/models/forms/addflags.js @@ -9,6 +9,7 @@ const path = require('path') , imageIdentify = require(__dirname+'/../../helpers/files/imageidentify.js') , deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js') , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js') + , { countryCodesSet } = require(__dirname+'/../../helpers/countries.js') , { Boards } = require(__dirname+'/../../db/') , buildQueue = require(__dirname+'/../../queue.js'); @@ -52,11 +53,16 @@ module.exports = async (req, res, next) => { const filenames = []; for (let i = 0; i < res.locals.numFiles; i++) { const file = req.files.file[i]; - const filename = file.sha256 + path.extname(file.name); - file.filename = filename; + let noExt = path.parse(file.name).name; + + //match case for real country flags + if (noExt.length === 2 && countryCodesSet.has(noExt.toUpperCase())) { + file.name = file.name.toUpperCase(); + } //check if already exists - const exists = await pathExists(`${uploadDirectory}/flag/${req.params.board}/${filename}`); + const exists = await res.locals.board.flags + .some(f => path.parse(f).name.toLowerCase() === noExt.toLowerCase()); if (exists) { await remove(file.tempFilePath); @@ -64,10 +70,10 @@ module.exports = async (req, res, next) => { } //add to list after checking it doesnt already exist - filenames.push(filename); + filenames.push(file.name); //then upload it - await moveUpload(file, filename, `flag/${req.params.board}`); + await moveUpload(file, file.name, `flag/${req.params.board}`); //and delete the temp file await remove(file.tempFilePath); diff --git a/views/mixins/fileform.pug b/views/mixins/fileform.pug index 551196ac..0332512c 100644 --- a/views/mixins/fileform.pug +++ b/views/mixins/fileform.pug @@ -30,4 +30,5 @@ mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, fileP label.banner-check input(type='checkbox' name=checkName value=file) img(class=imageClass src=`${filePath}/${file}` loading='lazy') + small #{file.substring(0, file.lastIndexOf('.'))} input(type='submit', value='delete') From 042445d28c3cfe03f315aeb1e2e7722420e73ad2 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 12:43:19 +0000 Subject: [PATCH 09/19] it works, with some kinks still to get through --- configs/template.js.example | 3 ++- controllers/forms.js | 2 +- controllers/forms/addflags.js | 2 +- controllers/forms/deleteflags.js | 2 +- db/boards.js | 16 +++++++++------- gulp/res/css/style.css | 6 ++++++ gulp/res/js/flag.js | 13 +++++++++++++ migrations/0.1.1.js | 11 ++++++++++- models/forms/addflags.js | 28 ++++++---------------------- models/forms/changeboardsettings.js | 3 ++- models/forms/changeglobalsettings.js | 3 ++- models/forms/deleteflags.js | 9 ++++++--- models/forms/makepost.js | 15 +++++++++++++-- views/includes/postform.pug | 10 ++++++++++ views/mixins/fileform.pug | 6 +++--- views/mixins/post.pug | 8 ++++++-- views/pages/globalmanagesettings.pug | 6 +++++- views/pages/manageassets.pug | 4 ++-- views/pages/managesettings.pug | 6 +++++- 19 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 gulp/res/js/flag.js diff --git a/configs/template.js.example b/configs/template.js.example index 6ac56b9c..cbf1c575 100644 --- a/configs/template.js.example +++ b/configs/template.js.example @@ -364,7 +364,8 @@ module.exports = { sageOnlyEmail: false, //only allow sage email early404: true, //delete threads beyond the first 1/3 of pages with less than 5 replies ids: false, //show per thread poster ID based on ip - flags: false, //show geo flags, requires nginx setup + geoFlags: false, //show geo flags, requires nginx setup + customFlags: false, //show custom flags userPostDelete: true, //allow users to delete their posts userPostSpoiler: true, //allow user to spoiler their post files userPostUnlink: true, //alow user to unlink files fomr their post diff --git a/controllers/forms.js b/controllers/forms.js index 6af1e0b6..23726a8e 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -73,7 +73,7 @@ router.post('/board/:board/transfer', useSession, sessionRefresh, csrf, Boards.e router.post('/board/:board/settings', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, boardSettingsController); router.post('/board/:board/addbanners', useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, uploadBannersController); //add banners router.post('/board/:board/deletebanners', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteBannersController); //delete banners -router.post('/board/:board/addflags', useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, addFlagsController); //add flags +router.post('/board/:board/addflags', useSession, sessionRefresh, fileMiddlewares.handleFlagFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, addFlagsController); //add flags router.post('/board/:board/deleteflags', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteFlagsController); //delete flags router.post('/board/:board/addcustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, addCustomPageController); //add banners router.post('/board/:board/deletecustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteCustomPageController); //delete banners diff --git a/controllers/forms/addflags.js b/controllers/forms/addflags.js index c5885b77..b6c55159 100644 --- a/controllers/forms/addflags.js +++ b/controllers/forms/addflags.js @@ -23,7 +23,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'errors': errors, - 'redirect': `/${req.params.board}/manage/flags.html` + 'redirect': `/${req.params.board}/manage/assets.html` }) } diff --git a/controllers/forms/deleteflags.js b/controllers/forms/deleteflags.js index 367ec237..21a08bf0 100644 --- a/controllers/forms/deleteflags.js +++ b/controllers/forms/deleteflags.js @@ -20,7 +20,7 @@ module.exports = async (req, res, next) => { } for (let i = 0; i < req.body.checkedflags.length; i++) { - if (!res.locals.board.flags.includes(req.body.checkedflags[i])) { + if (!res.locals.board.flags[req.body.checkedflags[i]]) { return dynamicResponse(req, res, 400, 'message', { 'title': 'Bad request', 'message': 'Invalid flags selected', diff --git a/db/boards.js b/db/boards.js index 1a0ddef2..f942c92c 100644 --- a/db/boards.js +++ b/db/boards.js @@ -137,14 +137,16 @@ module.exports = { return module.exports.addToArray(board, 'banners', filenames) }, - removeFlags: (board, filenames) => { + setFlags: (board, flags) => { cache.del(`board:${board}`); - return module.exports.removeFromArray(board, 'flags', filenames); - }, - - addFlags: (board, filenames) => { - cache.del(`board:${board}`); - return module.exports.addToArray(board, 'flags', filenames) + //could use dot notation and set flags.x for only changes? seems a bit unsafe though and couldnt have . in name + return db.updateOne({ + '_id': board, + }, { + '$set': { + 'flags': flags, + } + }); }, getLocalListed: async () => { diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 1630b724..23f92703 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -904,6 +904,12 @@ input:invalid, textarea:invalid { height: 22px; } +#selected-flag { + border: 1px solid var(--post-outline-color); + width: 32px; + height: 22px; +} + .board-description { text-align:center; margin: 0; diff --git a/gulp/res/js/flag.js b/gulp/res/js/flag.js new file mode 100644 index 00000000..b4feeac2 --- /dev/null +++ b/gulp/res/js/flag.js @@ -0,0 +1,13 @@ +window.addEventListener('DOMContentLoaded', function(e) { + + const customFlagInput = document.getElementById('customflag'); + const selectedFlagImage = document.getElementById('selected-flag'); + if (customFlagInput && selectedFlagImage) { + const updateFlag = () => { + selectedFlagImage.src = customFlagInput.options[customFlagInput.options.selectedIndex].dataset.src || ''; + }; + customFlagInput.addEventListener('change', updateFlag, false); + updateFlag(); + } + +}); diff --git a/migrations/0.1.1.js b/migrations/0.1.1.js index 27a94000..372fc003 100644 --- a/migrations/0.1.1.js +++ b/migrations/0.1.1.js @@ -1,6 +1,7 @@ 'use strict'; -const fs = require('fs-extra'); +const fs = require('fs-extra') + , uploadDirectory = require(__dirname+'/../helpers/files/uploadDirectory.js'); module.exports = async(db, redis) => { console.log('adding flags customisation'); @@ -10,11 +11,19 @@ module.exports = async(db, redis) => { '$set': { 'globalLimits.flagFiles': template.globalLimits.flagFiles, 'globalLimits.flagFilesSize': template.globalLimits.flagFilesSize, + 'boardDefaults.customFlags': false, + }, + '$rename': { + 'boardDefaults.flags': 'boardDefaults.geoFlags', } }); await db.collection('boards').updateMany({}, { '$set': { 'flags': [], + 'settings.customFlags': false, + }, + '$rename': { + 'settings.flags': 'settings.geoFlags', } }); }; diff --git a/models/forms/addflags.js b/models/forms/addflags.js index 9c2f63bb..bc987aae 100644 --- a/models/forms/addflags.js +++ b/models/forms/addflags.js @@ -50,27 +50,18 @@ module.exports = async (req, res, next) => { } } - const filenames = []; + const newFlags = {}; for (let i = 0; i < res.locals.numFiles; i++) { const file = req.files.file[i]; let noExt = path.parse(file.name).name; //match case for real country flags if (noExt.length === 2 && countryCodesSet.has(noExt.toUpperCase())) { - file.name = file.name.toUpperCase(); - } - - //check if already exists - const exists = await res.locals.board.flags - .some(f => path.parse(f).name.toLowerCase() === noExt.toLowerCase()); - - if (exists) { - await remove(file.tempFilePath); - continue; + noExt = noExt.toUpperCase(); } //add to list after checking it doesnt already exist - filenames.push(file.name); + newFlags[noExt] = file.name; //then upload it await moveUpload(file, file.name, `flag/${req.params.board}`); @@ -82,17 +73,10 @@ module.exports = async (req, res, next) => { deleteTempFiles(req).catch(e => console.error); - // no new flags added, so they all must already existed - if (filenames.length === 0) { - return dynamicResponse(req, res, 400, 'message', { - 'title': 'Bad request', - 'message': `Flag${res.locals.numFiles > 1 ? 's' : ''} already exist${res.locals.numFiles > 1 ? '' : 's'}`, - 'redirect': redirect - }); - } + const updatedFlags = { ...res.locals.board.flags, ...newFlags }; // add flags in db - await Boards.addFlags(req.params.board, filenames); + await Boards.setFlags(req.params.board, updatedFlags); /* should we rebuild here if (overwriting country flag){}? @@ -100,7 +84,7 @@ module.exports = async (req, res, next) => { return dynamicResponse(req, res, 200, 'message', { 'title': 'Success', - 'message': `Uploaded ${filenames.length} new flags.`, + 'message': `Uploaded ${res.locals.numFiles} new flags.`, 'redirect': redirect }); diff --git a/models/forms/changeboardsettings.js b/models/forms/changeboardsettings.js index 455a276d..3c3f2ac9 100644 --- a/models/forms/changeboardsettings.js +++ b/models/forms/changeboardsettings.js @@ -72,7 +72,8 @@ module.exports = async (req, res, next) => { 'unlistedWebring': booleanSetting(req.body.unlisted_webring), 'early404': booleanSetting(req.body.early404), 'ids': booleanSetting(req.body.ids), - 'flags': booleanSetting(req.body.flags), + 'geoFlags': booleanSetting(req.body.geo_flags), + 'customFlags': booleanSetting(req.body.custom_flags), 'forceAnon': booleanSetting(req.body.force_anon), 'sageOnlyEmail': booleanSetting(req.body.sage_only_email), 'userPostDelete': booleanSetting(req.body.user_post_delete), diff --git a/models/forms/changeglobalsettings.js b/models/forms/changeglobalsettings.js index e194e54b..0f662aa8 100644 --- a/models/forms/changeglobalsettings.js +++ b/models/forms/changeglobalsettings.js @@ -243,7 +243,8 @@ module.exports = async (req, res, next) => { sageOnlyEmail: booleanSetting(req.body.board_defaults_sage_only_email, oldSettings.boardDefaults.sageOnlyEmail), early404: booleanSetting(req.body.board_defaults_early_404, oldSettings.boardDefaults.early404), ids: booleanSetting(req.body.board_defaults_ids, oldSettings.boardDefaults.ids), - flags: booleanSetting(req.body.board_defaults_flags, oldSettings.boardDefaults.flags), + customFlags: booleanSetting(req.body.board_defaults_custom_flags, oldSettings.boardDefaults.customFlags), + geoFlags: booleanSetting(req.body.board_defaults_geo_flags, oldSettings.boardDefaults.geoFlags), userPostDelete: booleanSetting(req.body.board_defaults_user_post_delete, oldSettings.boardDefaults.userPostDelete), userPostSpoiler: booleanSetting(req.body.board_defaults_user_post_spoiler, oldSettings.boardDefaults.userPostSpoiler), userPostUnlink: booleanSetting(req.body.board_defaults_user_post_unlink, oldSettings.boardDefaults.userPostUnlink), diff --git a/models/forms/deleteflags.js b/models/forms/deleteflags.js index 905845e2..d8dcacaf 100644 --- a/models/forms/deleteflags.js +++ b/models/forms/deleteflags.js @@ -9,13 +9,16 @@ module.exports = async (req, res, next) => { const redirect = `/${req.params.board}/manage/assets.html`; + const updatedFlags = res.locals.board.flags; + //delete file of all selected flags - await Promise.all(req.body.checkedflags.map(async filename => { - remove(`${uploadDirectory}/flag/${req.params.board}/${filename}`); + await Promise.all(req.body.checkedflags.map(async flagName => { + remove(`${uploadDirectory}/flag/${req.params.board}/${res.locals.board.flags[flagName]}`); + delete res.locals.board.flags[flagName]; })); //remove from db - const amount = await Boards.removeFlags(req.params.board, req.body.checkedflags); + await Boards.setFlags(req.params.board, updatedFlags); return dynamicResponse(req, res, 200, 'message', { 'title': 'Success', diff --git a/models/forms/makepost.js b/models/forms/makepost.js index 9629a334..1aa325ad 100644 --- a/models/forms/makepost.js +++ b/models/forms/makepost.js @@ -54,7 +54,7 @@ module.exports = async (req, res, next) => { const { filterBanDuration, filterMode, filters, blockedCountries, threadLimit, ids, userPostSpoiler, lockReset, captchaReset, pphTrigger, tphTrigger, tphTriggerAction, pphTriggerAction, maxFiles, sageOnlyEmail, forceAnon, replyLimit, disableReplySubject, - captchaMode, lockMode, allowedFileTypes, flags, fileR9KMode, messageR9KMode } = res.locals.board.settings; + captchaMode, lockMode, allowedFileTypes, customFlags, geoFlags, fileR9KMode, messageR9KMode } = res.locals.board.settings; if (res.locals.permLevel >= 4 && res.locals.country && blockedCountries.includes(res.locals.country.code)) { @@ -393,9 +393,20 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')). // } } let country = null; - if (flags === true) { + if (geoFlags === true) { country = res.locals.country; } + if (customFlags === true) { + if (req.body.customflag && res.locals.board.flags[req.body.customflag] != null) { + //if customflags allowed, and its a valid selection + country = { + name: req.body.customflag, + code: req.body.customflag, + src: res.locals.board.flags[req.body.customflag], + custom: true, //this will help + }; + } + } let password = null; if (req.body.postpassword) { password = createHash('sha256').update(postPasswordSecret + req.body.postpassword).digest('base64'); diff --git a/views/includes/postform.pug b/views/includes/postform.pug index 84b420d5..f799db1c 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -65,6 +65,16 @@ section.form-wrapper.flex-center section.row .label Password input(type='password', name='postpassword', placeholder='Password to delete/spoiler/unlink later' maxlength='50') + if modview || board.settings.customFlags === true + - const boardFlags = Object.entries(board.flags) + if boardFlags.length > 0 + section.row + .label Flag + select#customflag(name='customflag') + option(value='') #{board.settings.geoFlags === true ? 'Country Flag' : ''} + each flag in boardFlags + option(value=flag[0] data-src=`/flag/${board._id}/${flag[1]}`) #{flag[0]} + img.jsonly#selected-flag if ((board.settings.captchaMode === 1 && !isThread) || board.settings.captchaMode === 2) && !modview if captchaType === 'text' include ./captchasidelabel.pug diff --git a/views/mixins/fileform.pug b/views/mixins/fileform.pug index 0332512c..8f0b29e7 100644 --- a/views/mixins/fileform.pug +++ b/views/mixins/fileform.pug @@ -1,6 +1,6 @@ include ./filelabel.pug -mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, filePath, imageClass) +mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, nameList, filePath, imageClass) - const capitalName = `${name.charAt(0).toUpperCase()}${name.substring(1)}`; h4.no-m-p Add #{capitalName}s (Max #{globalLimits.bannerFiles.total}) .form-wrapper.flexleft.mt-10 @@ -26,9 +26,9 @@ mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, fileP form.form-post(action=deletePath, enctype='application/x-www-form-urlencoded', method='POST') input(type='hidden' name='_csrf' value=csrf) .catalog - each file in fileList + each file, index in fileList label.banner-check - input(type='checkbox' name=checkName value=file) + input(type='checkbox' name=checkName value=nameList[index]) img(class=imageClass src=`${filePath}/${file}` loading='lazy') small #{file.substring(0, file.lastIndexOf('.'))} input(type='submit', value='delete') diff --git a/views/mixins/post.pug b/views/mixins/post.pug index 496fa3ae..8310eb3b 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -32,8 +32,12 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false, overboar span.post-name #{post.name} | if post.country && post.country.code - span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name) - | + if post.country.custom === true + img.flag(src=`/flag/${post.board}/${post.country.src}` title=post.country.name alt=post.country.name) + | + else + span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name) + | if post.tripcode span.post-tripcode #{post.tripcode} | diff --git a/views/pages/globalmanagesettings.pug b/views/pages/globalmanagesettings.pug index 0081a428..59b9d97d 100644 --- a/views/pages/globalmanagesettings.pug +++ b/views/pages/globalmanagesettings.pug @@ -650,7 +650,11 @@ block content .row .label Geo Flags label.postform-style.ph-5 - input(type='checkbox', name='board_defaults_flags', value='true' checked=settings.boardDefaults.flags) + input(type='checkbox', name='board_defaults_geo_flags', value='true' checked=settings.boardDefaults.geoFlags) + .row + .label Custom Flags + label.postform-style.ph-5 + input(type='checkbox', name='board_defaults_custom_flags', value='true' checked=settings.boardDefaults.customFlags) .row .label User Post Deletion label.postform-style.ph-5 diff --git a/views/pages/manageassets.pug b/views/pages/manageassets.pug index 15a7443c..c841a05c 100644 --- a/views/pages/manageassets.pug +++ b/views/pages/manageassets.pug @@ -13,10 +13,10 @@ block content hr(size=1) +fileform('banner', globalLimits.bannerFiles.max, globalLimits.bannerFiles.total, `/forms/board/${board._id}/addbanners`, `/forms/board/${board._id}/deletebanners`, - 'checkedbanners', board.banners, `/banners/${board._id}`, 'board-banner') + 'checkedbanners', board.banners, board.banners, `/banners/${board._id}`, 'board-banner') hr(size=1) +fileform('flag', globalLimits.flagFiles.max, globalLimits.flagFiles.total, `/forms/board/${board._id}/addflags`, `/forms/board/${board._id}/deleteflags`, - 'checkedflags', board.flags, `/flag/${board._id}`, 'board-flag') + 'checkedflags', Object.values(board.flags), Object.keys(board.flags), `/flag/${board._id}`, 'board-flag') hr(size=1) p todo: custom other files diff --git a/views/pages/managesettings.pug b/views/pages/managesettings.pug index 36bb5f98..36e22255 100644 --- a/views/pages/managesettings.pug +++ b/views/pages/managesettings.pug @@ -77,7 +77,11 @@ block content .row .label Geo Flags label.postform-style.ph-5 - input(type='checkbox', name='flags', value='true' checked=board.settings.flags) + input(type='checkbox', name='geo_flags', value='true' checked=board.settings.geoFlags) + .row + .label Custom Flags + label.postform-style.ph-5 + input(type='checkbox', name='custom_flags', value='true' checked=board.settings.customFlags) .row .label SFW label.postform-style.ph-5 From ad7da047dac822af04074585a82cfbb79b188e52 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 13:01:24 +0000 Subject: [PATCH 10/19] dont show broken image for deleted flag, fallback to ? flag. should they just be completely be deleted from db? because they can be added back later, etc. hmmm... --- gulp/res/css/style.css | 16 +++++++++++++++- views/mixins/post.pug | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 23f92703..31573e75 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -1529,7 +1529,7 @@ row.wrap.sb .col { } -.flag { +.flag, .customflag::before { display: inline-block; width: 16px; height: 11px; @@ -1540,6 +1540,20 @@ row.wrap.sb .col { -ms-interpolation-mode: nearest-neighbor; } +.customflag { + display: inline-block; + width: 16px; + height: 11px; + image-rendering: crisp-edges; + image-rendering: pixelated; + image-rendering: -webkit-optimize-contrast; + -ms-interpolation-mode: nearest-neighbor; +} +.customflag:before { + content: ' '; + background-position:-48px -165px; +} + .flag.flag-xx { background-position:-48px -165px; } diff --git a/views/mixins/post.pug b/views/mixins/post.pug index 8310eb3b..aa88834d 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -33,7 +33,7 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false, overboar | if post.country && post.country.code if post.country.custom === true - img.flag(src=`/flag/${post.board}/${post.country.src}` title=post.country.name alt=post.country.name) + img.customflag(src=`/flag/${post.board}/${post.country.src}` title=post.country.name alt=' ') | else span(class=`flag flag-${post.country.code.toLowerCase()}` title=post.country.name alt=post.country.name) From b122676a6948ca752895ee8a04da2039439e5697 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 13:24:40 +0000 Subject: [PATCH 11/19] make flag script part of form script and reeston submit properly --- gulp/res/css/style.css | 1 - gulp/res/js/flag.js | 13 ------------- gulp/res/js/forms.js | 17 ++++++++++++++++- 3 files changed, 16 insertions(+), 15 deletions(-) delete mode 100644 gulp/res/js/flag.js diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 31573e75..ea8e591c 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -906,7 +906,6 @@ input:invalid, textarea:invalid { #selected-flag { border: 1px solid var(--post-outline-color); - width: 32px; height: 22px; } diff --git a/gulp/res/js/flag.js b/gulp/res/js/flag.js deleted file mode 100644 index b4feeac2..00000000 --- a/gulp/res/js/flag.js +++ /dev/null @@ -1,13 +0,0 @@ -window.addEventListener('DOMContentLoaded', function(e) { - - const customFlagInput = document.getElementById('customflag'); - const selectedFlagImage = document.getElementById('selected-flag'); - if (customFlagInput && selectedFlagImage) { - const updateFlag = () => { - selectedFlagImage.src = customFlagInput.options[customFlagInput.options.selectedIndex].dataset.src || ''; - }; - customFlagInput.addEventListener('change', updateFlag, false); - updateFlag(); - } - -}); diff --git a/gulp/res/js/forms.js b/gulp/res/js/forms.js index 919822cb..9e30ea9b 100644 --- a/gulp/res/js/forms.js +++ b/gulp/res/js/forms.js @@ -93,6 +93,12 @@ class formHandler { this.fileInput.addEventListener('change', e => this.fileInputChange(e)); this.fileLabel.addEventListener('auxclick', e => this.fileLabelAuxclick(e)); } + this.customFlagInput = this.form.elements.customflag; + this.selectedFlagImage = document.getElementById('selected-flag'); + if (this.customFlagInput && this.selectedFlagImage) { + this.customFlagInput.addEventListener('change', () => this.updateFlagField(), false); + this.updateFlagField(); + } this.messageBox && this.messageBox.addEventListener('keydown', e => this.controlEnterSubmit(e)); form.addEventListener('paste', e => this.paste(e)); form.addEventListener('submit', e => this.formSubmit(e)); @@ -102,11 +108,12 @@ class formHandler { const savedName = this.form.elements.name && this.form.elements.name.value; this.form.reset(); if (this.form.elements.name) { - this.form.elements.name.value = savedName + this.form.elements.name.value = savedName; } if (this.form.elements.postpassword) { this.form.elements.postpassword.value = localStorage.getItem('postpassword'); } + this.updateFlagField(); this.updateMessageBox(); this.files = []; this.updateFilesText(); @@ -116,6 +123,14 @@ class formHandler { } } + updateFlagField() { + if (this.customFlagInput) { + const flagSrc = this.customFlagInput.options[this.customFlagInput.options.selectedIndex].dataset.src || ''; + this.selectedFlagImage.src = flagSrc; + this.selectedFlagImage.style.width = flagSrc ? '32px' : '0px'; + } + } + controlEnterSubmit(e) { if (e.ctrlKey && e.key === 'Enter') { this.formSubmit(e); From 163522f02c6db7929f96110dabfe88112850c543 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 22:03:43 +0000 Subject: [PATCH 12/19] playing golf with file middleware helper. came out nice --- controllers/forms.js | 8 +-- helpers/filemiddlewares.js | 141 ++++++++++++------------------------- 2 files changed, 48 insertions(+), 101 deletions(-) diff --git a/controllers/forms.js b/controllers/forms.js index 23726a8e..1abac162 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -54,9 +54,9 @@ const express = require('express') , logout = require(__dirname+'/../models/forms/logout.js'); //make new post -router.post('/board/:board/post', geoAndTor, fileMiddlewares.handlePostFilesEarlyTor, torPreBypassCheck, processIp, useSession, sessionRefresh, Boards.exists, calcPerms, banCheck, fileMiddlewares.handlePostFiles, +router.post('/board/:board/post', geoAndTor, fileMiddlewares.postsEarly, torPreBypassCheck, processIp, useSession, sessionRefresh, Boards.exists, calcPerms, banCheck, fileMiddlewares.posts, paramConverter, verifyCaptcha, numFiles, blockBypassCheck, dnsblCheck, imageHashes, makePostController); -router.post('/board/:board/modpost', geoAndTor, fileMiddlewares.handlePostFilesEarlyTor, torPreBypassCheck, processIp, useSession, sessionRefresh, Boards.exists, calcPerms, banCheck, isLoggedIn, hasPerms(3), fileMiddlewares.handlePostFiles, +router.post('/board/:board/modpost', geoAndTor, fileMiddlewares.postsEarly, torPreBypassCheck, processIp, useSession, sessionRefresh, Boards.exists, calcPerms, banCheck, isLoggedIn, hasPerms(3), fileMiddlewares.posts, paramConverter, csrf, numFiles, blockBypassCheck, dnsblCheck, makePostController); //mod post has token instead of captcha //post actions @@ -71,9 +71,9 @@ router.post('/editpost', geoAndTor, torPreBypassCheck, processIp, useSession, se //board management forms router.post('/board/:board/transfer', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, transferController); router.post('/board/:board/settings', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, boardSettingsController); -router.post('/board/:board/addbanners', useSession, sessionRefresh, fileMiddlewares.handleBannerFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, uploadBannersController); //add banners +router.post('/board/:board/addbanners', useSession, sessionRefresh, fileMiddlewares.banner, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, uploadBannersController); //add banners router.post('/board/:board/deletebanners', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteBannersController); //delete banners -router.post('/board/:board/addflags', useSession, sessionRefresh, fileMiddlewares.handleFlagFiles, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, addFlagsController); //add flags +router.post('/board/:board/addflags', useSession, sessionRefresh, fileMiddlewares.flag, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, numFiles, addFlagsController); //add flags router.post('/board/:board/deleteflags', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteFlagsController); //delete flags router.post('/board/:board/addcustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, addCustomPageController); //add banners router.post('/board/:board/deletecustompages', useSession, sessionRefresh, csrf, Boards.exists, calcPerms, isLoggedIn, hasPerms(2), paramConverter, deleteCustomPageController); //delete banners diff --git a/helpers/filemiddlewares.js b/helpers/filemiddlewares.js index 1fdd0e8b..67891163 100644 --- a/helpers/filemiddlewares.js +++ b/helpers/filemiddlewares.js @@ -3,120 +3,67 @@ const { debugLogs } = require(__dirname+'/../configs/secrets.js') , dynamicResponse = require(__dirname+'/dynamic.js') , { addCallback } = require(__dirname+'/../redis.js') - , upload = require('express-fileupload'); - -let postFiles, - uploadLimitFunction, - handleBannerFiles, - handleFlagFiles, - numFilesUploadLimitFunction, - numFlagsUploadLimitFunction, - numBannersUploadLimitFunction; - -const updateHandlers = () => { - //this thing is kinda gross - const { globalLimits, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../config.js').get - uploadLimitFunction = (req, res, next) => { + , upload = require('express-fileupload') + , fileHandlers = {} + , fileSizeLimitFunction = (req, res, next) => { return dynamicResponse(req, res, 413, 'message', { 'title': 'Payload Too Large', 'message': 'Your upload was too large', 'redirect': req.headers.referer }); - }; - numFilesUploadLimitFunction = (req, res, next) => { - return dynamicResponse(req, res, 400, 'message', { - 'title': 'Too many files', - 'message': res.locals.board ? `Max files per post ${res.locals.board.settings.maxFiles < globalLimits.postFiles.max ? 'on this board ' : ''}is ${res.locals.board.settings.maxFiles}` - : `Max files per request is ${globalLimits.postFiles.max}`, //because of difference in TOR body parsing, we dont populate res.locals.board at this point. something to address later. - 'redirect': req.headers.referer - }); - }; - numBannersUploadLimitFunction = (req, res, next) => { - return dynamicResponse(req, res, 400, 'message', { - 'title': 'Too many files', - 'message': `Max banners per request is ${globalLimits.bannerFiles.max}`, - 'redirect': req.headers.referer - }); - }; - numFlagsUploadLimitFunction = (req, res, next) => { - return dynamicResponse(req, res, 400, 'message', { - 'title': 'Too many files', - 'message': `Max banners per request is ${globalLimits.flagFiles.max}`, - 'redirect': req.headers.referer + } + , updateHandlers = () => { + const { globalLimits, filterFileNames, spaceFileNameReplacement } = require(__dirname+'/../config.js').get; + ['flag', 'banner', 'post'].forEach(fileType => { + //one day this will be more easy to extend + const fileSizeLimit = globalLimits[`${fileType}FilesSize`]; + const fileNumLimit = globalLimits[`${fileType}Files`]; + const fileNumLimitFunction = (req, res, next) => { + return dynamicResponse(req, res, 400, 'message', { + 'title': 'Too many files', + 'message': (req.path.endsWith('/post') && res.locals.board) ? `Max files per post ${res.locals.board.settings.maxFiles < globalLimits.postFiles.max ? 'on this board ' : ''}is ${res.locals.board.settings.maxFiles}` + : `Max files per request is ${fileNumLimit.max}`, + 'redirect': req.headers.referer + }); + }; + fileHandlers[fileType] = upload({ + debug: debugLogs, + createParentPath: true, + safeFileNames: filterFileNames, + spaceFileNameReplacement, + preserveExtension: 4, + limits: { + totalSize: fileSizeLimit.max, + fileSize: fileSizeLimit.max, + files: fileNumLimit.max, + }, + numFilesLimitHandler: fileNumLimitFunction, + limitHandler: fileSizeLimitFunction, + useTempFiles: true, + tempFileDir: __dirname+'/../tmp/' + }); + module.exports[fileType] = fileHandlers[fileType]; }); }; - handleBannerFiles = upload({ - debug: debugLogs, - createParentPath: true, - safeFileNames: filterFileNames, - spaceFileNameReplacement, - preserveExtension: 4, - limits: { - totalSize: globalLimits.bannerFilesSize.max, - fileSize: globalLimits.bannerFilesSize.max, - files: globalLimits.bannerFiles.max - }, - numFilesLimitHandler: numBannersUploadLimitFunction, - limitHandler: uploadLimitFunction, - useTempFiles: true, - tempFileDir: __dirname+'/../tmp/' - }); - module.exports.handleBannerFiles = handleBannerFiles; - handleFlagFiles = upload({ - debug: debugLogs, - createParentPath: true, - safeFileNames: filterFileNames, - spaceFileNameReplacement, - preserveExtension: 4, - limits: { - totalSize: globalLimits.flagFilesSize.max, - fileSize: globalLimits.flagFilesSize.max, - files: globalLimits.flagFiles.max - }, - numFilesLimitHandler: numFlagsUploadLimitFunction, - limitHandler: uploadLimitFunction, - useTempFiles: true, - tempFileDir: __dirname+'/../tmp/' - }); - module.exports.handleFlagFiles = handleFlagFiles; - postFiles = upload({ - debug: debugLogs, - createParentPath: true, - safeFileNames: filterFileNames, - spaceFileNameReplacement, - preserveExtension: 4, - limits: { - totalSize: globalLimits.postFilesSize.max, - fileSize: globalLimits.postFilesSize.max, - files: globalLimits.postFiles.max - }, - numFilesLimitHandler: numFilesUploadLimitFunction, - limitHandler: uploadLimitFunction, - useTempFiles: true, - tempFileDir: __dirname+'/../tmp/' - }); -}; updateHandlers(); addCallback('config', updateHandlers); module.exports = { - handleBannerFiles, - handleFlagFiles, - - handlePostFilesEarlyTor: (req, res, next) => { + banner: fileHandlers.banner, + flag: fileHandlers.flag, + posts: (req, res, next) => { if (res.locals.anonymizer) { - return postFiles(req, res, next); + return next(); } - return next(); + return fileHandlers.post(req, res, next); }, - - handlePostFiles: (req, res, next) => { + postsEarly: (req, res, next) => { if (res.locals.anonymizer) { - return next(); + return fileHandlers.post(req, res, next); } - return postFiles(req, res, next); + return next(); }, -} +}; From 0b03b05d7cae5e04643148853e78abdcb32a216e Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 22:14:38 +0000 Subject: [PATCH 13/19] add a few missing things for this to globalsettings --- controllers/forms/globalsettings.js | 5 ++++- helpers/paramconverter.js | 5 +++-- models/forms/changeglobalsettings.js | 7 +++++++ views/pages/globalmanagesettings.pug | 9 +++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/controllers/forms/globalsettings.js b/controllers/forms/globalsettings.js index aa9cfbb4..0d847bf1 100644 --- a/controllers/forms/globalsettings.js +++ b/controllers/forms/globalsettings.js @@ -99,11 +99,14 @@ module.exports = async (req, res, next) => { { result: minmaxBody(req.body.global_limits_bump_limit_min, req.body.global_limits_bump_limit_max), expected: true, error: 'Global bump limit min must be less than max' }, { result: numberBody(req.body.global_limits_post_files_max), expected: true, error: 'Post files max must be a number' }, { result: numberBody(req.body.global_limits_post_files_size_max), expected: true, error: 'Post files size must be a number' }, - { result: numberBody(req.body.global_limits_banner_files_size_max), expected: true, error: 'Banner files size must be a number' }, { result: numberBody(req.body.global_limits_banner_files_width, 1), expected: true, error: 'Banner files height must be a number > 0' }, { result: numberBody(req.body.global_limits_banner_files_height, 1), expected: true, error: 'Banner files width must be a number > 0' }, + { result: numberBody(req.body.global_limits_banner_files_size_max), expected: true, error: 'Banner files size must be a number' }, { result: numberBody(req.body.global_limits_banner_files_max), expected: true, error: 'Banner files max must be a number' }, { result: numberBody(req.body.global_limits_banner_files_total), expected: true, error: 'Banner files total must be a number' }, + { result: numberBody(req.body.global_limits_flag_files_size_max), expected: true, error: 'Flag files size must be a number' }, + { result: numberBody(req.body.global_limits_flag_files_max), expected: true, error: 'Flag files max must be a number' }, + { result: numberBody(req.body.global_limits_flag_files_total), expected: true, error: 'Flag files total must be a number' }, { result: numberBody(req.body.global_limits_field_length_name), expected: true, error: 'Global limit name field length must be a number' }, { result: numberBody(req.body.global_limits_field_length_email), expected: true, error: 'Global limit email field length must be a number' }, { result: numberBody(req.body.global_limits_field_length_subject), expected: true, error: 'Global limit subject field length must be a number' }, diff --git a/helpers/paramconverter.js b/helpers/paramconverter.js index c94b81f5..ca1922ba 100644 --- a/helpers/paramconverter.js +++ b/helpers/paramconverter.js @@ -17,8 +17,9 @@ const { ObjectId } = require(__dirname+'/../db/db.js') 'lock_wait', 'prune_modlogs', 'prune_ips', 'thumb_size', 'video_thumb_percentage', 'quote_limit', 'preview_replies', 'sticky_preview_replies', 'early_404_fraction', 'early_404_replies', 'max_recent_news', 'highlight_options_threshold', 'global_limits_thread_limit_min', 'global_limits_thread_limit_max', 'global_limits_reply_limit_min', 'global_limits_reply_limit_max', 'global_limits_bump_limit_min', 'global_limits_bump_limit_max', 'global_limits_post_files_max', - 'global_limits_post_files_size_max', 'global_limits_banner_files_width', 'global_limits_banner_files_height', 'global_limits_banner_files_max', 'global_limits_banner_files_total', - 'global_alimits_banner_files_total', 'global_limits_banner_files_size_max', 'global_limits_field_length_name', 'global_limits_field_length_email', + 'global_limits_post_files_size_max', 'global_limits_banner_files_width', 'global_limits_banner_files_height', + 'global_limits_banner_files_max', 'global_limits_banner_files_total', 'global_limits_banner_files_size_max', 'global_limits_flag_files_max', + 'global_limits_flag_files_total', 'global_limits_flag_files_size_max', 'global_limits_field_length_name', 'global_limits_field_length_email', 'global_limits_field_length_subject', 'global_limits_field_length_postpassword', 'global_limits_field_length_message', 'global_limits_field_length_report_reason', 'global_limits_field_length_ban_reason', 'global_limits_field_length_log_message', 'global_limits_field_length_uri', 'global_limits_field_length_boardname', 'global_limits_field_length_description', 'global_limits_multi_input_posts_anon', 'global_limits_multi_input_posts_staff', 'global_limits_custom_css_max', diff --git a/models/forms/changeglobalsettings.js b/models/forms/changeglobalsettings.js index 0f662aa8..6b6caeac 100644 --- a/models/forms/changeglobalsettings.js +++ b/models/forms/changeglobalsettings.js @@ -198,6 +198,13 @@ module.exports = async (req, res, next) => { bannerFilesSize: { max: numberSetting(req.body.global_limits_banner_files_size_max, oldSettings.globalLimits.bannerFilesSize.max), }, + flagFiles: { + max: numberSetting(req.body.global_limits_flag_files_max, oldSettings.globalLimits.flagFiles.max), + total: numberSetting(req.body.global_limits_flag_files_total, oldSettings.globalLimits.flagFiles.total), + }, + flagFilesSize: { + max: numberSetting(req.body.global_limits_flag_files_size_max, oldSettings.globalLimits.flagFilesSize.max), + }, fieldLength: { name: numberSetting(req.body.global_limits_field_length_name, oldSettings.globalLimits.fieldLength.name), email: numberSetting(req.body.global_limits_field_length_email, oldSettings.globalLimits.fieldLength.email), diff --git a/views/pages/globalmanagesettings.pug b/views/pages/globalmanagesettings.pug index 59b9d97d..f93d7d72 100644 --- a/views/pages/globalmanagesettings.pug +++ b/views/pages/globalmanagesettings.pug @@ -529,6 +529,15 @@ block content .row .label Total Banners Per Board input(type='number' name='global_limits_banner_files_total' value=settings.globalLimits.bannerFiles.total) + .row + .label Flag File Size Max + input(type='number' name='global_limits_flag_files_size_max' value=settings.globalLimits.flagFilesSize.max) + .row + .label Flags Per Upload Max + input(type='number' name='global_limits_flag_files_max' value=settings.globalLimits.flagFiles.max) + .row + .label Total Flags Per Board + input(type='number' name='global_limits_flag_files_total' value=settings.globalLimits.flagFiles.total) .row h4.mv-5 Webring From ef8708748c10f784545378642b124adc76117988 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 22:57:19 +0000 Subject: [PATCH 14/19] css optimization --- gulp/res/css/style.css | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index ea8e591c..bb6f8d0a 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -1528,7 +1528,7 @@ row.wrap.sb .col { } -.flag, .customflag::before { +.flag, .customflag, .customflag::before { display: inline-block; width: 16px; height: 11px; @@ -1540,15 +1540,9 @@ row.wrap.sb .col { } .customflag { - display: inline-block; - width: 16px; - height: 11px; - image-rendering: crisp-edges; - image-rendering: pixelated; - image-rendering: -webkit-optimize-contrast; - -ms-interpolation-mode: nearest-neighbor; + background: none; } -.customflag:before { +.customflag::before { content: ' '; background-position:-48px -165px; } From 940007b0e9dfcee1e2bfddb080a8de833b2fd84c Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 23:02:30 +0000 Subject: [PATCH 15/19] fix incorrect total for fileform mixin --- views/mixins/fileform.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/mixins/fileform.pug b/views/mixins/fileform.pug index 8f0b29e7..7d5b29ba 100644 --- a/views/mixins/fileform.pug +++ b/views/mixins/fileform.pug @@ -2,7 +2,7 @@ include ./filelabel.pug mixin fileform(name, max, total, addPath, deletePath, checkName, fileList, nameList, filePath, imageClass) - const capitalName = `${name.charAt(0).toUpperCase()}${name.substring(1)}`; - h4.no-m-p Add #{capitalName}s (Max #{globalLimits.bannerFiles.total}) + h4.no-m-p Add #{capitalName}s (Max #{total}) .form-wrapper.flexleft.mt-10 form.form-post(action=addPath, enctype='multipart/form-data', method='POST') input(type='hidden' name='_csrf' value=csrf) From 325b629a28f8b11b46cf4abadecd1d4749a840c8 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Sun, 14 Mar 2021 23:56:44 +0000 Subject: [PATCH 16/19] allow that to be an actual space if they want, lol --- models/forms/changeglobalsettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/forms/changeglobalsettings.js b/models/forms/changeglobalsettings.js index 6b6caeac..104f6ad5 100644 --- a/models/forms/changeglobalsettings.js +++ b/models/forms/changeglobalsettings.js @@ -162,7 +162,7 @@ module.exports = async (req, res, next) => { early404Replies: numberSetting(req.body.early_404_replies, oldSettings.early404Replies), maxRecentNews: numberSetting(req.body.max_recent_news, oldSettings.maxRecentNews), filterFileNames: booleanSetting(req.body.filter_file_names, oldSettings.filterFileNames), - spaceFileNameReplacement: trimSetting(req.body.space_file_name_replacement, oldSettings.spaceFileNameReplacement), + spaceFileNameReplacement: req.body.space_file_name_replacement, globalLimits: { customCss: { enabled: booleanSetting(req.body.global_limits_custom_css_enabled, oldSettings.globalLimits.customCss.enabled), From ac5b498cd7dbd317f00cbf7a66bb261421a85754 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Wed, 17 Mar 2021 19:01:24 +0000 Subject: [PATCH 17/19] dont like the look of it without any text --- views/includes/postform.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/includes/postform.pug b/views/includes/postform.pug index f799db1c..91a5f6ed 100644 --- a/views/includes/postform.pug +++ b/views/includes/postform.pug @@ -71,7 +71,7 @@ section.form-wrapper.flex-center section.row .label Flag select#customflag(name='customflag') - option(value='') #{board.settings.geoFlags === true ? 'Country Flag' : ''} + option(value='') #{board.settings.geoFlags === true ? 'Geographic Flag' : 'None'} each flag in boardFlags option(value=flag[0] data-src=`/flag/${board._id}/${flag[1]}`) #{flag[0]} img.jsonly#selected-flag From 921d2b14c20bb1e438fe03165eb8f3dbafec1240 Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Wed, 17 Mar 2021 19:24:33 +0000 Subject: [PATCH 18/19] change method of hiding flag input --- gulp/res/css/style.css | 3 ++- gulp/res/js/forms.js | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index bb6f8d0a..1aa8af86 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -904,9 +904,10 @@ input:invalid, textarea:invalid { height: 22px; } -#selected-flag { +#selected-flag[src^='/'] { border: 1px solid var(--post-outline-color); height: 22px; + width: 32px; } .board-description { diff --git a/gulp/res/js/forms.js b/gulp/res/js/forms.js index 9e30ea9b..c574c123 100644 --- a/gulp/res/js/forms.js +++ b/gulp/res/js/forms.js @@ -125,9 +125,7 @@ class formHandler { updateFlagField() { if (this.customFlagInput) { - const flagSrc = this.customFlagInput.options[this.customFlagInput.options.selectedIndex].dataset.src || ''; - this.selectedFlagImage.src = flagSrc; - this.selectedFlagImage.style.width = flagSrc ? '32px' : '0px'; + this.selectedFlagImage.src = this.customFlagInput.options[this.customFlagInput.options.selectedIndex].dataset.src || ''; } } From 441e2be577948e921721f77d41f9987c2615f8fd Mon Sep 17 00:00:00 2001 From: Thomas Lynch Date: Wed, 17 Mar 2021 19:26:52 +0000 Subject: [PATCH 19/19] add changelog remove todo placeholder on assets --- CHANGELOG.md | 6 ++++++ views/pages/manageassets.pug | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..9909a086 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +##### 0.1.1 + - Added changelog + - Version now changes with some kind of meaning + - Animated gif thumbnails no longer generate static image for images < thumbnail dimensions + - Boards management "Banners" page is now "Assets" + - Boards can have custom flags diff --git a/views/pages/manageassets.pug b/views/pages/manageassets.pug index c841a05c..19f41048 100644 --- a/views/pages/manageassets.pug +++ b/views/pages/manageassets.pug @@ -18,5 +18,3 @@ block content +fileform('flag', globalLimits.flagFiles.max, globalLimits.flagFiles.total, `/forms/board/${board._id}/addflags`, `/forms/board/${board._id}/deleteflags`, 'checkedflags', Object.values(board.flags), Object.keys(board.flags), `/flag/${board._id}`, 'board-flag') - hr(size=1) - p todo: custom other files