Compare commits

...

75 Commits

Author SHA1 Message Date
renovate[bot]
933f06ce02 fix(deps): update dependency nodebb-theme-persona to v14.1.21 2025-12-31 16:09:22 +00:00
renovate[bot]
89abdca179 fix(deps): update dependency @isaacs/ttlcache to v2.1.4 (#13861)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-31 11:08:26 -05:00
renovate[bot]
f35c77ddee chore(deps): update dependency smtp-server to v3.18.0 (#13858)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-27 09:31:16 -05:00
renovate[bot]
37c052f4c6 chore(deps): update dependency jsdom to v27.4.0 (#13860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-27 09:31:04 -05:00
Barış Soner Uşaklı
403230ccfd refactor: silence if-function deprecation on prod 2025-12-26 23:38:44 -05:00
Barış Soner Uşaklı
5a3cf50111 Merge branch 'master' into develop 2025-12-26 23:33:01 -05:00
Barış Soner Uşaklı
648d9c78bb chore: up mentions 2025-12-26 23:32:49 -05:00
Barış Soner Uşaklı
c2e57061d9 Merge branch 'master' into develop 2025-12-26 23:21:55 -05:00
renovate[bot]
6807f86048 fix(deps): update socket.io packages to v4.8.3 (#13857)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 23:33:28 -05:00
renovate[bot]
7325b995fe fix(deps): update dependency sass to v1.97.1 (#13856)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-24 23:32:17 -05:00
Barış Soner Uşaklı
ded4315899 Merge branch 'develop' of https://github.com/NodeBB/NodeBB into develop 2025-12-24 23:27:07 -05:00
Barış Soner Uşaklı
2a5bd6ef36 Merge branch 'master' into develop 2025-12-24 23:27:06 -05:00
Misty Release Bot
2f0526b8a4 chore: update changelog for v4.7.2 2025-12-24 18:38:30 +00:00
Misty Release Bot
bab4304e04 chore: incrementing version number - v4.7.2 2025-12-24 18:38:30 +00:00
renovate[bot]
b8f68fb460 fix(deps): update dependency nodebb-theme-persona to v14.1.20 (#13855)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 21:23:21 -05:00
renovate[bot]
f98fd6dc57 fix(deps): update dependency nodebb-theme-harmony to v2.1.28 (#13854)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 21:23:11 -05:00
renovate[bot]
d28866abc8 chore(deps): update dependency sass-embedded to v1.97.1 (#13850)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 21:20:49 -05:00
renovate[bot]
160ce17f85 fix(deps): update dependency fs-extra to v11.3.3 (#13851)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 21:20:38 -05:00
renovate[bot]
f6ef041c18 fix(deps): update dependency nodemailer to v7.0.12 (#13853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-23 21:20:20 -05:00
Barış Soner Uşaklı
1f9f2dff2f fix: update data-isowner when changing is ownership
fixes multiple ownership toggles
2025-12-23 14:29:28 -05:00
renovate[bot]
abcb2382ca fix(deps): update dependency nodebb-plugin-2factor to v7.6.1 (#13852)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 16:35:03 -05:00
renovate[bot]
2a10f9046a fix(deps): update dependency validator to v13.15.26 (#13846)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 14:54:41 -05:00
renovate[bot]
b933d1a274 fix(deps): update dependency nodebb-theme-persona to v14.1.19 (#13849)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 14:54:03 -05:00
renovate[bot]
61d8cba984 fix(deps): update dependency nodebb-theme-harmony to v2.1.27 (#13848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-22 14:53:56 -05:00
Barış Soner Uşaklı
59dd1ca607 chore: up body-parser 2025-12-19 17:38:19 -05:00
Julian Lam
d03137128c fix: bump 2factor 2025-12-18 11:39:31 -05:00
Barış Soner Uşaklı
a331f8da77 refactor: clear quick reply as soon as submitting
bring back message on error
2025-12-18 10:47:55 -05:00
Barış Soner Uşaklı
b405a09bfd Revert "test: check if tests pass without await"
This reverts commit 5414cf473d.
2025-12-18 10:38:28 -05:00
Barış Soner Uşaklı
5414cf473d test: check if tests pass without await 2025-12-18 10:31:46 -05:00
renovate[bot]
bb5a90a3fe fix(deps): update dependency webpack to v5.104.1 (#13847)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-18 10:04:35 -05:00
b2cc
da79582148 * Docker: add function to entrypoint to auto-install plugins on reboot (fixes #13735) (#13749)
* * Docker: add function to entrypoint to auto-install plugins on reboot (fixes #13735)

Added a function to install additional NodeBB plugins if specified. This fixes #13735

* fix: case on

---------

Co-authored-by: Jakub Bliźniuk <opliko.reg@protonmail.com>
2025-12-17 17:00:43 -05:00
renovate[bot]
5844e393bd fix(deps): update dependency esbuild to v0.27.2 (#13842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 16:56:29 -05:00
Barış Uşaklı
550411fb58 test: change redis connection (#13844) 2025-12-17 16:56:07 -05:00
renovate[bot]
2ffa43834e fix(deps): update dependency nodebb-plugin-mentions to v4.8.4 (#13845)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 16:46:16 -05:00
Barış Soner Uşaklı
1305faa838 test: add await to check tests 2025-12-17 14:35:35 -05:00
Barış Soner Uşaklı
d505301fa0 chore: up mentions 2025-12-17 13:02:43 -05:00
Barış Soner Uşaklı
9f8d50706e test: add back logs for failing test 2025-12-17 12:46:33 -05:00
Barış Soner Uşaklı
301b538649 test: add back logs for failing test 2025-12-17 12:34:54 -05:00
Barış Soner Uşaklı
e3ecc5436d Merge branch 'master' into develop 2025-12-17 11:16:17 -05:00
renovate[bot]
f16eec3045 fix(deps): update dependency webpack to v5.104.0 (#13839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 10:23:33 -05:00
renovate[bot]
168b6e630c chore(deps): update dependency sass-embedded to v1.97.0 (#13837)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 10:23:21 -05:00
renovate[bot]
ab8dbb4158 fix(deps): update dependency sass to v1.97.0 (#13838)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-17 10:23:11 -05:00
Barış Soner Uşaklı
d60db54419 Merge branch 'master' into develop 2025-12-17 10:21:00 -05:00
Misty Release Bot
8668cfb38c chore: update changelog for v4.7.1 2025-12-17 15:18:53 +00:00
Misty Release Bot
e6deb625f2 chore: incrementing version number - v4.7.1 2025-12-17 15:18:53 +00:00
Julian Lam
b1fc5bfdaa fix: wrong increment value 2025-12-17 09:57:45 -05:00
Julian Lam
9f94a72117 fix: increment progress on upgrade script 2025-12-17 09:57:45 -05:00
Julian Lam
9f72996416 feat: stop extraneous vote and tids_read data from being saved for remote users 2025-12-17 09:57:45 -05:00
renovate[bot]
0ef5cbbbca fix(deps): update dependency fetch-cookie to v3.2.0 (#13836)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-15 16:56:30 -05:00
renovate[bot]
7c2e83303c fix(deps): update dependency autoprefixer to v10.4.23 (#13835)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-15 16:54:56 -05:00
Shlomo
5ae8d553ed fix: disallow inline viewing of unsafe files (#13833) 2025-12-15 13:16:38 -05:00
Barış Soner Uşaklı
90a151348e fix: moving topic to cid=-1 will remove it from list 2025-12-13 17:19:16 -05:00
renovate[bot]
ad895efb61 chore(deps): update dependency smtp-server to v3.17.1 (#13829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 10:36:23 -05:00
renovate[bot]
22fe83f005 chore(deps): update dependency @eslint/js to v9.39.2 (#13830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 10:36:13 -05:00
renovate[bot]
b169621860 chore(deps): update github artifact actions (#13831)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 10:36:02 -05:00
renovate[bot]
da7c9b32b8 fix(deps): update dependency terser-webpack-plugin to v5.3.16 (#13827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 07:56:00 -05:00
renovate[bot]
0fcc8543c6 chore(deps): update actions/cache action to v5 (#13828)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-12 07:55:41 -05:00
Barış Soner Uşaklı
ed977c48b4 Merge branch 'master' into develop 2025-12-11 21:25:56 -05:00
Barış Soner Uşaklı
f49f540bfa fix: show errors when saving settings 2025-12-11 21:25:42 -05:00
Julian Lam
20918b5281 fix: wrong increment value 2025-12-11 11:10:29 -05:00
Julian Lam
8abe0dfa9f fix: increment progress on upgrade script 2025-12-11 11:09:13 -05:00
Julian Lam
097d0802b7 feat: stop extraneous vote and tids_read data from being saved for remote users 2025-12-11 10:57:00 -05:00
renovate[bot]
3adcbe0f7d chore(deps): update dependency smtp-server to v3.17.0 (#13824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 10:34:57 -05:00
renovate[bot]
b992511bb9 chore(deps): update dependency sass-embedded to v1.96.0 (#13821)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 10:13:51 -05:00
renovate[bot]
d4f53a6242 fix(deps): update dependency sass to v1.96.0 (#13822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 10:13:35 -05:00
Julian Lam
528cd258c4 feat: support remote Dislike activity, federate out a Dislike on downvote, bwahahah 2025-12-10 12:40:46 -05:00
renovate[bot]
a2f2c8c761 chore(deps): update dependency sass-embedded to v1.95.1 (#13817)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 20:04:25 -05:00
renovate[bot]
81c232f181 fix(deps): update dependency winston to v3.19.0 (#13812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 19:47:59 -05:00
renovate[bot]
f077c4cab8 fix(deps): update dependency cron to v4.4.0 (#13818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 19:46:55 -05:00
renovate[bot]
adedb7b626 fix(deps): update dependency sass to v1.95.1 (#13816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 19:46:11 -05:00
renovate[bot]
a35c326a6c chore(deps): update dependency jsdom to v27.3.0 (#13814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 19:25:06 -05:00
renovate[bot]
eaa6e71a99 fix(deps): update dependency sass to v1.95.0 (#13815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 19:24:47 -05:00
Barış Soner Uşaklı
011f8b2465 Merge branch 'master' into develop 2025-12-08 10:19:00 -05:00
Barış Soner Uşaklı
b19281b061 revert: spec change 2025-12-08 10:18:38 -05:00
Barış Soner Uşaklı
9d6665505e chore: up widget-essentials 2025-12-08 09:43:50 -05:00
21 changed files with 366 additions and 78 deletions

View File

@@ -53,7 +53,7 @@ jobs:
- name: Cache node_modules
id: cache-node-modules
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: var-cache-node-modules
key: var-cache-node-modules-${{ hashFiles('Dockerfile', 'install/package.json') }}
@@ -77,7 +77,7 @@ jobs:
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
@@ -93,7 +93,7 @@ jobs:
echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY@L}" >> $GITHUB_ENV
echo "CURRENT_DATE_NST=$(date +'%Y%m%d-%H%M%S' -d '-3 hours -30 minutes')" >> $GITHUB_ENV
- name: Download digests
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
path: ${{ runner.temp }}/digests
pattern: digests-*

View File

@@ -1,3 +1,125 @@
#### v4.7.2 (2025-12-24)
##### Chores
* up body-parser (59dd1ca6)
* up mentions (d505301f)
* incrementing version number - v4.7.1 (afb88805)
* update changelog for v4.7.1 (8668cfb3)
* 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)
##### Bug Fixes
* update data-isowner when changing is ownership (1f9f2dff)
* bump 2factor (d0313712)
##### Tests
* change redis connection (#13844) (550411fb)
* add await to check tests (1305faa8)
* add back logs for failing test (9f8d5070)
#### v4.7.1 (2025-12-17)
##### Chores
* up widget-essentials (9d666550)
* remove log (2142b680)
* up harmony (59f649b8)
* incrementing version number - v4.7.0 (e82d40f8)
* update changelog for v4.7.0 (1c0a43dc)
* 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)
##### Continuous Integration
* drop ARM v7 from docker builds (#13808) (254370c5)
##### New Features
* stop extraneous vote and tids_read data from being saved for remote users (9f729964)
* add hreflang to buildLinkTag (ba85474d)
* #13790, allow ssl setup in psql (5bd1f7b7)
##### Bug Fixes
* wrong increment value (b1fc5bfd)
* increment progress on upgrade script (9f94a721)
* disallow inline viewing of unsafe files (#13833) (5ae8d553)
* moving topic to cid=-1 will remove it from list (90a15134)
* show errors when saving settings (f49f540b)
* closes #13666, update category label (193aaf55)
* respect user pagination settings in infinite scroll (#13765) (#13788) (ebf2a2c5)
* remove hardcoded name for sentinel, #13794 (53e22acf)
##### Other Changes
* fix missing comma (9fb41c69)
##### Reverts
* spec change (b19281b0)
##### Tests
* fix tests (11b01dfc)
#### v4.7.0 (2025-11-26)
##### Chores

View File

@@ -12,6 +12,7 @@ set_defaults() {
export SETUP="${SETUP:-}"
export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}"
export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}"
export NODEBB_ADDITIONAL_PLUGINS="${NODEBB_ADDITIONAL_PLUGINS:-}"
}
# Function to check if a directory exists and is writable
@@ -172,6 +173,33 @@ debug_log() {
echo "DEBUG: $message"
}
install_additional_plugins() {
if [[ ! -z ${NODEBB_ADDITIONAL_PLUGINS} ]]; then
export START_BUILD="true"
for plugin in "${NODEBB_ADDITIONAL_PLUGINS[@]}"; do
echo "Installing additional plugin ${plugin}..."
case "$PACKAGE_MANAGER" in
yarn) yarn install || {
echo "Failed to install plugin ${plugin} with yarn"
exit 1
} ;;
npm) npm install || {
echo "Failed to install plugin ${plugin} with npm"
exit 1
} ;;
pnpm) pnpm install || {
echo "Failed to install plugin ${plugin} with pnpm"
exit 1
} ;;
*)
echo "Unknown package manager: $PACKAGE_MANAGER"
exit 1
;;
esac
done
fi
}
# Main function
main() {
set_defaults
@@ -182,12 +210,14 @@ main() {
debug_log "PACKAGE_MANAGER: $PACKAGE_MANAGER"
debug_log "CONFIG location: $CONFIG"
debug_log "START_BUILD: $START_BUILD"
debug_log "NODEBB_ADDITIONAL_PLUGINS: ${NODEBB_ADDITIONAL_PLUGINS}"
if [ -n "$SETUP" ]; then
start_setup_session "$CONFIG"
fi
if [ -f "$CONFIG" ]; then
install_additional_plugins
start_forum "$CONFIG" "$START_BUILD"
else
start_installation_session "$NODEBB_INIT_VERB" "$CONFIG"

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "4.7.0",
"version": "4.7.2",
"homepage": "https://www.nodebb.org",
"repository": {
"type": "git",
@@ -33,7 +33,7 @@
"@fontsource/inter": "5.2.8",
"@fontsource/poppins": "5.2.7",
"@fortawesome/fontawesome-free": "6.7.2",
"@isaacs/ttlcache": "2.1.3",
"@isaacs/ttlcache": "2.1.4",
"@nodebb/spider-detector": "2.0.3",
"@popperjs/core": "2.11.8",
"@textcomplete/contenteditable": "0.1.13",
@@ -42,7 +42,7 @@
"ace-builds": "1.43.5",
"archiver": "7.0.1",
"async": "3.2.6",
"autoprefixer": "10.4.22",
"autoprefixer": "10.4.23",
"bcryptjs": "3.0.3",
"benchpressjs": "2.5.5",
"body-parser": "2.2.1",
@@ -61,18 +61,18 @@
"connect-pg-simple": "10.0.0",
"connect-redis": "9.0.0",
"cookie-parser": "1.4.7",
"cron": "4.3.5",
"cron": "4.4.0",
"cropperjs": "1.6.2",
"csrf-sync": "4.2.1",
"daemon": "1.1.0",
"diff": "8.0.2",
"esbuild": "0.27.1",
"esbuild": "0.27.2",
"express": "4.22.1",
"express-session": "1.18.2",
"express-useragent": "2.0.2",
"fetch-cookie": "3.1.0",
"fetch-cookie": "3.2.0",
"file-loader": "6.2.0",
"fs-extra": "11.3.2",
"fs-extra": "11.3.3",
"graceful-fs": "4.2.11",
"helmet": "7.2.0",
"html-to-text": "9.0.5",
@@ -96,23 +96,23 @@
"mousetrap": "1.6.5",
"multer": "2.0.2",
"nconf": "0.13.0",
"nodebb-plugin-2factor": "7.6.0",
"nodebb-plugin-2factor": "7.6.1",
"nodebb-plugin-composer-default": "10.3.1",
"nodebb-plugin-dbsearch": "6.3.4",
"nodebb-plugin-emoji": "6.0.5",
"nodebb-plugin-emoji-android": "4.1.1",
"nodebb-plugin-link-preview": "2.1.5",
"nodebb-plugin-markdown": "13.2.2",
"nodebb-plugin-mentions": "4.8.3",
"nodebb-plugin-mentions": "4.8.5",
"nodebb-plugin-spam-be-gone": "2.3.2",
"nodebb-plugin-web-push": "0.7.6",
"nodebb-rewards-essentials": "1.0.2",
"nodebb-theme-harmony": "2.1.26",
"nodebb-theme-harmony": "2.1.28",
"nodebb-theme-lavender": "7.1.19",
"nodebb-theme-peace": "2.2.49",
"nodebb-theme-persona": "14.1.18",
"nodebb-widget-essentials": "7.0.40",
"nodemailer": "7.0.11",
"nodebb-theme-persona": "14.1.21",
"nodebb-widget-essentials": "7.0.41",
"nodemailer": "7.0.12",
"nprogress": "0.2.0",
"passport": "0.7.0",
"passport-http-bearer": "1.0.1",
@@ -129,19 +129,19 @@
"rss": "1.2.2",
"rtlcss": "4.3.0",
"sanitize-html": "2.17.0",
"sass": "1.94.2",
"sass": "1.97.1",
"satori": "0.18.3",
"sbd": "^1.0.19",
"semver": "7.7.3",
"serve-favicon": "2.5.1",
"sharp": "0.34.5",
"sitemap": "9.0.0",
"socket.io": "4.8.1",
"socket.io-client": "4.8.1",
"socket.io": "4.8.3",
"socket.io-client": "4.8.3",
"@socket.io/redis-adapter": "8.3.0",
"sortablejs": "1.15.6",
"spdx-license-list": "6.10.0",
"terser-webpack-plugin": "5.3.15",
"terser-webpack-plugin": "5.3.16",
"textcomplete": "0.18.2",
"textcomplete.contenteditable": "0.1.1",
"timeago": "1.6.7",
@@ -149,10 +149,10 @@
"toobusy-js": "0.5.1",
"tough-cookie": "6.0.0",
"undici": "^7.10.0",
"validator": "13.15.23",
"webpack": "5.103.0",
"validator": "13.15.26",
"webpack": "5.104.1",
"webpack-merge": "6.0.1",
"winston": "3.18.3",
"winston": "3.19.0",
"workerpool": "10.0.1",
"xml": "1.0.1",
"xregexp": "5.1.2",
@@ -164,23 +164,23 @@
"@commitlint/cli": "20.2.0",
"@commitlint/config-angular": "20.2.0",
"coveralls": "3.1.1",
"@eslint/js": "9.39.1",
"@eslint/js": "9.39.2",
"@stylistic/eslint-plugin": "5.6.1",
"eslint-config-nodebb": "1.1.11",
"eslint-plugin-import": "2.32.0",
"grunt": "1.6.1",
"grunt-contrib-watch": "1.1.0",
"husky": "8.0.3",
"jsdom": "27.2.0",
"jsdom": "27.4.0",
"lint-staged": "16.2.7",
"mocha": "11.7.5",
"mocha-lcov-reporter": "1.3.0",
"mockdate": "3.0.5",
"nyc": "17.1.0",
"smtp-server": "3.16.1"
"smtp-server": "3.18.0"
},
"optionalDependencies": {
"sass-embedded": "1.93.3"
"sass-embedded": "1.97.1"
},
"resolutions": {
"*/jquery": "3.7.1"

View File

@@ -19,9 +19,7 @@ delete:
description: a valid UNIX timestamp
example: 1611850000000
responses:
"200":
'200':
description: Post diff successfully deleted
content:
application/json:
schema:
$ref: ../diffs.yaml#/get/responses/200/content
$ref: ../diffs.yaml#/get/responses/200/content

View File

@@ -102,7 +102,7 @@ define('forum/account/settings', [
if (languageChanged && parseInt(app.user.uid, 10) === parseInt(ajaxify.data.theirid, 10)) {
window.location.reload();
}
});
}).catch(alerts.error);
}
function toggleCustomRoute() {

View File

@@ -285,7 +285,7 @@ define('forum/category/tools', [
}
async function onTopicMoved(data) {
if (ajaxify.data.template.category) {
if (ajaxify.data.template.category || String(data.toCid) === '-1') {
getTopicEl(data.tid).remove();
} else {
const category = await api.get(`/categories/${data.toCid}`);

View File

@@ -81,6 +81,7 @@ define('forum/groups/details', [
case 'toggleOwnership':
api[isOwner ? 'del' : 'put'](`/groups/${ajaxify.data.group.slug}/ownership/${uid}`, {}).then(() => {
ownerFlagEl.toggleClass('invisible');
userRow.attr('data-isowner', isOwner ? '0' : '1');
}).catch(alerts.error);
break;

View File

@@ -60,7 +60,7 @@ define('quickreply', [
return;
}
const replyMsg = components.get('topic/quickreply/text').val();
const replyMsg = element.val();
const replyData = {
tid: ajaxify.data.tid,
handle: undefined,
@@ -74,9 +74,11 @@ define('quickreply', [
}
ready = false;
element.val('');
api.post(`/topics/${ajaxify.data.tid}`, replyData, function (err, data) {
ready = true;
if (err) {
element.val(replyMsg);
return alerts.error(err);
}
if (data && data.queued) {
@@ -91,7 +93,7 @@ define('quickreply', [
});
}
components.get('topic/quickreply/text').val('');
element.val('');
storage.removeItem(qrDraftId);
QuickReply._autocomplete.hide();
hooks.fire('action:quickreply.success', { data });

View File

@@ -352,6 +352,26 @@ inbox.like = async (req) => {
socketHelpers.upvote(result, 'notifications:upvoted-your-post-in');
};
inbox.dislike = async (req) => {
const { actor, object } = req.body;
const { type, id } = await activitypub.helpers.resolveLocalId(object.id);
if (type !== 'post' || !(await posts.exists(id))) {
return reject('Dislike', object, actor);
}
const allowed = await privileges.posts.can('posts:downvote', id, activitypub._constants.uid);
if (!allowed) {
activitypub.helpers.log(`[activitypub/inbox.like] ${id} not allowed to be downvoted.`);
return reject('Dislike', object, actor);
}
activitypub.helpers.log(`[activitypub/inbox/dislike] id ${id} via ${actor}`);
await posts.downvote(id, actor);
await activitypub.feps.announce(object.id, req.body);
};
inbox.announce = async (req) => {
let { actor, object, published, to, cc } = req.body;
activitypub.helpers.log(`[activitypub/inbox/announce] Parsing Announce(${object.type}) from ${actor}`);

View File

@@ -266,8 +266,8 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
await Notes.syncUserInboxes(tid, uid);
if (!hasTid && uid && options.cid) {
// New topic via search/post-redirect, have category announce it
activitypub.out.announce.topic(tid);
// New topic, have category announce it
await activitypub.out.announce.topic(tid);
}
return { tid, count };

View File

@@ -277,6 +277,32 @@ Out.like.note = enabledCheck(async (uid, pid) => {
]);
});
Out.dislike = {};
Out.dislike.note = enabledCheck(async (uid, pid) => {
const payload = {
id: `${nconf.get('url')}/uid/${uid}#activity/dislike/${encodeURIComponent(pid)}`,
type: 'Dislike',
actor: `${nconf.get('url')}/uid/${uid}`,
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
};
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
await activitypub.feps.announce(pid, payload);
return;
}
const recipient = await posts.getPostField(pid, 'uid');
if (!activitypub.helpers.isUri(recipient)) {
return;
}
await Promise.all([
activitypub.send('uid', uid, [recipient], payload),
activitypub.feps.announce(pid, payload),
]);
});
Out.announce = {};
Out.announce.topic = enabledCheck(async (tid) => {

View File

@@ -147,14 +147,34 @@ async function executeCommand(caller, command, eventName, notification, data) {
websockets.in(`uid_${caller.uid}`).emit(`posts.${command}`, result);
websockets.in(data.room_id).emit(`event:${eventName}`, result);
}
if (result && command === 'upvote') {
socketHelpers.upvote(result, notification);
await activitypub.out.like.note(caller.uid, data.pid);
} else if (result && notification) {
socketHelpers.sendNotificationToPostOwner(data.pid, caller.uid, command, notification);
} else if (result && command === 'unvote') {
socketHelpers.rescindUpvoteNotification(data.pid, caller.uid);
await activitypub.out.undo.like(caller.uid, data.pid);
if (result) {
switch (command) {
case 'upvote': {
socketHelpers.upvote(result, notification);
await activitypub.out.like.note(caller.uid, data.pid);
break;
}
case 'downvote': {
await activitypub.out.dislike.note(caller.uid, data.pid);
break;
}
case 'unvote': {
socketHelpers.rescindUpvoteNotification(data.pid, caller.uid);
await activitypub.out.undo.like(caller.uid, data.pid);
break;
}
default: {
if (notification) {
socketHelpers.sendNotificationToPostOwner(data.pid, caller.uid, command, notification);
}
break;
}
}
}
return result;
}

View File

@@ -58,13 +58,12 @@ connection.connect = async function (options) {
winston.error(err.stack);
reject(err);
});
cxn.on('ready', () => {
cxn.connect().then(() => {
// back-compat with node_redis
cxn.batch = cxn.multi;
resolve(cxn);
});
cxn.connect().then(() => {
winston.info('Connected to Redis successfully');
resolve(cxn);
}).catch((err) => {
winston.error('Error connecting to Redis:', err);
});

View File

@@ -167,7 +167,7 @@ actions.buildCSS = async function buildCSS(data) {
if (data.minify) {
opts.silenceDeprecations = [
'legacy-js-api', 'color-functions',
'global-builtin', 'import',
'global-builtin', 'import', 'if-function',
];
}
const scssOutput = await sass.compileStringAsync(data.source, opts);

View File

@@ -273,10 +273,19 @@ middleware.buildSkinAsset = helpers.try(async (req, res, next) => {
middleware.addUploadHeaders = function addUploadHeaders(req, res, next) {
// Trim uploaded files' timestamps when downloading + force download if html
let basename = path.basename(req.path);
const extname = path.extname(req.path);
if (req.path.startsWith('/uploads/files/') && middleware.regexes.timestampedUpload.test(basename)) {
basename = basename.slice(14);
res.header('Content-Disposition', `${extname.startsWith('.htm') ? 'attachment' : 'inline'}; filename="${basename}"`);
const extname = path.extname(req.path).toLowerCase();
const unsafeExtensions = [
'.html', '.htm', '.xhtml', '.mht', '.mhtml', '.stm', '.shtm', '.shtml',
'.svg', '.svgz',
'.xml', '.xsl', '.xslt',
];
const isInlineSafe = !unsafeExtensions.includes(extname);
const dispositionType = isInlineSafe ? 'inline' : 'attachment';
if (req.path.startsWith('/uploads/files/')) {
if (middleware.regexes.timestampedUpload.test(basename)) {
basename = basename.slice(14);
}
res.header('Content-Disposition', `${dispositionType}; filename="${basename}"`);
}
next();

View File

@@ -177,16 +177,18 @@ module.exports = function (Posts) {
}
const now = Date.now();
if (type === 'upvote' && !unvote) {
await db.sortedSetAdd(`uid:${uid}:upvote`, now, pid);
} else {
await db.sortedSetRemove(`uid:${uid}:upvote`, pid);
}
if (utils.isNumber(uid)) {
if (type === 'upvote' && !unvote) {
await db.sortedSetAdd(`uid:${uid}:upvote`, now, pid);
} else {
await db.sortedSetRemove(`uid:${uid}:upvote`, pid);
}
if (type === 'upvote' || unvote) {
await db.sortedSetRemove(`uid:${uid}:downvote`, pid);
} else {
await db.sortedSetAdd(`uid:${uid}:downvote`, now, pid);
if (type === 'upvote' || unvote) {
await db.sortedSetRemove(`uid:${uid}:downvote`, pid);
} else {
await db.sortedSetAdd(`uid:${uid}:downvote`, now, pid);
}
}
const postData = await Posts.getPostFields(pid, ['pid', 'uid', 'tid']);

View File

@@ -290,7 +290,7 @@ module.exports = function (Topics) {
};
Topics.markAsRead = async function (tids, uid) {
if (!Array.isArray(tids) || !tids.length) {
if (!Array.isArray(tids) || !tids.length || !utils.isNumber(uid)) {
return false;
}

View File

@@ -0,0 +1,24 @@
'use strict';
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Remove extraneous upvote and tids_read data for remote users',
timestamp: Date.UTC(2025, 11, 11),
method: async function () {
const { progress } = this;
await batch.processSortedSet('usersRemote:lastCrawled', async (uids) => {
const readKeys = uids.map(uid => `uid:${uid}:tids_read`);
const voteKeys = uids.map(uid => `uid:${uid}:upvote`);
const combined = readKeys.concat(voteKeys);
await db.deleteAll(combined);
progress.incr(uids.length);
}, {
batch: 500,
progress,
});
},
};

View File

@@ -164,9 +164,9 @@ describe('FEPs', () => {
});
pid = id;
({ activity } = await helpers.mocks.create(note));
console.log('before inbox create', activitypub._sent);
await activitypub.inbox.create({ body: activity });
console.log('after inbox create', activitypub._sent);
const activities = Array.from(activitypub._sent);
const test1 = activities.some((activity) => {
@@ -175,13 +175,13 @@ describe('FEPs', () => {
activity.object && activity.object.type === 'Create' &&
activity.object.object && activity.object.object.type === 'Note';
});
assert(test1);
const test2 = activities.some((activity) => {
[, activity] = activity;
return activity.type === 'Announce' &&
activity.object && activity.object.type === 'Note';
});
assert(test1 && test2);
assert(test2);
});
it('should federate out an Announce(Create(Note)) on reply', async () => {

View File

@@ -432,6 +432,7 @@ describe('Notes', () => {
describe('Create', () => {
let uid;
let cid;
before(async () => {
uid = await user.create({ username: utils.generateUUID() });
@@ -451,6 +452,17 @@ describe('Notes', () => {
assert.strictEqual(cid, -1);
});
it('should not append to the tids_read sorted set', async () => {
const { note, id } = helpers.mocks.note();
const { activity } = helpers.mocks.create(note);
await db.sortedSetAdd(`followersRemote:${note.attributedTo}`, Date.now(), uid);
await activitypub.inbox.create({ body: activity });
const exists = await db.exists(`uid:${note.attributedTo}:tids_read`);
assert(!exists);
});
it('should create a new topic in a remote category if addressed (category same-origin)', async () => {
const { id: remoteCid } = helpers.mocks.group();
const { note, id } = helpers.mocks.note({
@@ -467,40 +479,63 @@ describe('Notes', () => {
});
it('should create a new topic in cid -1 if a non-same origin remote category is addressed', async function () {
this.timeout(30000);
const start = Date.now();
const { id: remoteCid } = helpers.mocks.group({
id: `https://example.com/${utils.generateUUID()}`,
});
console.log('1', Date.now() - start);
const { note, id } = helpers.mocks.note({
audience: [remoteCid],
});
console.log('2', Date.now() - start);
const { activity } = helpers.mocks.create(note);
console.log('3', Date.now() - start);
try {
await activitypub.inbox.create({ body: activity });
} catch (err) {
console.log('error in test', err.stack);
assert(false);
}
console.log('4', Date.now() - start);
assert(await posts.exists(id));
console.log('5', Date.now() - start);
const cid = await posts.getCidByPid(id);
console.log('6', Date.now() - start);
assert.strictEqual(cid, -1);
});
});
describe('(Like)', () => {
let pid;
let voterUid;
before(async () => {
({ cid } = await categories.create({ name: utils.generateUUID() }));
const { postData } = await topics.post({
uid,
cid,
title: utils.generateUUID(),
content: utils.generateUUID(),
});
pid = postData.pid;
const object = await activitypub.mocks.notes.public(postData);
const { activity } = helpers.mocks.like({ object });
voterUid = activity.actor;
await activitypub.inbox.like({ body: activity });
});
it('should increment a like for the post', async () => {
const voted = await posts.hasVoted(pid, voterUid);
const count = await posts.getPostField(pid, 'upvotes');
assert(voted);
assert.strictEqual(count, 1);
});
it('should not append to the uid upvotes zset', async () => {
const exists = await db.exists(`uid:${voterUid}:upvote`);
assert(!exists);
});
});
});
describe('Announce', () => {
let cid;
before(async () => {
({ cid } = await categories.create({ name: utils.generateUUID().slice(0, 8) }));
({ cid } = await categories.create({ name: utils.generateUUID() }));
});
describe('(Create)', () => {