From e9da60db3bb3553f6cb8c633e8907d3b88cebbdf Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Thu, 19 Mar 2026 16:20:32 +0000 Subject: [PATCH 01/11] chore: incrementing version number - v4.10.0 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index c31cbf4889..9e90dc0dac 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "4.9.2", + "version": "4.10.0", "homepage": "https://www.nodebb.org", "repository": { "type": "git", @@ -204,4 +204,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file From c480df9e9c8ad5b54bbb7ce77c5d1e087ef76bb7 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Thu, 19 Mar 2026 16:20:32 +0000 Subject: [PATCH 02/11] chore: update changelog for v4.10.0 --- CHANGELOG.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e6f7ec88f..8a6be3f690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,234 @@ +#### v4.10.0 (2026-03-19) + +##### Chores + +* **deps:** + * update dependency lint-staged to v16.4.0 (#14099) (bea46026) + * update commitlint monorepo to v20.5.0 (#14098) (06c3b88b) + * update dependency jsdom to v29 (#14100) (3825c755) + * update commitlint monorepo to v20.4.4 (#14088) (02f8ea2c) + * update dependency lint-staged to v16.3.3 (#14075) (ac45b719) + * update dependency @stylistic/eslint-plugin to v5.10.0 (#14065) (9e2c6b67) + * update docker/build-push-action action to v7 (#14066) (6d8c4493) + * update docker/metadata-action action to v6 (#14067) (73b5bce5) + * update docker/setup-buildx-action action to v4 (#14060) (d7de8cf6) + * update docker/login-action action to v4 (#14054) (8c15096f) + * update dependency lint-staged to v16.3.2 (#14048) (ddd6db0f) + * update commitlint monorepo to v20.4.3 (#14047) (07881cbf) + * update dependency lint-staged to v16.3.1 (#14029) (2e4ee9f1) + * update dependency globals to v17.4.0 (#14035) (32864460) + * update github artifact actions (#14027) (aec68c6b) + * update postgres docker tag to v18.3 (#14023) (b69dbc38) + * update dependency nyc to v18 (#14011) (dc1ce5e1) +* up develop (4a01d55f) +* up mentions (168b17e8) +* up composer (73b023b4) +* up harmony (ec4e87ff) +* incrementing version number - v4.9.2 (e6846052) +* update changelog for v4.9.2 (2c00b137) +* incrementing version number - v4.9.1 (72e44c86) +* incrementing version number - v4.9.0 (3fdd1bef) +* incrementing version number - v4.8.1 (713ae0c0) +* incrementing version number - v4.8.0 (3fac737a) +* incrementing version number - v4.7.2 (cd419d8a) +* incrementing version number - v4.7.1 (afb88805) +* incrementing version number - v4.7.0 (e82d40f8) +* incrementing version number - v4.6.3 (9fc5b0f3) +* incrementing version number - v4.6.2 (f98747db) +* incrementing version number - v4.6.1 (f47aa678) +* incrementing version number - v4.6.0 (ee395bc5) +* 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) +* **i18n:** + * fallback strings for new resources: nodebb.admin-settings-activitypub (aeb53043) + * fallback strings for new resources: nodebb.world (e9063a63) + * fallback strings for new resources: nodebb.world (61414fa4) + * fallback strings for new resources: nodebb.world (702f7c62) + * fallback strings for new resources: nodebb.admin-settings-general (e3ba38f2) + * fallback strings for new resources: nodebb.admin-manage-users (a46f0136) + * fallback strings for new resources: nodebb.admin-advanced-jobs (8e050353) + * fallback strings for new resources: nodebb.admin-menu (5a8a1661) + * fallback strings for new resources: nodebb.error (a17cd6c7) + * fallback strings for new resources: nodebb.world (4d44e913) + * fallback strings for new resources: nodebb.admin-dashboard (49a21a1f) + * fallback strings for new resources: nodebb.admin-dashboard (858d84ff) + +##### Documentation Changes + +* wrong type for worldDefaultCid (895997b2) + +##### New Features + +* add /world as a potential home page route (58d3aa77) +* add category selector to /world quick composer (2f5021e5) +* ability to show only local posts in /world (44e65b8d) +* #14094, notification drawer UX improvements (6c01a5d8) +* allow 3 profile pics (#14092) (533ae69c) +* screenshot upload in ACP, send fallback brand icons in manifest, serve assets for richer PWA install UI (75a6dfff) +* category group actor outbox, #14083 (b317cdd3) +* new ap mocks, now publishing user outboxes (f848393e) +* show cronjobs in acp (#14068) (3c0a6540) +* redirect cold requests to remote resources to their canonical source, #14043 (2b12f8b5) +* include alt text in image/attachment property federating out (ca5aee10) + +##### Bug Fixes + +* improve idempotency of ap test (8ca34e74) +* call syncfollowcounts on unfollow as well (ebe709da) +* sync follow counts on local and remote follows, #14105 (44e78e47) +* cold load redirect should only affect guests (cc606677) +* schema fix for new api config value (7e2c7db3) +* close notif drawer on item click, fix crash in module (7c65471b) +* schema fix for new api config value (efaf8eb9) +* issue where initial quickcreate post wouldn't go to the right cid (35c03e5c) +* only show category selector on quickreply on /world (27b0fbe6) +* delete cid::privilegeMask on category.purge (902533db) +* bump themes (3aa8d5ba) +* removing topic tools/checkbox from /world for guests, reword guest CTA in /world (53286625) +* add back 'after' query param handling in /world that was removed accidentally (67a93da5) +* restrict contextmenu preventDefault to the checkbox only (2eb0964d) +* long-press support for topicSelect, #14045 (d1e1a008) +* debug log (58da9036) +* restore guest access to /world, default to latest(all) (1aa5ca88) +* bump harmony (10859455) +* restored popular calculation behaviour that was broken by e2131d1d2e1c6f14cb8867ac7e22840da3f4c63f, removed followingOnly arg passing for popular (1af83564) +* imagesLoaded integration for handleBack in world.js (38a1da46) +* merged chat notifications if all the messages are from the same user (26bb60ef) +* type (59dd22ca) +* type (40fecd01) +* merged chat notifications if all the messages are from the same user (6147a4d0) +* screenshot fallback (09c54127) +* buildRecipients to handle if local uids are passed in followers (a8f081c0) +* update Like/Dislike to have addressees in activity (c8e349ca) +* accidental hardcoded cid (464bc275) +* skip AP cache on context processing methods (a3ee7447) +* skip AP cache on context processing methods (10e4d579) +* cache key (9eea12ec) +* cache key (74fa77dd) +* missing orderedItems on category outbox index retrieval (8496e1ef) +* #14084, fix tags not getting properly removed from topics (0a94cecb) +* group badge on group details page (52e8ede8) +* return digest header only if it is set to something (aka not null) (1ad9ce5f) +* missing page parseInt (9978af59) +* dont add self username when clicking reply (9fcaad38) +* notifs (ed3a3672) +* also use tx.compile in chat notif merge (6d22e33a) +* publish id with user outbox, fixes #13478 (c08a45a5) +* merge with txArgs (e1d0e2a0) +* merged notification translations (34b68109) +* derped handleBack in world.js (ac483152) +* syntax error on undefined value (6b3b3e7e) +* filter out image attachments from remote data if they are already embedded in content (40b8544f) +* update thumbs loading logic to always include post attachments as part of thumbs (prior: was controlled by thumbsOnly flag orshowPostUploadsAsThumbnail setting) (c2d190e1) +* #14072, world to call thumbs with thumbsOnly filter (f1976168) +* promises in groups.leave (f826e629) +* #14071, duplicate items loaded via IS on /world (d29f1fbd) +* #14043, cold-load redirect should only affect guests (5a7316b1) +* hacking handleBack module to work with world page (971c8603) +* bump web-push (27e12a28) +* patch translateKey to wrap arguments in first string isolate (FSI) and Pop Directional Isolate (PDI) characters (59f19ba4) +* add missing db call (4d1d1c86) +* #14061, world.js show more buttons on infinite scroll (0aead782) +* update clamp-fade to use mask-image, add background to btn-link on Brite skin (9bc1b400) +* #14046, sneak in a mention to the community in mocked replies (b8ef027c) +* world page 'see more' bugs (cc733631) +* #13239, unescape custom user field values (9e69b9ad) +* restore `preview` as it is now supported by BridyFed (8a371d23) +* missing done (9604a0cd) +* bump harmony, #14042 (b02cdaa9) +* #14042, adopt plugin-feed's show-more/less logic/scss (43f2951a) +* update minimum title length default to zero to allow title-less topics via composer (6bfe3cd0) +* parent cid (f567d970) +* skip parsing of duplicate emoji tags (363cad29) +* **deps:** + * update dependency cronstrue to v3.14.0 (#14107) (f51e1b2a) + * update dependency nodemailer to v8.0.3 (#14104) (fa7c1a52) + * update dependency esbuild to v0.27.4 (#14090) (c26bfddf) + * update dependency lru-cache to v11.2.7 (#14096) (3765fb37) + * update dependency tough-cookie to v6.0.1 (#14097) (d5f4a370) + * update dependency nodebb-theme-peace to v2.2.57 (#14076) (cd08a5e4) + * update dependency nodemailer to v8.0.2 (#14077) (add3c651) + * update dependency satori to v0.25.0 (#14037) (817c38b9) + * update dependency postcss to v8.5.8 (#14051) (942619db) + * update dependency pg to v8.20.0 (#14058) (9cc2c2f9) + * update dependency pg-cursor to v2.19.0 (#14059) (045a7073) + * update dependency terser-webpack-plugin to v5.3.17 (#14052) (4410d884) + * update dependency multer to v2.1.1 (#14050) (de22b7a9) + * update dependency webpack to v5.105.4 (#14053) (3dc3b2e2) + * update dependency fs-extra to v11.3.4 (#14049) (cfb6145e) + * update dependency satori to v0.19.3 (#14036) (250911b7) + * update dependency nodebb-plugin-emoji to v6.0.6 (#14034) (7434103c) + * update dependency sitemap to v9.0.1 (#14028) (f97484c2) + * update dependency webpack to v5.105.3 (#14022) (38787a2d) + * update dependency multer to v2.1.0 (#14024) (9b65e316) + * update dependency pg-cursor to v2.18.0 (#14026) (54810cfd) + * update dependency pg to v8.19.0 (#14025) (badb57f2) + * update dependency autoprefixer to v10.4.27 (#14021) (054b4aa6) + +##### Other Changes + +* remove unused (19bb37ca) +* jobs.json (e1b6e617) +* remove unused (d6d3116e) +* remove unused (b50a10df) + +##### Performance Improvements + +* switch to set, remove parseFloat in redis (09de6fb9) +* move out nconf.get and isClientScript regex (4d55ee0a) +* move out nconf.get and isClientScript regex (f2bca332) +* make a single round trip for set(s)Remove (bcbb7bc4) +* cache groups:createtime (380d9895) + +##### Refactors + +* /world sorting logic to always use topics/sorted logic (e2131d1d) +* move to data (36bf3f16) +* get rid of cleanupUids use missing set (6569ea51) +* remove async.series, use batch.processSortedSet (894248e6) +* get rid of helper function (1cc77343) +* switch to cursor (1c7daf0d) +* use set (d9344140) +* pass in cid to rename/remove (fe4a22fb) +* remove admin.themes.getInstalled (92d72f67) + +##### Tests + +* add missing selectedCategory to world.yaml (779a372f) +* exclude uploadScreenshot from routeMap parsing test (ff1e1b92) +* make tests happy (08bed89b) +* fix test maybe (25f6088f) +* add one more topic to tag test (e01cb104) +* cleaner user.delete test (215d6440) +* set minimumtitlelength for test (7429b5d4) +* added test to ensure that Likes do not get processed when privilege is rescinded (8cba65cd) +* break apart inbox handling tests to its own file in test/activitypub (06e0bd6a) +* add debug test to see if failing test is due to race condition (e3119c76) +* fix spec (6dd9f734) + #### v4.9.2 (2026-03-11) ##### Chores From 361134f9a21ff180da9fd6a5e83a1343056da382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Fri, 20 Mar 2026 10:38:03 -0400 Subject: [PATCH 03/11] fix: share url for ap posts, fallback to window.location.href if pid doesnt exist closes #14109 --- public/src/modules/share.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/public/src/modules/share.js b/public/src/modules/share.js index e9e6c27489..94478d30cf 100644 --- a/public/src/modules/share.js +++ b/public/src/modules/share.js @@ -17,7 +17,7 @@ define('share', ['hooks'], function (hooks) { $('#content').off('shown.bs.dropdown', '.share-dropdown').on('shown.bs.dropdown', '.share-dropdown', function () { const postLink = $(this).find('.post-link'); - postLink.val(baseUrl + getPostUrl($(this))); + postLink.val(getPostUrl($(this))); // without the setTimeout can't select the text in the input setTimeout(function () { @@ -77,9 +77,10 @@ define('share', ['hooks'], function (hooks) { } function getPostUrl(clickedElement) { - const pid = parseInt(clickedElement.parents('[data-pid]').attr('data-pid'), 10); - const path = '/post' + (pid ? '/' + (pid) : ''); - return baseUrl + config.relative_path + path; + const pid = clickedElement.parents('[data-pid]').attr('data-pid'); + return pid ? + `${baseUrl + config.relative_path}/post/${pid}` : + window.location.href; } return share; From 43e7f0abb93e8c7d0b543091db6c144bc2877077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 22 Mar 2026 21:25:43 -0400 Subject: [PATCH 04/11] feat: add email share --- public/language/en-GB/topic.json | 2 ++ public/src/modules/share.js | 12 +++++++++++- src/social.js | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/public/language/en-GB/topic.json b/public/language/en-GB/topic.json index ac69995c89..52808ea5bd 100644 --- a/public/language/en-GB/topic.json +++ b/public/language/en-GB/topic.json @@ -104,6 +104,8 @@ "watch.title": "Be notified of new replies in this topic", "unwatch.title": "Stop watching this topic", "share-this-post": "Share this Post", + "share-mail-subject": "Check out this post on \"%1\"", + "share-mail-body": "I thought you might be interested in this post: %1", "watching": "Watching", "not-watching": "Not Watching", "ignoring": "Ignoring", diff --git a/public/src/modules/share.js b/public/src/modules/share.js index 94478d30cf..db6e4e19ac 100644 --- a/public/src/modules/share.js +++ b/public/src/modules/share.js @@ -1,7 +1,7 @@ 'use strict'; -define('share', ['hooks'], function (hooks) { +define('share', ['hooks', 'translator'], function (hooks, translator) { const share = {}; const baseUrl = window.location.protocol + '//' + window.location.host; @@ -69,6 +69,16 @@ define('share', ['hooks'], function (hooks) { return openShare(mastodon_url, postUrl, 626, 760); }); + addHandler('[component="share/email"]', async function () { + const postUrl = getPostUrl($(this)); + const [subject, body] = await translator.translateKeys([ + translator.compile('topic:share-mail-subject', config.siteTitle), + translator.compile('topic:share-mail-body', postUrl), + ]); + const mailtoUrl = `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`; + window.location.href = mailtoUrl; + }); + hooks.fire('action:share.addHandlers', { openShare: openShare }); }; diff --git a/src/social.js b/src/social.js index 9d52114583..c540a391c3 100644 --- a/src/social.js +++ b/src/social.js @@ -45,6 +45,11 @@ social.getPostSharing = async function () { name: 'Mastodon', class: 'fa-brands fa-mastodon', }, + { + id: 'email', + name: 'Email', + class: 'fa-regular fa-envelope', + }, ]; networks = await plugins.hooks.fire('filter:social.posts', networks); networks.forEach((network) => { From 9bcef6b5ea4e28c17b7897a4ebce2d1aa73d6a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Mar 2026 09:43:15 -0400 Subject: [PATCH 05/11] fix: #14116, don't return ban reason if login credentials are incorrect --- src/controllers/authentication.js | 19 +++++++++---------- test/user.js | 11 +++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 63c551c5af..fab6b8d8cc 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -409,21 +409,20 @@ authenticationController.localLogin = async function (req, username, password, n userData.isAdminOrGlobalMod = isAdminOrGlobalMod; - if (!canLoginIfBanned) { - return next(await getBanError(uid)); - } - - // Doing this after the ban check, because user's privileges might change after a ban expires - const hasLoginPrivilege = await privileges.global.can('local:login', uid); - if (parseInt(uid, 10) && !hasLoginPrivilege) { - return next(new Error('[[error:local-login-disabled]]')); - } - try { const passwordMatch = await user.isPasswordCorrect(uid, password, req.ip); if (!passwordMatch) { return next(new Error('[[error:invalid-login-credentials]]')); } + if (!canLoginIfBanned) { + return next(await getBanError(uid)); + } + + // Doing this after the ban check, because user's privileges might change after a ban expires + const hasLoginPrivilege = await privileges.global.can('local:login', uid); + if (parseInt(uid, 10) && !hasLoginPrivilege) { + return next(new Error('[[error:local-login-disabled]]')); + } } catch (e) { if (req.loggedIn) { await logoutAsync(req); diff --git a/test/user.js b/test/user.js index 3eb85d83ed..f6619bd651 100644 --- a/test/user.js +++ b/test/user.js @@ -1394,6 +1394,17 @@ describe('User', () => { assert.strictEqual(await db.isSortedSetMember('users:banned', testUid), false); }); + it('should not return ban reason if login is incorrect', async () => { + const testUid = await User.create({ username: 'bannedUser4', password: '654321' }); + await User.bans.ban(testUid, 0, 'testing bans'); + let { response, body } = await helpers.loginUser('bannedUser4', '5555555'); + assert.strictEqual(response.status, 403); + assert.strictEqual(body, '[[error:invalid-login-credentials]]'); + + ({ response, body } = await helpers.loginUser('bannedUser4', '654321')); + assert.strictEqual(response.status, 403); + assert.strictEqual(body.reason, 'testing bans'); + }); }); describe('Digest.getSubscribers', () => { From ad1433e14b73ff8937d0803e0e7ef6ad42e06472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Mar 2026 09:51:56 -0400 Subject: [PATCH 06/11] fix: #14108, reset filter on notif dropdown open --- public/src/client/header/notifications.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/src/client/header/notifications.js b/public/src/client/header/notifications.js index 94a23c9ecd..7c0a73cfa9 100644 --- a/public/src/client/header/notifications.js +++ b/public/src/client/header/notifications.js @@ -9,6 +9,9 @@ define('forum/header/notifications', function () { notifTrigger.on('show.bs.dropdown', async (ev) => { const notifications = await app.require('notifications'); const triggerEl = $(ev.target); + const dropdownEl = triggerEl.parent().find('.dropdown-menu'); + dropdownEl.find('[data-filter="all"]').addClass('active'); + dropdownEl.find('[data-filter="unread"]').removeClass('active'); notifications.loadNotifications(triggerEl, triggerEl.parent().find('[component="notifications/list"]')); }); From 52e42685e8c87ae8be88488b6f1e15c3a7881099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 23 Mar 2026 11:24:05 -0400 Subject: [PATCH 07/11] fix: key name --- src/user/delete.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/delete.js b/src/user/delete.js index 3de2127ed0..5c15c5c660 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -134,7 +134,7 @@ module.exports = function (User) { `uid:${uid}:flag:pids`, `uid:${uid}:sessions`, `uid:${uid}:shares`, - `uid:${uid}:profile:images`, + `uid:${uid}:profile:pictures`, `invitation:uid:${uid}`, ]; From bd0157c3ebca8e6fa4573f89e2dc4747c510d28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Mar 2026 10:05:52 -0400 Subject: [PATCH 08/11] allow different clam-fade-xx values like clamp-fade-4 vs clamp-fade-sm-4 --- public/src/client/category.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/public/src/client/category.js b/public/src/client/category.js index 1e950e8983..3e86df0ae0 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -115,14 +115,9 @@ define('forum/category', [ } function handleDescription() { - const fadeEl = document.querySelector(`.description.clamp-fade-sm-4`); - if (!fadeEl) { - return; - } - - fadeEl.addEventListener('click', () => { - const state = fadeEl.classList.contains('line-clamp-4'); - fadeEl.classList.toggle('line-clamp-4', !state); + const fadeEl = $(`.description[class*="clamp-fade-"]`); + fadeEl.on('click', function () { + fadeEl.toggleClass('line-clamp-4'); }); } From 9b885162508249e726dec31b730adf70932abf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Mar 2026 10:17:19 -0400 Subject: [PATCH 09/11] refactor: work with different line-clamp values --- public/src/client/category.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/public/src/client/category.js b/public/src/client/category.js index 3e86df0ae0..abcaed35a6 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -117,7 +117,18 @@ define('forum/category', [ function handleDescription() { const fadeEl = $(`.description[class*="clamp-fade-"]`); fadeEl.on('click', function () { - fadeEl.toggleClass('line-clamp-4'); + const $this = $(this); + let clampClass = $this.data('clampClass'); + if (!clampClass) { + const match = $this.attr('class').match(/line-clamp-(\S+)/); + if (match && match[1]) { + clampClass = `line-clamp-${match[1]}`; + fadeEl.data('clampClass', clampClass); + } + } + if (clampClass) { + fadeEl.toggleClass(clampClass); + } }); } From a10471fce766c3c070a60b3d2290ada3dcf66281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 24 Mar 2026 12:43:57 -0400 Subject: [PATCH 10/11] fix: #14121, use normalizedPath when uploading add test for normalize --- src/user/picture.js | 2 +- test/user.js | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/user/picture.js b/src/user/picture.js index db135b463f..fe8a99c9a8 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -143,7 +143,7 @@ module.exports = function (User) { const filename = generateProfileImageFilename(updateUid, extension); const uploadedImage = await image.uploadImage(filename, `profile/uid-${updateUid}`, { uid: updateUid, - path: picture.path, + path: normalizedPath, name: 'profileAvatar', }); diff --git a/test/user.js b/test/user.js index f6619bd651..cd0ba94e57 100644 --- a/test/user.js +++ b/test/user.js @@ -1144,6 +1144,30 @@ describe('User', () => { }); }); + it('should normalize uploaded image to png', async () => { + const oldValue = meta.config['profile:convertProfileImageToPNG']; + meta.config['profile:convertProfileImageToPNG'] = 1; + + const uid = await User.create({ username: 'pngnormalize', password: '123456' }); + const { jar, csrf_token } = await helpers.loginUser('pngnormalize', '123456'); + const pathToJpeg = path.join(__dirname, '../test/files/normalise.jpg'); + + const { response } = await helpers.uploadFile( + `${nconf.get('url')}/api/user/pngnormalize/uploadpicture`, + pathToJpeg, { }, jar, csrf_token + ); + assert.strictEqual(response.statusCode, 200); + const picture = await db.getObjectField(`user:${uid}`, 'picture'); + const uploadedPath = path.join( + nconf.get('upload_path'), `${picture.replace(nconf.get('upload_url'), '')}` + ); + const sharp = require('sharp'); + const metadata = await sharp(uploadedPath).metadata(); + assert.strictEqual(metadata.format, 'png'); + + meta.config['profile:convertProfileImageToPNG'] = oldValue; + }); + it('should not allow image data with bad MIME type to be passed in', (done) => { User.uploadCroppedPicture({ callerUid: uid, From 72f48fd9c47eaddfacd85d5ad963236190270278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 25 Mar 2026 10:21:24 -0400 Subject: [PATCH 11/11] fix: #14123, aria-hidden fixes add aria-labels --- install/package.json | 2 +- src/views/admin/extend/plugins.tpl | 2 +- src/views/admin/footer.tpl | 2 +- src/views/admin/manage/tags.tpl | 4 ++-- src/views/admin/partials/create_group_modal.tpl | 4 ++-- src/views/chat.tpl | 4 ++-- src/views/modals/crop_picture.tpl | 6 +++--- src/views/modals/flag.tpl | 4 ++-- src/views/modals/upload-file.tpl | 6 +++--- src/views/modals/upload-picture-from-url.tpl | 6 +++--- src/views/partials/reconnect-alert.tpl | 2 +- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/install/package.json b/install/package.json index 9e90dc0dac..47294dcd3e 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.2", "nodebb-plugin-web-push": "0.7.7", "nodebb-rewards-essentials": "1.0.2", - "nodebb-theme-harmony": "2.2.61", + "nodebb-theme-harmony": "2.2.62", "nodebb-theme-lavender": "7.1.21", "nodebb-theme-peace": "2.2.57", "nodebb-theme-persona": "14.2.33", diff --git a/src/views/admin/extend/plugins.tpl b/src/views/admin/extend/plugins.tpl index c43e286411..34f2de1bfe 100644 --- a/src/views/admin/extend/plugins.tpl +++ b/src/views/admin/extend/plugins.tpl @@ -125,7 +125,7 @@