mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-24 17:30:39 +01:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
672dcc5d14 | ||
|
|
948bfe46f1 | ||
|
|
ce5ef1ab6e | ||
|
|
61a63851d4 | ||
|
|
0a574d7240 | ||
|
|
8f9f377121 | ||
|
|
a460a55064 | ||
|
|
3674fa5783 | ||
|
|
45a11d45fc | ||
|
|
6c3e2a8e22 | ||
|
|
3faae559a8 | ||
|
|
3d96afb2d1 | ||
|
|
09cc91d5a0 |
110
CHANGELOG.md
110
CHANGELOG.md
@@ -1,3 +1,113 @@
|
||||
#### v4.4.0 (2025-05-14)
|
||||
|
||||
##### Breaking Changes
|
||||
|
||||
* removal of deprecated privilege hooks (8ea377a4)
|
||||
* removal of `filter:flags.getFilters` (547fb482)
|
||||
* removal of `filter:user.verify.code` (7e25946c)
|
||||
* removal of `filter:post.purge` (df5c1a93)
|
||||
* removal of `filter:post.purge` (c84b72fb)
|
||||
* removal of `filter:router.page` (9d8061ea)
|
||||
* removal of `filter:email.send` (b73a8d3e)
|
||||
|
||||
##### Chores
|
||||
|
||||
* **deps:**
|
||||
* update redis docker tag to v8.0.1 (#13415) (fbe97b4e)
|
||||
* update redis docker tag to v8 (#13387) (1df7313c)
|
||||
* update postgres docker tag to v17.5 (#13398) (d319b0aa)
|
||||
* update dependency sass-embedded to v1.88.0 (#13402) (694c79bc)
|
||||
* update dependency lint-staged to v16 (#13404) (9d877481)
|
||||
* update commitlint monorepo to v19.8.1 (#13394) (7a7a4f0a)
|
||||
* update dependency lint-staged to v15.5.2 (#13383) (96dc5c89)
|
||||
* update dependency @eslint/js to v9.26.0 (#13371) (450ce3b8)
|
||||
* update dependency mocha to v11.2.2 (#13366) (e958010f)
|
||||
* incrementing version number - v4.3.2 (b92b5d80)
|
||||
* update changelog for v4.3.2 (0aa9c187)
|
||||
* incrementing version number - v4.3.1 (308e6b9f)
|
||||
* remove unused require (15b6a2c1)
|
||||
* 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
|
||||
|
||||
* remove since-removed `labels` property from api (860ac895)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* adjust Peertube-specific handling to shove mp4 into post attachments, #13324 (799b08db)
|
||||
* #13081, don't add mention when you are replying to yourself (d5865613)
|
||||
* add `announces` to postdataobject schema (0f576a42)
|
||||
* #13375, plus additional tests (fe13c755)
|
||||
* missing awaits, more comprehensive 1b12 tests (5802c7dd)
|
||||
* another case (6bfe4e62)
|
||||
* handle missing orderedItems property in followers route (e042201f)
|
||||
* missing await (651ebaaf)
|
||||
* handle missing orderedItems (53bb0bbc)
|
||||
* extra `orderedItems` property in generated paginated OrderedCollection, #13153 (f83b1fbf)
|
||||
* #13153, follower and following collections to use generateCollection helper (a2de7aae)
|
||||
* #13374, updates to posts.edit to handle remote content updates better (b4338489)
|
||||
* leftover `handle` var (625ce96f)
|
||||
* AP inbox update handling for non-note objects (f8d012c8)
|
||||
* 1b12 creates being dropped (9f80d10d)
|
||||
* update AP api (un)follow ids to be url encoded id instead of handle (7cf61ab0)
|
||||
* **deps:**
|
||||
* update dependency diff to v8 (#13409) (919d62ab)
|
||||
* update dependency sanitize-html to v2.17.0 (#13418) (3e18af1e)
|
||||
* update dependency satori to v0.13.1 (#13408) (f176d6b2)
|
||||
* update dependency pg-cursor to v2.15.0 (#13414) (7320a858)
|
||||
* update dependency nodebb-plugin-markdown to v13.2.1 (#13416) (84b8ecc7)
|
||||
* update dependency semver to v7.7.2 (#13410) (366651d6)
|
||||
* update dependency pg to v8.16.0 (#13411) (0825c569)
|
||||
* update dependency nodebb-plugin-mentions to v4.7.6 (#13417) (383a7ce5)
|
||||
* update dependency lru-cache to v11 (#12685) (23374fd7)
|
||||
* update dependency rimraf to v6 (#12686) (6a4ffe02)
|
||||
* update dependency bootswatch to v5.3.6 (#13400) (7a7cf830)
|
||||
* update dependency csrf-sync to v4.2.1 (#13401) (ecce9998)
|
||||
* update dependency sass to v1.88.0 (#13403) (7ffba218)
|
||||
* update dependency nodemailer to v7.0.3 (#13395) (af3afba0)
|
||||
* update dependency nodemailer to v7 (#13381) (0b4d403c)
|
||||
* update dependency csrf-sync to v4.2.0 (#13364) (4f0f67a4)
|
||||
* update dependency webpack to v5.99.8 (#13390) (c7a164ae)
|
||||
* update dependency bootstrap to v5.3.6 (#13384) (e6a19612)
|
||||
* update dependency esbuild to v0.25.4 (#13385) (b6f4de5b)
|
||||
* update dependency @fontsource/poppins to v5.2.6 (#13376) (e2a8cf98)
|
||||
* update dependency nodebb-plugin-mentions to v4.7.5 (#13386) (2c0aba02)
|
||||
* update dependency nodebb-widget-essentials to v7.0.38 (#13380) (7f757615)
|
||||
* update dependency nodebb-theme-persona to v14.1.11 (#13379) (954aa541)
|
||||
* update dependency nodebb-theme-peace to v2.2.42 (#13378) (2aa0bfc5)
|
||||
* update dependency nodebb-theme-harmony to v2.1.12 (#13377) (72b3a215)
|
||||
* update dependency ace-builds to v1.41.0 (#13372) (4b78710b)
|
||||
* bump markdown (f3bd8590)
|
||||
|
||||
##### Other Changes
|
||||
|
||||
* //github.com/NodeBB/NodeBB/issues/13367 (39953ee1)
|
||||
|
||||
##### Refactors
|
||||
|
||||
* use a single until (1b0b1da6)
|
||||
* Helpers.generateCollection so that total count and a bound function can be passed in, #13153 (7f59238d)
|
||||
|
||||
##### Tests
|
||||
|
||||
* a few additional tests for announce handling (61f6806b)
|
||||
* fix regression from 5802c7ddd9506a4e296f6dbdf2d9a32621c7f4ef (5b118904)
|
||||
* fix broken test due to adjusted note assertion relation logic (9dc91f11)
|
||||
* update filter:router.page tests to response:router.page (a819d39c)
|
||||
* adjustment for now-removed labels property (52df41b9)
|
||||
|
||||
#### v4.3.2 (2025-05-12)
|
||||
|
||||
##### Chores
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "4.4.0",
|
||||
"version": "4.4.1",
|
||||
"homepage": "https://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -107,10 +107,10 @@
|
||||
"nodebb-plugin-spam-be-gone": "2.3.2",
|
||||
"nodebb-plugin-web-push": "0.7.4",
|
||||
"nodebb-rewards-essentials": "1.0.2",
|
||||
"nodebb-theme-harmony": "2.1.12",
|
||||
"nodebb-theme-harmony": "2.1.13",
|
||||
"nodebb-theme-lavender": "7.1.19",
|
||||
"nodebb-theme-peace": "2.2.42",
|
||||
"nodebb-theme-persona": "14.1.11",
|
||||
"nodebb-theme-peace": "2.2.43",
|
||||
"nodebb-theme-persona": "14.1.12",
|
||||
"nodebb-widget-essentials": "7.0.38",
|
||||
"nodemailer": "7.0.3",
|
||||
"nprogress": "0.2.0",
|
||||
|
||||
@@ -2,4 +2,15 @@ PostsObject:
|
||||
description: One of the objects in the array returned from `Posts.getPostSummaryByPids`
|
||||
type: array
|
||||
items:
|
||||
$ref: ./PostObject.yaml#/PostObject
|
||||
allOf:
|
||||
- $ref: ./PostObject.yaml#/PostObject
|
||||
- type: object
|
||||
description: Optional properties that may or may not be present (except for `pid`, which is always present, and is only here as a hack to pass validation)
|
||||
properties:
|
||||
pid:
|
||||
type: number
|
||||
description: A post identifier
|
||||
attachments:
|
||||
type: array
|
||||
required:
|
||||
- pid
|
||||
@@ -42,7 +42,7 @@ const sanitizeConfig = {
|
||||
|
||||
Mocks._normalize = async (object) => {
|
||||
// Normalized incoming AP objects into expected types for easier mocking
|
||||
let { type, attributedTo, url, image, content, source, attachment } = object;
|
||||
let { type, attributedTo, url, image, mediaType, content, source, attachment, cc } = object;
|
||||
|
||||
switch (true) { // non-string attributedTo handling
|
||||
case Array.isArray(attributedTo): {
|
||||
@@ -52,6 +52,10 @@ Mocks._normalize = async (object) => {
|
||||
} else if (typeof cur === 'object') {
|
||||
if (cur.type === 'Person' && cur.id) {
|
||||
valid.push(cur.id);
|
||||
} else if (cur.type === 'Group' && cur.id) {
|
||||
// Add any groups found to cc where it is expected
|
||||
cc = Array.isArray(cc) ? cc : [cc];
|
||||
cc.push(cur.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +74,9 @@ Mocks._normalize = async (object) => {
|
||||
if (sourceContent) {
|
||||
content = null;
|
||||
sourceContent = await activitypub.helpers.remoteAnchorToLocalProfile(sourceContent, true);
|
||||
} else if (mediaType === 'text/markdown') {
|
||||
sourceContent = await activitypub.helpers.remoteAnchorToLocalProfile(content, true);
|
||||
content = null;
|
||||
} else if (content && content.length) {
|
||||
content = sanitize(content, sanitizeConfig);
|
||||
content = await activitypub.helpers.remoteAnchorToLocalProfile(content);
|
||||
@@ -107,9 +114,9 @@ Mocks._normalize = async (object) => {
|
||||
const stream = url.reduce((memo, { type, mediaType, tag }) => {
|
||||
if (!memo) {
|
||||
if (type === 'Link' && mediaType === 'application/x-mpegURL') {
|
||||
memo = tag.reduce((memo, { type, mediaType, href }) => {
|
||||
memo = tag.reduce((memo, { type, mediaType, href, width, height }) => {
|
||||
if (!memo && (type === 'Link' && mediaType === 'video/mp4')) {
|
||||
memo = { type, mediaType, href };
|
||||
memo = { mediaType, href, width, height };
|
||||
}
|
||||
|
||||
return memo;
|
||||
@@ -145,6 +152,7 @@ Mocks._normalize = async (object) => {
|
||||
|
||||
return {
|
||||
...object,
|
||||
cc,
|
||||
attributedTo,
|
||||
content,
|
||||
sourceContent,
|
||||
|
||||
@@ -115,18 +115,33 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
|
||||
if (hasTid) {
|
||||
mainPid = await topics.getTopicField(tid, 'mainPid');
|
||||
} else {
|
||||
// Check recipients/audience for local category
|
||||
// Check recipients/audience for category (local or remote)
|
||||
const set = activitypub.helpers.makeSet(_activitypub, ['to', 'cc', 'audience']);
|
||||
await activitypub.actors.assert(Array.from(set));
|
||||
|
||||
// Local
|
||||
const resolved = await Promise.all(Array.from(set).map(async id => await activitypub.helpers.resolveLocalId(id)));
|
||||
const recipientCids = resolved
|
||||
.filter(Boolean)
|
||||
.filter(({ type }) => type === 'category')
|
||||
.map(obj => obj.id);
|
||||
|
||||
if (recipientCids.length) {
|
||||
// Remote
|
||||
let remoteCid;
|
||||
const assertedGroups = await categories.exists(Array.from(set));
|
||||
try {
|
||||
const { hostname } = new URL(mainPid);
|
||||
remoteCid = Array.from(set).filter((id, idx) => {
|
||||
const { hostname: cidHostname } = new URL(id);
|
||||
return assertedGroups[idx] && cidHostname === hostname;
|
||||
}).shift();
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
|
||||
if (remoteCid || recipientCids.length) {
|
||||
// Overrides passed-in value, respect addressing from main post over booster
|
||||
options.cid = recipientCids.shift();
|
||||
options.cid = remoteCid || recipientCids.shift();
|
||||
}
|
||||
|
||||
// mainPid ok to leave as-is
|
||||
|
||||
@@ -146,7 +146,9 @@ async function searchInContent(data) {
|
||||
metadata.pids = metadata.pids.slice(start, start + itemsPerPage);
|
||||
}
|
||||
|
||||
returnData.posts = await posts.getPostSummaryByPids(metadata.pids, data.uid, {});
|
||||
returnData.posts = await posts.getPostSummaryByPids(metadata.pids, data.uid, {
|
||||
extraFields: ['attachments'],
|
||||
});
|
||||
await plugins.hooks.fire('filter:search.contentGetResult', { result: returnData, data: data });
|
||||
delete metadata.pids;
|
||||
delete metadata.data;
|
||||
|
||||
@@ -88,13 +88,20 @@ Digest.send = async function (data) {
|
||||
return emailsSent;
|
||||
}
|
||||
let errorLogged = false;
|
||||
const date = new Date();
|
||||
await batch.processArray(data.subscribers, async (uids) => {
|
||||
let userData = await user.getUsersFields(uids, ['uid', 'email', 'email:confirmed', 'username', 'userslug', 'lastonline']);
|
||||
userData = userData.filter(u => u && u.email && (meta.config.includeUnverifiedEmails || u['email:confirmed']));
|
||||
let userData = await user.getUsersFields(uids, [
|
||||
'uid', 'email', 'email:confirmed', 'username', 'userslug', 'lastonline',
|
||||
]);
|
||||
userData = userData.filter(
|
||||
u => u && u.email && (meta.config.includeUnverifiedEmails || u['email:confirmed'])
|
||||
);
|
||||
if (!userData.length) {
|
||||
return;
|
||||
}
|
||||
await Promise.all(userData.map(async (userObj) => {
|
||||
const userSettings = await user.getMultipleUserSettings(userData.map(u => u.uid));
|
||||
await Promise.all(userData.map(async (userObj, index) => {
|
||||
const userSetting = userSettings[index];
|
||||
const [publicRooms, notifications, topics] = await Promise.all([
|
||||
getUnreadPublicRooms(userObj.uid),
|
||||
user.notifications.getUnreadInterval(userObj.uid, data.interval),
|
||||
@@ -118,9 +125,8 @@ Digest.send = async function (data) {
|
||||
});
|
||||
|
||||
emailsSent += 1;
|
||||
const now = new Date();
|
||||
await emailer.send('digest', userObj.uid, {
|
||||
subject: `[[email:digest.subject, ${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}]]`,
|
||||
subject: `[[email:digest.subject, ${date.toLocaleDateString(userSetting.userLang)}]]`,
|
||||
username: userObj.username,
|
||||
userslug: userObj.userslug,
|
||||
notifications: unreadNotifs,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="group-area">
|
||||
{{{ each users.groups }}}
|
||||
<div class="group-card float-start m-1" data-group-name="{users.groups.nameEscaped}">
|
||||
<a href="{config.relative_path}/admin/manage/groups/{users.groups.nameEncoded}"><span class="badge p-2" style="color:{users.groups.textColor}; background-color: {users.groups.labelColor};">{{{ if users.groups.icon }}}<i class="fa {users.groups.icon}"></i> {{{ end }}}{users.groups.displayName} <i class="ms-2 remove-group-icon fa fa-times" role="button"></i></span></a>
|
||||
<a href="{config.relative_path}/admin/manage/groups/{users.groups.slug}"><span class="badge p-2" style="color:{users.groups.textColor}; background-color: {users.groups.labelColor};">{{{ if users.groups.icon }}}<i class="fa {users.groups.icon}"></i> {{{ end }}}{users.groups.displayName} <i class="ms-2 remove-group-icon fa fa-times" role="button"></i></span></a>
|
||||
</div>
|
||||
{{{ end }}}
|
||||
</div>
|
||||
|
||||
@@ -451,7 +451,7 @@ describe('Notes', () => {
|
||||
assert.strictEqual(cid, -1);
|
||||
});
|
||||
|
||||
it('should create a new topic in cid -1 even if a remote category is addressed', async () => {
|
||||
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({
|
||||
audience: [remoteCid],
|
||||
@@ -462,6 +462,23 @@ describe('Notes', () => {
|
||||
|
||||
assert(await posts.exists(id));
|
||||
|
||||
const cid = await posts.getCidByPid(id);
|
||||
assert.strictEqual(cid, remoteCid);
|
||||
});
|
||||
|
||||
it('should create a new topic in cid -1 if a non-same origin remote category is addressed', async () => {
|
||||
const { id: remoteCid } = helpers.mocks.group({
|
||||
id: `https://example.com/${utils.generateUUID()}`,
|
||||
});
|
||||
const { note, id } = helpers.mocks.note({
|
||||
audience: [remoteCid],
|
||||
});
|
||||
const { activity } = helpers.mocks.create(note);
|
||||
|
||||
await activitypub.inbox.create({ body: activity });
|
||||
|
||||
assert(await posts.exists(id));
|
||||
|
||||
const cid = await posts.getCidByPid(id);
|
||||
assert.strictEqual(cid, -1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user