diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1cfa95ed..239173312d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,66 @@ +#### v4.10.3 (2026-04-15) + +##### Chores + +* up persona (71c6132f) +* incrementing version number - v4.10.2 (c270bb91) +* update changelog for v4.10.2 (64c6417c) +* incrementing version number - v4.10.1 (0c9bbcea) +* incrementing version number - v4.10.0 (5b703104) +* incrementing version number - v4.9.2 (e6846052) +* 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) + +##### Documentation Changes + +* add development setup overview for contributors (#14132) (648344a2) + +##### Bug Fixes + +* copy ip, closes #14177 (d99e46ab) +* don't prepend relative_path to absolute URLs in getProfilePictures (#14176) (37f23190) +* memberPostCids saving, closes #14170 (c40708f4) + +##### Tests + +* set thumbs during topic.post (ac8bad8b) +* another delete after create (ab6d462b) +* fix unread deleted topic test (#14164) (15d65943) + #### v4.10.2 (2026-04-08) ##### Chores diff --git a/install/package.json b/install/package.json index 808343ce51..2ac50ddc24 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "4.10.2", + "version": "4.10.3", "homepage": "https://www.nodebb.org", "repository": { "type": "git", diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index e3d9f24db6..e2f4438fdb 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -495,6 +495,7 @@ define('admin/manage/users', [ }); const tableEl = document.querySelector('.users-table'); + const $tableEl = $(tableEl); const actionBtn = document.getElementById('action-dropdown'); tableEl.addEventListener('change', (e) => { const subselector = e.target.closest('[component="user/select/single"]') || e.target.closest('[component="user/select/all"]'); @@ -509,7 +510,7 @@ define('admin/manage/users', [ }); let lastSelectedUser; - $(tableEl).on('click', '[component="user/select/single"]', function (ev) { + $tableEl.on('click', '[component="user/select/single"]', function (ev) { function selectRange(clickedUserRow) { function selectIndexRange(start, end, isChecked) { if (start > end) { @@ -545,7 +546,7 @@ define('admin/manage/users', [ lastSelectedUser = userRow; }); - $('[data-copy]').on('click', function () { + $tableEl.on('click', '[data-copy]', function () { const btn = $(this); navigator.clipboard.writeText(this.getAttribute('data-copy')); btn.find('i') diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 6023efc11a..268d0c3f66 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -160,11 +160,13 @@ ajaxify.widgets = { render: render }; ajaxify.handleRedirects = function (url) { url = ajaxify.removeRelativePath(url.replace(/^\/|\/$/g, '')).toLowerCase(); - const isClientToAdmin = url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') !== 0; - const isAdminToClient = !url.startsWith('admin') && window.location.pathname.indexOf(config.relative_path + '/admin') === 0; + const urlStartsWithAdmin = url.startsWith('admin'); + const currentPathStartsWithAdmin = window.location.pathname.indexOf(`${config.relative_path}/admin`) === 0; + const isClientToAdmin = urlStartsWithAdmin && !currentPathStartsWithAdmin; + const isAdminToClient = !urlStartsWithAdmin && currentPathStartsWithAdmin; if (isClientToAdmin || isAdminToClient) { - window.open(config.relative_path + '/' + url, '_top'); + window.open(`${config.relative_path}/${url}`, '_top'); return true; } return false; @@ -369,10 +371,12 @@ ajaxify.widgets = { render: render }; }; ajaxify.removeRelativePath = function (url) { - if (config.relative_path && url.startsWith(config.relative_path.slice(1))) { - url = url.slice(config.relative_path.length - 1); - if (url.startsWith('/')) { - url = url.slice(1); + if (config.relative_path && url) { + const prefix = config.relative_path.slice(1); + if (url === prefix) { + return ''; + } else if (url.startsWith(`${prefix}/`)) { + url = url.slice(prefix.length + 1); } } return url; diff --git a/src/socket.io/user/picture.js b/src/socket.io/user/picture.js index d63f28e61c..5800a0e794 100644 --- a/src/socket.io/user/picture.js +++ b/src/socket.io/user/picture.js @@ -39,7 +39,7 @@ module.exports = function (SocketUser) { userPictures.forEach((picture) => { list.pictures.push({ type: 'uploaded', - url: `${nconf.get('relative_path')}${picture}`, + url: picture.startsWith('http') ? picture : `${nconf.get('relative_path')}${picture}`, text: '[[user:uploaded-picture]]', }); }); diff --git a/src/upgrades/1.10.2/fix_category_topic_zsets.js b/src/upgrades/1.10.2/fix_category_topic_zsets.js index 83b4d7b27f..536ffb1401 100644 --- a/src/upgrades/1.10.2/fix_category_topic_zsets.js +++ b/src/upgrades/1.10.2/fix_category_topic_zsets.js @@ -14,15 +14,15 @@ module.exports = { progress.total = await db.sortedSetCard('topics:tid'); await batch.processSortedSet('topics:tid', async (tids) => { progress.incr(tids.length); - const topicData = await db.getObjectFields( + const topicData = await db.getObjectsFields( tids.map(tid => `topic:${tid}`), ['tid', 'cid', 'pinned', 'postcount'], ); const bulkAdd = []; topicData.forEach((topic) => { if (topic && parseInt(topic.pinned, 10) !== 1) { - topicData.postcount = parseInt(topicData.postcount, 10) || 0; - bulkAdd.push([`cid:${topicData.cid}:tids:posts`, topicData.postcount, topicData.tid]); + topic.postcount = parseInt(topic.postcount, 10) || 0; + bulkAdd.push([`cid:${topic.cid}:tids:posts`, topic.postcount, topic.tid]); } }); await db.sortedSetAddBulk(bulkAdd);