diff --git a/CHANGELOG.md b/CHANGELOG.md index cbb3067783..d4de66ec7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,44 @@ +#### v4.6.1 (2025-10-17) + +##### Chores + +* up persona (b309a672) +* up harmony (79327e6c) +* incrementing version number - v4.6.0 (ee395bc5) +* update changelog for v4.6.0 (c0d9bb07) +* incrementing version number - v4.5.2 (ad2da639) +* incrementing version number - v4.5.1 (69f4b61f) +* incrementing version number - v4.5.0 (f05c5d06) +* incrementing version number - v4.4.6 (074043ad) +* incrementing version number - v4.4.5 (6f106923) +* incrementing version number - v4.4.4 (d323af44) +* incrementing version number - v4.4.3 (d354c2eb) +* incrementing version number - v4.4.2 (55c510ae) +* incrementing version number - v4.4.1 (5ae79b4e) +* incrementing version number - v4.4.0 (0a75eee3) +* incrementing version number - v4.3.2 (b92b5d80) +* incrementing version number - v4.3.1 (308e6b9f) +* incrementing version number - v4.3.0 (bff291db) +* incrementing version number - v4.2.2 (17fecc24) +* incrementing version number - v4.2.1 (852a270c) +* incrementing version number - v4.2.0 (87581958) +* incrementing version number - v4.1.1 (b2afbb16) +* incrementing version number - v4.1.0 (36c80850) +* incrementing version number - v4.0.6 (4a52fb2e) +* incrementing version number - v4.0.5 (1792a62b) +* incrementing version number - v4.0.4 (b1125cce) +* incrementing version number - v4.0.3 (2b65c735) +* incrementing version number - v4.0.2 (73fe5fcf) +* incrementing version number - v4.0.1 (a461b758) +* incrementing version number - v4.0.0 (c1eaee45) + +##### Bug Fixes + +* do not include image or icon props if they are falsy values (ecf95d18) +* #13705, don't cover link if preview is opening up (499c50a4) +* logic error in image mime type checking (623cec9d) +* omg what. (ec399897) + #### v4.6.0 (2025-10-01) ##### Chores diff --git a/install/package.json b/install/package.json index d49977d6fb..77d0c46b2c 100644 --- a/install/package.json +++ b/install/package.json @@ -99,7 +99,7 @@ "nodebb-plugin-2factor": "7.6.0", "nodebb-plugin-composer-default": "10.3.1", "nodebb-plugin-dbsearch": "6.3.2", - "nodebb-plugin-emoji": "6.0.3", + "nodebb-plugin-emoji": "6.0.5", "nodebb-plugin-emoji-android": "4.1.1", "nodebb-plugin-markdown": "13.2.1", "nodebb-plugin-mentions": "4.7.6", @@ -108,7 +108,7 @@ "nodebb-rewards-essentials": "1.0.2", "nodebb-theme-harmony": "2.1.21", "nodebb-theme-lavender": "7.1.19", - "nodebb-theme-peace": "2.2.48", + "nodebb-theme-peace": "2.2.49", "nodebb-theme-persona": "14.1.15", "nodebb-widget-essentials": "7.0.40", "nodemailer": "7.0.6", diff --git a/public/src/client/category.js b/public/src/client/category.js index c90b2710ad..9ae2152b41 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -2,7 +2,6 @@ define('forum/category', [ 'forum/infinitescroll', - 'share', 'navigator', 'topicList', 'sort', @@ -11,7 +10,7 @@ define('forum/category', [ 'alerts', 'api', 'clipboard', -], function (infinitescroll, share, navigator, topicList, sort, categorySelector, hooks, alerts, api, clipboard) { +], function (infinitescroll, navigator, topicList, sort, categorySelector, hooks, alerts, api, clipboard) { const Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { diff --git a/public/src/modules/topicList.js b/public/src/modules/topicList.js index bc5a6e4abd..4af7eee9c5 100644 --- a/public/src/modules/topicList.js +++ b/public/src/modules/topicList.js @@ -216,9 +216,12 @@ define('topicList', [ 'reputation:disabled': data['reputation:disabled'], template: { name: templateName, + [templateName]: true, }, }; - tplData.template[templateName] = true; + if (ajaxify.data.cid) { + tplData.cid = ajaxify.data.cid; + } hooks.fire('action:topics.loading', { topics: topics, after: after, before: before }); diff --git a/src/activitypub/index.js b/src/activitypub/index.js index 5971c69326..e64dd66d6b 100644 --- a/src/activitypub/index.js +++ b/src/activitypub/index.js @@ -119,8 +119,13 @@ ActivityPub.resolveInboxes = async (ids) => { if (!meta.config.activitypubAllowLoopback) { ids = ids.filter((id) => { - const { hostname } = new URL(id); - return hostname !== nconf.get('url_parsed').hostname; + try { + const { hostname } = new URL(id); + return hostname !== nconf.get('url_parsed').hostname; + } catch (err) { + winston.error(`[activitypub/resolveInboxes] Invalid id: ${id}`); + return false; + } }); } diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js index 130cb1874f..973e97a7b6 100644 --- a/src/activitypub/notes.js +++ b/src/activitypub/notes.js @@ -137,7 +137,11 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => { const { hostname } = new URL(mainPid); remoteCid = Array.from(set).filter((id, idx) => { const { hostname: cidHostname } = new URL(id); - return assertedGroups[idx] && cidHostname === hostname; + const explicitAudience = Array.isArray(_activitypub.audience) ? + _activitypub.audience.includes(id) : + _activitypub.audience === id; + + return assertedGroups[idx] && (explicitAudience || cidHostname === hostname); }).shift(); } catch (e) { // noop diff --git a/src/categories/update.js b/src/categories/update.js index bf32317ae2..ff4d6e4d11 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -165,9 +165,9 @@ module.exports = function (Categories) { throw new Error('[[error:category.handle-taken]]'); } + await db.sortedSetRemove('categoryhandle:cid', existing); await Promise.all([ db.setObjectField(`category:${cid}`, 'handle', handle), - db.sortedSetRemove('categoryhandle:cid', existing), db.sortedSetAdd('categoryhandle:cid', cid, handle), ]); } diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 581f7d931c..162bea8ecd 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -490,7 +490,7 @@ authenticationController.logout = async function (req, res) { }; await plugins.hooks.fire('filter:user.logout', payload); - if (req.body?.noscript === 'true') { + if (req.body?.noscript === 'true' || res.locals.logoutRedirect === true) { return res.redirect(payload.next); } res.status(200).send(payload); diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index d2cfb48107..11fcebc95a 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -61,7 +61,7 @@ async function uploadAsImage(req, uploadedFile) { throw new Error('[[error:no-privileges]]'); } await image.checkDimensions(uploadedFile.path); - await image.stripEXIF(uploadedFile.path); + await image.stripEXIF({ path: uploadedFile.path, type: uploadedFile.type }); if (plugins.hooks.hasListeners('filter:uploadImage')) { return await plugins.hooks.fire('filter:uploadImage', { diff --git a/src/image.js b/src/image.js index 06e3f2d76d..0d60fddc6a 100644 --- a/src/image.js +++ b/src/image.js @@ -102,14 +102,15 @@ image.size = async function (path) { return imageData ? { width: imageData.width, height: imageData.height } : undefined; }; -image.stripEXIF = async function (path) { - if (!meta.config.stripEXIFData || path.endsWith('.gif') || path.endsWith('.svg')) { +image.stripEXIF = async function ({ path, type }) { + if (!meta.config.stripEXIFData || type === 'image/gif' || type === 'image/svg+xml') { return; } try { if (plugins.hooks.hasListeners('filter:image.stripEXIF')) { await plugins.hooks.fire('filter:image.stripEXIF', { path: path, + type: type, }); return; } diff --git a/src/middleware/multer.js b/src/middleware/multer.js new file mode 100644 index 0000000000..e288fe309e --- /dev/null +++ b/src/middleware/multer.js @@ -0,0 +1,17 @@ +'use strict'; + +const multer = require('multer'); +const storage = multer.diskStorage({}); +const upload = multer({ + storage, + // from https://github.com/TriliumNext/Trilium/pull/3058/files + fileFilter: (req, file, cb) => { + // UTF-8 file names are not well decoded by multer/busboy, so we handle the conversion on our side. + // See https://github.com/expressjs/multer/pull/1102. + file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf-8'); + cb(null, true); + }, +}); + +module.exports = upload; + diff --git a/src/middleware/user.js b/src/middleware/user.js index c49481e625..620c4bad9c 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -53,6 +53,12 @@ module.exports = function (middleware) { } if (req.loggedIn) { + const exists = await user.exists(req.uid); + if (!exists) { + res.locals.logoutRedirect = true; + return controllers.authentication.logout(req, res); + } + return true; } else if (req.headers.hasOwnProperty('authorization')) { const user = await passportAuthenticateAsync(req, res); diff --git a/src/plugins/index.js b/src/plugins/index.js index 47676a5197..f1265d4c7f 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -194,7 +194,7 @@ Plugins.listTrending = async () => { Plugins.normalise = async function (apiReturn) { const pluginMap = {}; - const { dependencies } = require(paths.currentPackage); + const { dependencies } = require(paths.installPackage); apiReturn = Array.isArray(apiReturn) ? apiReturn : []; apiReturn.forEach((packageData) => { packageData.id = packageData.name; @@ -229,7 +229,7 @@ Plugins.normalise = async function (apiReturn) { pluginMap[plugin.id].settingsRoute = plugin.settingsRoute; pluginMap[plugin.id].license = plugin.license; - // If package.json defines a version to use, stick to that + // If install/package.json defines a version to use, stick to that if (dependencies.hasOwnProperty(plugin.id) && semver.valid(dependencies[plugin.id])) { pluginMap[plugin.id].latest = dependencies[plugin.id]; } else { diff --git a/src/routes/admin.js b/src/routes/admin.js index 5421b4d0ef..4788593c5a 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -85,9 +85,7 @@ function apiRoutes(router, name, middleware, controllers) { router.post(`/api/${name}/manage/categories/:cid/name`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.renameRemote)); router.delete(`/api/${name}/manage/categories/:cid`, middleware.ensureLoggedIn, helpers.tryRoute(controllers.admin.categories.removeRemote)); - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); const middlewares = [ upload.array('files[]', 20), diff --git a/src/routes/api.js b/src/routes/api.js index 4424d9a979..79fb83b811 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -23,9 +23,7 @@ module.exports = function (app, middleware, controllers) { router.get('/topic/teaser/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.teaser)); router.get('/topic/pagination/:topic_id', [...middlewares], helpers.tryRoute(controllers.topics.pagination)); - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); const postMiddlewares = [ middleware.maintenanceMode, diff --git a/src/routes/authentication.js b/src/routes/authentication.js index 720675b29d..a4290544a1 100644 --- a/src/routes/authentication.js +++ b/src/routes/authentication.js @@ -154,10 +154,7 @@ Auth.reloadRoutes = async function (params) { }); }); - - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); const middlewares = [ upload.any(), Auth.middleware.applyCSRF, diff --git a/src/routes/helpers.js b/src/routes/helpers.js index 2109a0bd9c..079eb36536 100644 --- a/src/routes/helpers.js +++ b/src/routes/helpers.js @@ -54,9 +54,7 @@ helpers.setupApiRoute = function (...args) { const [router, verb, name] = args; let middlewares = args.length > 4 ? args[args.length - 2] : []; const controller = args[args.length - 1]; - const multer = require('multer'); - const storage = multer.diskStorage({}); - const upload = multer({ storage }); + const upload = require('../middleware/multer'); middlewares = [ middleware.autoLocale, middleware.applyBlacklist, diff --git a/src/views/partials/chats/options.tpl b/src/views/partials/chats/options.tpl index 0c651d503c..88deba08d2 100644 --- a/src/views/partials/chats/options.tpl +++ b/src/views/partials/chats/options.tpl @@ -82,7 +82,7 @@ {{{ if users.length }}} -