mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-11 01:52:55 +01:00
Merge commit 'bf4e257c83988f5231721473acdb3c919d651d1c' into v3.x
This commit is contained in:
58
CHANGELOG.md
58
CHANGELOG.md
@@ -1,3 +1,61 @@
|
||||
#### v3.7.1 (2024-03-14)
|
||||
|
||||
##### Chores
|
||||
|
||||
* up harmony (1b24b337)
|
||||
* up harmony (b6c3a9d2)
|
||||
* up harmony (f107a7ff)
|
||||
* incrementing version number - v3.7.0 (9a6153d7)
|
||||
* update changelog for v3.7.0 (f1f81b17)
|
||||
* incrementing version number - v3.6.7 (86a17e38)
|
||||
* incrementing version number - v3.6.6 (6604bf37)
|
||||
* incrementing version number - v3.6.5 (6c653625)
|
||||
* incrementing version number - v3.6.4 (83d131b4)
|
||||
* incrementing version number - v3.6.3 (fc7d2bfd)
|
||||
* incrementing version number - v3.6.2 (0f577a57)
|
||||
* incrementing version number - v3.6.1 (f1a69468)
|
||||
* incrementing version number - v3.6.0 (4cdf85f8)
|
||||
* incrementing version number - v3.5.3 (ed0e8783)
|
||||
* incrementing version number - v3.5.2 (52fbb2da)
|
||||
* incrementing version number - v3.5.1 (4c543488)
|
||||
* incrementing version number - v3.5.0 (d06fb4f0)
|
||||
* incrementing version number - v3.4.3 (5c984250)
|
||||
* incrementing version number - v3.4.2 (3f0dac38)
|
||||
* incrementing version number - v3.4.1 (01e69574)
|
||||
* incrementing version number - v3.4.0 (fd9247c5)
|
||||
* incrementing version number - v3.3.9 (5805e770)
|
||||
* incrementing version number - v3.3.8 (a5603565)
|
||||
* incrementing version number - v3.3.7 (b26f1744)
|
||||
* incrementing version number - v3.3.6 (7fb38792)
|
||||
* incrementing version number - v3.3.4 (a67f84ea)
|
||||
* incrementing version number - v3.3.3 (f94d239b)
|
||||
* incrementing version number - v3.3.2 (ec9dac97)
|
||||
* incrementing version number - v3.3.1 (151cc68f)
|
||||
* incrementing version number - v3.3.0 (fc1ad70f)
|
||||
* incrementing version number - v3.2.3 (b06d3e63)
|
||||
* incrementing version number - v3.2.2 (758ecfcd)
|
||||
* incrementing version number - v3.2.1 (20145074)
|
||||
* incrementing version number - v3.2.0 (9ecac38e)
|
||||
* incrementing version number - v3.1.7 (0b4e81ab)
|
||||
* incrementing version number - v3.1.6 (b3a3b130)
|
||||
* incrementing version number - v3.1.5 (ec19343a)
|
||||
* incrementing version number - v3.1.4 (2452783c)
|
||||
* incrementing version number - v3.1.3 (3b4e9d3f)
|
||||
* incrementing version number - v3.1.2 (40fa3489)
|
||||
* incrementing version number - v3.1.1 (40250733)
|
||||
* incrementing version number - v3.1.0 (0cb386bd)
|
||||
* incrementing version number - v3.0.1 (26f6ea49)
|
||||
* incrementing version number - v3.0.0 (224e08cd)
|
||||
|
||||
##### New Features
|
||||
|
||||
* allow setting max-old-space-size in config (d1ce594c)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* #12408, fix mem leak due to mongodb 6.4.0 (285293c4)
|
||||
* tids:create on topic post (3b48695b)
|
||||
|
||||
#### v3.7.0 (2024-03-06)
|
||||
|
||||
##### Chores
|
||||
|
||||
@@ -103,8 +103,8 @@
|
||||
"nodebb-plugin-ntfy": "1.7.3",
|
||||
"nodebb-plugin-spam-be-gone": "2.2.1",
|
||||
"nodebb-rewards-essentials": "1.0.0",
|
||||
"nodebb-theme-harmony": "1.2.40",
|
||||
"nodebb-theme-lavender": "7.1.7",
|
||||
"nodebb-theme-harmony": "1.2.44",
|
||||
"nodebb-theme-lavender": "7.1.8",
|
||||
"nodebb-theme-peace": "2.2.4",
|
||||
"nodebb-theme-persona": "13.3.11",
|
||||
"nodebb-widget-essentials": "7.0.15",
|
||||
|
||||
@@ -28,8 +28,10 @@ module.exports = function (Groups) {
|
||||
|
||||
Groups.ownership.rescind = async function (toUid, groupName) {
|
||||
// If the owners set only contains one member (and toUid is that member), error out!
|
||||
const numOwners = await db.setCount(`group:${groupName}:owners`);
|
||||
const isOwner = await db.isSortedSetMember(`group:${groupName}:owners`);
|
||||
const [numOwners, isOwner] = await Promise.all([
|
||||
db.setCount(`group:${groupName}:owners`),
|
||||
db.isSetMember(`group:${groupName}:owners`, toUid),
|
||||
]);
|
||||
if (numOwners <= 1 && isOwner) {
|
||||
throw new Error('[[error:group-needs-owner]]');
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ let sanitizeConfig = {
|
||||
a: ['href', 'name', 'hreflang', 'media', 'rel', 'target', 'type'],
|
||||
img: ['alt', 'height', 'ismap', 'src', 'usemap', 'width', 'srcset'],
|
||||
iframe: ['height', 'name', 'src', 'width'],
|
||||
video: ['autoplay', 'controls', 'height', 'loop', 'muted', 'poster', 'preload', 'src', 'width'],
|
||||
video: ['autoplay', 'playsinline', 'controls', 'height', 'loop', 'muted', 'poster', 'preload', 'src', 'width'],
|
||||
audio: ['autoplay', 'controls', 'loop', 'muted', 'preload', 'src'],
|
||||
source: ['type', 'src', 'srcset', 'sizes', 'media', 'height', 'width'],
|
||||
embed: ['height', 'src', 'type', 'width'],
|
||||
|
||||
@@ -11,6 +11,7 @@ const topics = require('../topics');
|
||||
const notifications = require('../notifications');
|
||||
const utils = require('../utils');
|
||||
const events = require('../events');
|
||||
const translator = require('../translator');
|
||||
|
||||
const api = require('../api');
|
||||
const sockets = require('.');
|
||||
@@ -159,10 +160,13 @@ async function canEditQueue(socket, data, action) {
|
||||
}
|
||||
|
||||
async function sendQueueNotification(type, targetUid, path, notificationText) {
|
||||
const bodyShort = notificationText ?
|
||||
translator.compile(`notifications:${type}`, notificationText) :
|
||||
translator.compile(`notifications:${type}`);
|
||||
const notifData = {
|
||||
type: type,
|
||||
nid: `${type}-${targetUid}-${path}`,
|
||||
bodyShort: notificationText ? `[[notifications:${type}, ${notificationText}]]` : `[[notifications:${type}]]`,
|
||||
bodyShort: bodyShort,
|
||||
path: path,
|
||||
};
|
||||
if (parseInt(meta.config.postQueueNotificationUid, 10) > 0) {
|
||||
|
||||
@@ -159,18 +159,24 @@ module.exports = function (Topics) {
|
||||
return tids;
|
||||
}
|
||||
|
||||
const topicData = await Topics.getTopicsFields(tids, [
|
||||
'tid', 'timestamp', 'lastposttime', 'upvotes', 'downvotes', 'postcount', 'pinned',
|
||||
]);
|
||||
const sortMap = {
|
||||
recent: sortRecent,
|
||||
old: sortOld,
|
||||
create: sortCreate,
|
||||
posts: sortPopular,
|
||||
votes: sortVotes,
|
||||
views: sortViews,
|
||||
};
|
||||
const sortFn = sortMap[params.sort] || sortRecent;
|
||||
const { sortMap, fields } = await plugins.hooks.fire('filter:topics.sortOptions', {
|
||||
params,
|
||||
fields: [
|
||||
'tid', 'timestamp', 'lastposttime', 'upvotes', 'downvotes', 'postcount', 'pinned',
|
||||
],
|
||||
sortMap: {
|
||||
recent: sortRecent,
|
||||
old: sortOld,
|
||||
create: sortCreate,
|
||||
posts: sortPopular,
|
||||
votes: sortVotes,
|
||||
views: sortViews,
|
||||
},
|
||||
});
|
||||
|
||||
const topicData = await Topics.getTopicsFields(tids, fields);
|
||||
const sortFn = sortMap.hasOwnProperty(params.sort) && sortMap[params.sort] ?
|
||||
sortMap[params.sort] : sortRecent;
|
||||
|
||||
if (params.floatPinned) {
|
||||
floatPinned(topicData, sortFn);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<div>
|
||||
<label for="agree-terms">[[register:terms-of-use]]</label>
|
||||
<div class="tos">{termsOfUse}</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="agree-terms" id="agree-terms"> [[register:agree-to-terms-of-use]]
|
||||
</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="agree-terms" id="agree-terms">
|
||||
<label class="form-check-label">[[register:agree-to-terms-of-use]]</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
<p class="lead">[[user:consent.lead]]</p>
|
||||
<p>[[user:consent.intro]]</p>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="gdpr_agree_data" id="gdpr_agree_data">
|
||||
<input class="form-check-input" type="checkbox" name="gdpr_agree_data" id="gdpr_agree_data" role="switch">
|
||||
<label class="form-check-label" for="gdpr_agree_data">[[register:gdpr-agree-data]]</label>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</p>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="gdpr_agree_email" id="gdpr_agree_email">
|
||||
<input class="form-check-input" type="checkbox" name="gdpr_agree_email" id="gdpr_agree_email" role="switch">
|
||||
<label class="form-check-label" for="gdpr_agree_email">[[register:gdpr-agree-email]]</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -914,6 +914,19 @@ describe('Groups', () => {
|
||||
assert(!isInvited);
|
||||
});
|
||||
|
||||
it('should fail to rescind last owner', async () => {
|
||||
const uid = await User.create({ username: 'lastgroupowner' });
|
||||
await Groups.create({
|
||||
name: 'last owner',
|
||||
description: 'Foobar!',
|
||||
ownerUid: uid,
|
||||
});
|
||||
await assert.rejects(
|
||||
apiGroups.rescind({ uid: adminUid }, { slug: 'last-owner', uid: uid }),
|
||||
{ message: '[[error:group-needs-owner]]' },
|
||||
);
|
||||
});
|
||||
|
||||
it('should error if user is not invited', async () => {
|
||||
await assert.rejects(
|
||||
apiGroups.acceptInvite({ uid: adminUid }, { slug: 'privatecanjoin', uid: adminUid }),
|
||||
|
||||
Reference in New Issue
Block a user