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 diff --git a/install/package.json b/install/package.json index 318378b639..786f1fbbbd 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", @@ -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.10.0", "nprogress": "0.2.0", @@ -200,4 +200,4 @@ "url": "https://github.com/barisusakli" } ] -} +} \ No newline at end of file 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/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: 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: 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], diff --git a/src/categories/topics.js b/src/categories/topics.js index b4841200fd..918cc4c244 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -245,7 +245,7 @@ module.exports = function (Categories) { bodyShort: bodyShort, bodyLong: postData.content, pid: postData.pid, - path: `/post/${postData.pid}`, + path: `/post/${encodeURIComponent(postData.pid)}`, tid: postData.topic.tid, from: exceptUid, }); 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/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, 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, }; diff --git a/src/user/categories.js b/src/user/categories.js index 137cf595e3..38770138a9 100644 --- a/src/user/categories.js +++ b/src/user/categories.js @@ -4,19 +4,25 @@ const _ = require('lodash'); const db = require('../database'); const categories = require('../categories'); -const activitypub = require('../activitypub'); const plugins = require('../plugins'); +const utils = require('../utils'); module.exports = function (User) { User.setCategoryWatchState = async function (uid, cids, state) { - if (!activitypub.helpers.isUri(uid) && !(parseInt(uid, 10) > 0)) { + if (utils.isNumber(uid) && parseInt(uid, 10) <= 0) { return; } + const isStateValid = Object.values(categories.watchStates).includes(parseInt(state, 10)); if (!isStateValid) { throw new Error('[[error:invalid-watch-state]]'); } - cids = Array.isArray(cids) ? cids : [cids]; + + cids = new Set(Array.isArray(cids) ? cids : [cids]); + cids.delete(-1); // cannot watch cid -1 + cids.delete('-1'); + cids = Array.from(cids); + const exists = await categories.exists(cids); if (exists.includes(false)) { throw new Error('[[error:no-category]]'); 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 )); 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]]