From 97530589bf2d8098673895e2e825f2116b6de0de Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Wed, 29 Jan 2025 19:26:28 +0000 Subject: [PATCH 01/14] chore: incrementing version number - v4.0.1 --- install/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/package.json b/install/package.json index 99dce17367..b083516b7a 100644 --- a/install/package.json +++ b/install/package.json @@ -2,7 +2,7 @@ "name": "nodebb", "license": "GPL-3.0", "description": "NodeBB Forum", - "version": "4.0.0", + "version": "4.0.1", "homepage": "https://www.nodebb.org", "repository": { "type": "git", @@ -200,4 +200,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file From 3dbd2b308fdf52cf5e962b353af78460e4664bf1 Mon Sep 17 00:00:00 2001 From: Misty Release Bot Date: Wed, 29 Jan 2025 19:26:28 +0000 Subject: [PATCH 02/14] chore: update changelog for v4.0.1 --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54e488c9d6..a76bef6d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,52 @@ +#### v4.0.1 (2025-01-29) + +##### Chores + +* up dbsearch (88fa4553) +* up benchpress (c9584800) +* up harmony (10409e0e) +* up themes (6918c3f3) +* up themes (050effe2) +* up harmony (90e0a2d6) +* incrementing version number - v4.0.0 (c1eaee45) +* update changelog for v4.0.0 (ae8f58d6) + +##### New Features + +* use text-danger if chat over limit (2f5b4b29) + +##### Bug Fixes + +* #13087, disallow following cid -1 (ddb6e0f3) +* encoding of pid in notifyCategoryFollowers, #13087 (6d88dcb2) +* #13084 bump persona (4feda224) +* closes #13091, dont show world category (4c66eed9) +* #13088, up dbsearch (8644565a) +* #13090, update themes fix selector (822bff62) +* #13086 move rateLimit check (487d9f73) +* null checks for category sync and actor assertions (b3b8b9e9) +* #13067, add sourceContent to teasers (679fcb71) +* #13065, send missing `actor` property when 1b12 announcing local posts (e61df4de) +* closes #13068, encodeURIComponent X-Redirect (f3b8ed27) +* #13062 add displayname to email tpl data (f0c2090d) + +##### Other Changes + +* missing ; (8b38cb3a) +* reduce image size (#12702) (a95a51c6) + +##### Refactors + +* 🤡 (4ba01d18) + +##### Tests + +* adjust webfinger test for updated 404 status code (4a827b7e) +* fix x-redirect tests (b80440aa) +* add sourceContent to spec (526a9521) +* change test to 404 (52f7f0a7) +* remove only (0ba4ba65) + #### v4.0.0 (2025-01-20) ##### Breaking Changes From 933c18f4ac0e197ec61dad2025ec76c43a51fbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 16:01:22 -0500 Subject: [PATCH 03/14] feat: add description and keywords to api/config --- src/controllers/api.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/api.js b/src/controllers/api.js index 2e6bb661fc..d0120b692e 100644 --- a/src/controllers/api.js +++ b/src/controllers/api.js @@ -32,6 +32,8 @@ apiController.loadConfig = async function (req) { assetBaseUrl: asset_base_url, // deprecate in 1.20.x siteTitle: validator.escape(String(meta.config.title || meta.config.browserTitle || 'NodeBB')), browserTitle: validator.escape(String(meta.config.browserTitle || meta.config.title || 'NodeBB')), + description: validator.escape(String(meta.config.description || '')), + keywords: validator.escape(String(meta.config.keywords || '')), titleLayout: (meta.config.titleLayout || '{pageTitle} | {browserTitle}').replace(/{/g, '{').replace(/}/g, '}'), showSiteTitle: meta.config.showSiteTitle === 1, maintenanceMode: meta.config.maintenanceMode === 1, From 0b92d525937602c1f50894c392dbdbef81a07cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 16:06:03 -0500 Subject: [PATCH 04/14] fix: closes #13096, fix regression from renaming language files https://github.com/NodeBB/NodeBB/commit/a76781859c70d27a60ce71a701867a2fd9a2d1f6#diff-b2c5ad612412b958d1df03c07abfa9c4250b3256238502097d2639df203d7fed --- public/language/en-GB/admin/settings/general.json | 3 ++- src/views/admin/settings/general.tpl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/language/en-GB/admin/settings/general.json b/public/language/en-GB/admin/settings/general.json index 6848747e86..d56c819745 100644 --- a/public/language/en-GB/admin/settings/general.json +++ b/public/language/en-GB/admin/settings/general.json @@ -15,7 +15,7 @@ "title-layout": "Title Layout", "title-layout-help": "Define how the browser title will be structured ie. {pageTitle} | {browserTitle}", "description.placeholder": "A short description about your community", - "description": "Choose what page is shown when users navigate to the root URL of your forum.", + "description": "Site Description", "keywords": "Site Keywords", "keywords-placeholder": "Keywords describing your community, comma-separated", "logo-and-icons": "Site Logo & Icons", @@ -51,6 +51,7 @@ "topic-tools": "Topic Tools", "home-page": "Home Page", "home-page-route": "Home Page Route", + "home-page-description": "Choose what page is shown when users navigate to the root URL of your forum.", "custom-route": "Custom Route", "allow-user-home-pages": "Allow User Home Pages", "home-page-title": "Title of the home page (default \"Home\")", diff --git a/src/views/admin/settings/general.tpl b/src/views/admin/settings/general.tpl index 7070fbf0ee..0d75c9d601 100644 --- a/src/views/admin/settings/general.tpl +++ b/src/views/admin/settings/general.tpl @@ -162,7 +162,7 @@

- [[admin/settings/general:description]] + [[admin/settings/general:home-page-description]]

From 4d7335903ad05d831ba2495ab22af8d313568f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 18:01:52 -0500 Subject: [PATCH 05/14] feat: add uid to post.parent --- src/topics/posts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/topics/posts.js b/src/topics/posts.js index d9c20d3dc1..353dcddb1e 100644 --- a/src/topics/posts.js +++ b/src/topics/posts.js @@ -196,6 +196,7 @@ module.exports = function (Topics) { parentPosts.forEach((post, i) => { if (usersMap[post.uid]) { parents[parentPids[i]] = { + uid: post.uid, username: usersMap[post.uid].username, displayname: usersMap[post.uid].displayname, }; From 47734d4cd3dd997448776db583c67cd43925b12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 18:26:25 -0500 Subject: [PATCH 06/14] test: fix schema --- public/openapi/read/config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/openapi/read/config.yaml b/public/openapi/read/config.yaml index 5fb04f1f63..626e590e42 100644 --- a/public/openapi/read/config.yaml +++ b/public/openapi/read/config.yaml @@ -23,6 +23,10 @@ get: type: string browserTitle: type: string + description: + type: string + keywords: + type: string titleLayout: type: string showSiteTitle: From d4a1b4dad9421bb5f4c20af6c55adac01f425bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 18:27:46 -0500 Subject: [PATCH 07/14] refactor: remove old comment --- src/api/topics.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/topics.js b/src/api/topics.js index 10d1c4c10e..81c1ee876a 100644 --- a/src/api/topics.js +++ b/src/api/topics.js @@ -106,7 +106,7 @@ topicsAPI.reply = async function (caller, data) { return await posts.addToQueue(payload); } - const postData = await topics.reply(payload); // postData seems to be a subset of postObj, refactor? + const postData = await topics.reply(payload); const result = { posts: [postData], From ef5ae006528e65864808cdf90708a1a6eee78766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 29 Jan 2025 18:35:34 -0500 Subject: [PATCH 08/14] test: fix schema --- public/openapi/read/admin/config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/openapi/read/admin/config.yaml b/public/openapi/read/admin/config.yaml index e5a65fa040..4b3fa7ffdc 100644 --- a/public/openapi/read/admin/config.yaml +++ b/public/openapi/read/admin/config.yaml @@ -23,6 +23,10 @@ get: type: string browserTitle: type: string + description: + type: string + keywords: + type: string titleLayout: type: string showSiteTitle: From be62ae24ade1450ecbb631e33a2c1f4ffe7671d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 30 Jan 2025 10:22:45 -0500 Subject: [PATCH 09/14] feat: allow selecting empty for custom selects closes #13101 --- src/controllers/accounts/helpers.js | 6 +++++- src/user/profile.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 4c289e5007..53ecea8534 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -194,7 +194,11 @@ helpers.getCustomUserFields = async function (callerUID, userData) { if (f.type === 'input-link' && userValue) { f.linkValue = validator.escape(String(userValue.replace('http://', '').replace('https://', ''))); } - f['select-options'] = (f['select-options'] || '').split('\n').filter(Boolean).map( + f['select-options'] = (f['select-options'] || '').split('\n').filter(Boolean); + if (f.type === 'select') { + f['select-options'].unshift(''); + } + f['select-options'] = f['select-options'].map( opt => ({ value: opt, selected: Array.isArray(userValue) ? diff --git a/src/user/profile.js b/src/user/profile.js index 40a0d3bc99..c150675d6a 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -127,7 +127,7 @@ module.exports = function (User) { )); } else if (field.type === 'select') { const opts = field['select-options'].split('\n').filter(Boolean); - if (!opts.includes(value)) { + if (!opts.includes(value) && value !== '') { throw new Error(tx.compile( 'error:custom-user-field-select-value-invalid', field.name )); From 6672de00cb867416e8ee2d8ef39df10607b5b361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Thu, 30 Jan 2025 19:32:02 -0500 Subject: [PATCH 10/14] chore: up themes, closes #13102 --- install/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/package.json b/install/package.json index b083516b7a..5fe0432356 100644 --- a/install/package.json +++ b/install/package.json @@ -108,10 +108,10 @@ "nodebb-plugin-spam-be-gone": "2.3.0", "nodebb-plugin-web-push": "0.7.2", "nodebb-rewards-essentials": "1.0.0", - "nodebb-theme-harmony": "2.0.5", + "nodebb-theme-harmony": "2.0.6", "nodebb-theme-lavender": "7.1.17", - "nodebb-theme-peace": "2.2.35", - "nodebb-theme-persona": "14.0.5", + "nodebb-theme-peace": "2.2.36", + "nodebb-theme-persona": "14.0.6", "nodebb-widget-essentials": "7.0.32", "nodemailer": "6.9.16", "nprogress": "0.2.0", From d77d2055c3d41778997858405a46f4eccbf99899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 1 Feb 2025 17:24:15 -0500 Subject: [PATCH 11/14] chore: up harmony --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 5fe0432356..7a367d89f1 100644 --- a/install/package.json +++ b/install/package.json @@ -108,7 +108,7 @@ "nodebb-plugin-spam-be-gone": "2.3.0", "nodebb-plugin-web-push": "0.7.2", "nodebb-rewards-essentials": "1.0.0", - "nodebb-theme-harmony": "2.0.6", + "nodebb-theme-harmony": "2.0.7", "nodebb-theme-lavender": "7.1.17", "nodebb-theme-peace": "2.2.36", "nodebb-theme-persona": "14.0.6", From 0298a3af0d4831dbe0f003ec366e0013e98a0735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sat, 1 Feb 2025 17:40:36 -0500 Subject: [PATCH 12/14] chore: up persona --- install/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/package.json b/install/package.json index 7a367d89f1..448ef25a0a 100644 --- a/install/package.json +++ b/install/package.json @@ -111,7 +111,7 @@ "nodebb-theme-harmony": "2.0.7", "nodebb-theme-lavender": "7.1.17", "nodebb-theme-peace": "2.2.36", - "nodebb-theme-persona": "14.0.6", + "nodebb-theme-persona": "14.0.8", "nodebb-widget-essentials": "7.0.32", "nodemailer": "6.9.16", "nprogress": "0.2.0", From 1cdf37a21816f07f1dc82353cbff5029bfaef9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 2 Feb 2025 03:30:52 -0500 Subject: [PATCH 13/14] list remove all (#13113) * list remove all * one more test * sortedSetIncrByBulk * remove name * incrObjectFieldByBulk * test: disable api tests * try merge * another test * give upon bulk incr * update so answer * one more try * fix: name * chore: up dbsearch --- install/package.json | 4 ++-- src/database/mongo/hash.js | 2 +- src/database/postgres/hash.js | 38 ++++++++++++++++++++++++++------- src/database/postgres/list.js | 21 ++++++++++++++++-- src/database/postgres/sorted.js | 34 +++++++++++++++++++++++++++-- 5 files changed, 84 insertions(+), 15 deletions(-) diff --git a/install/package.json b/install/package.json index 448ef25a0a..b715497852 100644 --- a/install/package.json +++ b/install/package.json @@ -100,8 +100,8 @@ "nconf": "0.12.1", "nodebb-plugin-2factor": "7.5.8", "nodebb-plugin-composer-default": "10.2.44", - "nodebb-plugin-dbsearch": "6.2.7", - "nodebb-plugin-emoji": "6.0.1", + "nodebb-plugin-dbsearch": "6.2.8", + "nodebb-plugin-emoji": "6.0.2", "nodebb-plugin-emoji-android": "4.1.1", "nodebb-plugin-markdown": "13.0.0", "nodebb-plugin-mentions": "4.6.10", diff --git a/src/database/mongo/hash.js b/src/database/mongo/hash.js index 5e42a4f26d..958d5384f3 100644 --- a/src/database/mongo/hash.js +++ b/src/database/mongo/hash.js @@ -188,7 +188,7 @@ module.exports = function (module) { }; module.deleteObjectField = async function (key, field) { - await module.deleteObjectFields(key, [field]); + await module.deleteObjectFields(key, Array.isArray(field) ? field : [field]); }; module.deleteObjectFields = async function (key, fields) { diff --git a/src/database/postgres/hash.js b/src/database/postgres/hash.js index ced3207822..5e3a842d22 100644 --- a/src/database/postgres/hash.js +++ b/src/database/postgres/hash.js @@ -295,7 +295,7 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b }; module.deleteObjectField = async function (key, field) { - await module.deleteObjectFields(key, [field]); + await module.deleteObjectFields(key, Array.isArray(field) ? field : [field]); }; module.deleteObjectFields = async function (key, fields) { @@ -377,12 +377,34 @@ RETURNING ("data"->>$2::TEXT)::NUMERIC v`, if (!Array.isArray(data) || !data.length) { return; } - // TODO: perf? - await Promise.all(data.map(async (item) => { - for (const [field, value] of Object.entries(item[1])) { - // eslint-disable-next-line no-await-in-loop - await module.incrObjectFieldBy(item[0], field, value); - } - })); + + await module.transaction(async (client) => { + await helpers.ensureLegacyObjectsType(client, data.map(item => item[0]), 'hash'); + + const keys = data.map(item => item[0]); + const dataStrings = data.map(item => JSON.stringify(item[1])); + + await client.query({ + name: 'incrObjectFieldByBulk', + text: ` +INSERT INTO "legacy_hash" ("_key", "data") +SELECT k, d +FROM UNNEST($1::TEXT[], $2::JSONB[]) vs(k, d) +ON CONFLICT ("_key") +DO UPDATE SET "data" = ( + SELECT jsonb_object_agg( + key, + CASE + WHEN jsonb_typeof(legacy_hash.data -> key) = 'number' + AND jsonb_typeof(EXCLUDED.data -> key) = 'number' + THEN to_jsonb((legacy_hash.data ->> key)::NUMERIC + (EXCLUDED.data ->> key)::NUMERIC) + ELSE COALESCE(EXCLUDED.data -> key, legacy_hash.data -> key) + END + ) + FROM jsonb_each(legacy_hash.data || EXCLUDED.data) AS merged(key, value) +);`, + values: [keys, dataStrings], + }); + }); }; }; diff --git a/src/database/postgres/list.js b/src/database/postgres/list.js index a950f4ce41..5e91b8c947 100644 --- a/src/database/postgres/list.js +++ b/src/database/postgres/list.js @@ -75,9 +75,26 @@ RETURNING A."array"[array_length(A."array", 1)] v`, if (!key) { return; } - // TODO: remove all values with one query + if (Array.isArray(value)) { - await Promise.all(value.map(v => module.listRemoveAll(key, v))); + await module.pool.query({ + name: 'listRemoveAllMultiple', + text: ` + UPDATE "legacy_list" l + SET "array" = ( + SELECT ARRAY( + SELECT elem + FROM unnest(l."array") WITH ORDINALITY AS u(elem, ord) + WHERE elem NOT IN (SELECT unnest($2::TEXT[])) + ORDER BY ord + ) + ) + FROM "legacy_object_live" o + WHERE o."_key" = l."_key" + AND o."type" = l."type" + AND o."_key" = $1::TEXT;`, + values: [key, value], + }); return; } await module.pool.query({ diff --git a/src/database/postgres/sorted.js b/src/database/postgres/sorted.js index 27168493a7..916425e0c1 100644 --- a/src/database/postgres/sorted.js +++ b/src/database/postgres/sorted.js @@ -547,8 +547,38 @@ RETURNING "score" s`, }; module.sortedSetIncrByBulk = async function (data) { - // TODO: perf single query? - return await Promise.all(data.map(item => module.sortedSetIncrBy(item[0], item[1], item[2]))); + if (!data.length) { + return []; + } + + return await module.transaction(async (client) => { + await helpers.ensureLegacyObjectsType(client, data.map(item => item[0]), 'zset'); + + const values = []; + const queryParams = []; + let paramIndex = 1; + + data.forEach(([key, increment, value]) => { + value = helpers.valueToString(value); + increment = parseFloat(increment); + values.push(key, value, increment); + queryParams.push(`($${paramIndex}::TEXT, $${paramIndex + 1}::TEXT, $${paramIndex + 2}::NUMERIC)`); + paramIndex += 3; + }); + + const query = ` +INSERT INTO "legacy_zset" ("_key", "value", "score") +VALUES ${queryParams.join(', ')} +ON CONFLICT ("_key", "value") +DO UPDATE SET "score" = "legacy_zset"."score" + EXCLUDED."score" +RETURNING "value", "score"`; + + const res = await client.query({ + text: query, + values, + }); + return res.rows.map(row => parseFloat(row.score)); + }); }; module.getSortedSetRangeByLex = async function (key, min, max, start, count) { From 51e660d5aeca59acd541c99331027983defa0c92 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Sun, 2 Feb 2025 03:27:54 -0500 Subject: [PATCH 14/14] fix: bad logic that invisibly broke outgoing user follows completely --- src/api/activitypub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/activitypub.js b/src/api/activitypub.js index 36bb35acb5..76abcc9c51 100644 --- a/src/api/activitypub.js +++ b/src/api/activitypub.js @@ -38,7 +38,7 @@ function enabledCheck(next) { activitypubApi.follow = enabledCheck(async (caller, { type, id, actor } = {}) => { // Privilege checks should be done upstream const assertion = await activitypub.actors.assert(actor); - if (!assertion || !assertion.length) { + if (!assertion || (Array.isArray(assertion) && assertion.length)) { throw new Error('[[error:activitypub.invalid-id]]'); }