Compare commits

..

16 Commits

Author SHA1 Message Date
Misty Release Bot
78b8fab3e7 chore: incrementing version number - v3.6.7 2024-02-28 14:17:09 +00:00
Barış Soner Uşaklı
5f597dc97f align center 2024-02-27 15:13:06 -05:00
Barış Soner Uşaklı
107f5613bf fix: #12372, fix manual digest buttons 2024-02-27 15:07:23 -05:00
Barış Soner Uşaklı
b2a988190c maintain old behaviour 2024-02-26 13:19:26 -05:00
Barış Soner Uşaklı
52796bc54e static hooks don't return sync data 2024-02-26 09:22:58 -05:00
Barış Soner Uşaklı
00e29403f0 fix: closes #12365, staticHooks with callbacks 2024-02-26 00:50:22 -05:00
Barış Soner Uşaklı
c61b3bbd25 fix: #12359, fix api call 2024-02-22 11:40:47 -05:00
Barış Soner Uşaklı
3960d370e0 fix: closes #12358, only load pending/invited for owners 2024-02-22 10:14:35 -05:00
Barış Soner Uşaklı
4e51bf81bb test: better test for #12352 2024-02-21 11:38:07 -05:00
Barış Soner Uşaklı
4a405ce032 fix: retry setAdd on e11000 error 2024-02-21 11:36:04 -05:00
Misty Release Bot
debaa2b9cd chore: update changelog for v3.6.6 2024-02-14 15:41:31 +00:00
Misty Release Bot
62e3a59c27 chore: incrementing version number - v3.6.6 2024-02-14 15:41:30 +00:00
Barış Soner Uşaklı
e01bceff97 remove body short from subject 2024-02-07 19:11:36 -05:00
Barış Soner Uşaklı
5fec8b2381 test: fix spec 2024-02-05 16:32:22 -05:00
Barış Soner Uşaklı
88e9fa379f fix: closes #12329, fix default value of categoryWatchState 2024-02-05 15:40:33 -05:00
Misty Release Bot
04039f7620 chore: update changelog for v3.6.5 2024-01-31 13:49:22 +00:00
16 changed files with 186 additions and 80 deletions

View File

@@ -1,3 +1,100 @@
#### v3.6.6 (2024-02-14)
##### Chores
* incrementing version number - v3.6.5 (6c653625)
* update changelog for v3.6.5 (04039f76)
* 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)
##### Bug Fixes
* closes #12329, fix default value of categoryWatchState (88e9fa37)
##### Tests
* fix spec (5fec8b23)
#### v3.6.5 (2024-01-31)
##### Chores
* incrementing version number - v3.6.4 (83d131b4)
* update changelog for v3.6.4 (6e6c3974)
* 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)
##### Bug Fixes
* #12320, .text() gets \n\t characters (67c8bd99)
#### v3.6.4 (2024-01-24)
##### Chores

View File

@@ -176,7 +176,7 @@
"onlineCutoff": 30,
"timeagoCutoff": 30,
"necroThreshold": 7,
"categoryWatchState": "watching",
"categoryWatchState": "tracking",
"submitPluginUsage": 1,
"showAverageApprovalTime": 1,
"autoApproveTime": 0,

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "3.6.5",
"version": "3.6.7",
"homepage": "https://www.nodebb.org",
"repository": {
"type": "git",

View File

@@ -117,7 +117,7 @@ get:
categoryWatchState:
type: object
properties:
watching:
tracking:
type: boolean
disableCustomUserSkins:
type: number

View File

@@ -5,7 +5,7 @@ define('admin/manage/digest', ['bootbox', 'alerts'], function (bootbox, alerts)
const Digest = {};
Digest.init = function () {
$('table').on('click', '[data-action]', function () {
$('.digest').on('click', '[data-action]', function () {
const action = this.getAttribute('data-action');
const uid = this.getAttribute('data-uid');

View File

@@ -92,7 +92,7 @@ define('forum/groups/memberlist', ['api', 'bootbox', 'alerts'], function (api, b
const searchEl = $('[component="groups/members/search"]');
searchEl.on('keyup', utils.debounce(function () {
const query = searchEl.val();
api.get(`/groups/${groupName}/members`, { query }, function (err, results) {
api.get(`/groups/${ajaxify.data.group.slug}/members`, { query }, function (err, results) {
if (err) {
return alerts.error(err);
}

View File

@@ -236,7 +236,6 @@ module.exports = function (Categories) {
const notification = await notifications.create({
type: 'new-topic-in-category',
nid: `new_topic:tid:${postData.topic.tid}:uid:${exceptUid}`,
subject: bodyShort,
bodyShort: bodyShort,
bodyLong: postData.content,
pid: postData.pid,

View File

@@ -42,7 +42,6 @@ groupsController.get = async function (req, res, next) {
if (!group || groupName === groups.BANNED_USERS) {
return next();
}
group.isOwner = true;
const groupNameData = groupNames.map(name => ({
encodedName: encodeURIComponent(name),

View File

@@ -72,7 +72,6 @@ groupsController.details = async function (req, res, next) {
if (!groupData) {
return next();
}
groupData.isOwner = groupData.isOwner || isAdmin || (isGlobalMod && !groupData.system);
res.render('groups/details', {
title: `[[pages:group, ${groupData.displayName}]]`,

View File

@@ -13,17 +13,25 @@ module.exports = function (module) {
}
value = value.map(v => helpers.valueToString(v));
await module.client.collection('objects').updateOne({
_key: key,
}, {
$addToSet: {
members: {
$each: value,
try {
await module.client.collection('objects').updateOne({
_key: key,
}, {
$addToSet: {
members: {
$each: value,
},
},
},
}, {
upsert: true,
});
}, {
upsert: true,
});
} catch (err) {
if (err && err.message.includes('E11000 duplicate key error')) {
console.log(new Error('e11000').stack, key, value);
return await module.setAdd(key, value);
}
throw err;
}
};
module.setsAdd = async function (keys, value) {

View File

@@ -3,6 +3,7 @@
const user = require('../user');
const db = require('../database');
const plugins = require('../plugins');
const privileges = require('../privileges');
const slugify = require('../slugify');
const Groups = module.exports;
@@ -130,30 +131,37 @@ Groups.get = async function (groupName, options) {
stop = (parseInt(options.userListCount, 10) || 4) - 1;
}
const [groupData, members, pending, invited, isMember, isPending, isInvited, isOwner] = await Promise.all([
const [groupData, members, isMember, isPending, isInvited, isOwner, isAdmin, isGlobalMod] = await Promise.all([
Groups.getGroupData(groupName),
Groups.getOwnersAndMembers(groupName, options.uid, 0, stop),
Groups.getPending(groupName),
Groups.getInvites(groupName),
Groups.isMember(options.uid, groupName),
Groups.isPending(options.uid, groupName),
Groups.isInvited(options.uid, groupName),
Groups.ownership.isOwner(options.uid, groupName),
privileges.admin.can('admin:groups', options.uid),
user.isGlobalModerator(options.uid),
]);
if (!groupData) {
return null;
}
groupData.isOwner = isOwner || isAdmin || (isGlobalMod && !groupData.system);
if (groupData.isOwner) {
([groupData.pending, groupData.invited] = await Promise.all([
Groups.getPending(groupName),
Groups.getInvites(groupName),
]));
}
const descriptionParsed = await plugins.hooks.fire('filter:parse.raw', String(groupData.description || ''));
groupData.descriptionParsed = descriptionParsed;
groupData.members = members;
groupData.membersNextStart = stop + 1;
groupData.pending = pending.filter(Boolean);
groupData.invited = invited.filter(Boolean);
groupData.isMember = isMember;
groupData.isPending = isPending;
groupData.isInvited = isInvited;
groupData.isOwner = isOwner;
const results = await plugins.hooks.fire('filter:group.get', { group: groupData });
return results.group;
};

View File

@@ -4,13 +4,10 @@ const db = require('../database');
const user = require('../user');
module.exports = function (Groups) {
Groups.getUsersFromSet = async function (set, fields) {
Groups.getUsersFromSet = async function (set, fields = []) {
const uids = await db.getSetMembers(set);
if (fields) {
return await user.getUsersFields(uids, fields);
}
return await user.getUsersData(uids);
const userData = await user.getUsersFields(uids, fields);
return userData.filter(u => u && u.uid);
};
Groups.getUserGroups = async function (uids) {

View File

@@ -207,6 +207,38 @@ Hooks.hasListeners = function (hook) {
return !!(plugins.loadedHooks[hook] && plugins.loadedHooks[hook].length > 0);
};
function hookHandlerPromise(hook, hookObj, params) {
return new Promise((resolve, reject) => {
let resolved = false;
function _resolve(result) {
if (resolved) {
winston.warn(`[plugins] ${hook} already resolved in plugin ${hookObj.id}`);
return;
}
resolved = true;
resolve(result);
}
const returned = hookObj.method(params, (err, result) => {
if (err) reject(err); else _resolve(result);
});
if (utils.isPromise(returned)) {
returned.then(
payload => _resolve(payload),
err => reject(err)
);
return;
}
if (hook.startsWith('filter:') && returned !== undefined) {
_resolve(returned);
} else if (hook.startsWith('static:') && hookObj.method.length <= 1) {
// make sure it is resolved if static hook doesn't return anything and doesn't use callback
_resolve();
}
});
}
async function fireFilterHook(hook, hookList, params) {
if (!Array.isArray(hookList) || !hookList.length) {
return params;
@@ -223,31 +255,7 @@ async function fireFilterHook(hook, hookList, params) {
if (hookObj.method.constructor && hookObj.method.constructor.name === 'AsyncFunction') {
return await hookObj.method(params);
}
return new Promise((resolve, reject) => {
let resolved = false;
function _resolve(result) {
if (resolved) {
winston.warn(`[plugins] ${hook} already resolved in plugin ${hookObj.id}`);
return;
}
resolved = true;
resolve(result);
}
const returned = hookObj.method(params, (err, result) => {
if (err) reject(err); else _resolve(result);
});
if (utils.isPromise(returned)) {
returned.then(
payload => _resolve(payload),
err => reject(err)
);
return;
}
if (returned) {
_resolve(returned);
}
});
return hookHandlerPromise(hook, hookObj, params);
}
for (const hookObj of hookList) {
@@ -303,28 +311,7 @@ async function fireStaticHook(hook, hookList, params) {
return timeout(hookObj.method(params), 10000, 'timeout');
}
return new Promise((resolve, reject) => {
let resolved = false;
function _resolve(result) {
if (resolved) {
return;
}
resolved = true;
resolve(result);
}
const returned = hookObj.method(params, (err, result) => {
if (err) reject(err); else _resolve(result);
});
if (utils.isPromise(returned)) {
returned.then(
payload => _resolve(payload),
err => reject(err)
);
return;
}
_resolve();
});
return hookHandlerPromise(hook, hookObj, params);
}
for (const hookObj of hookList) {

View File

@@ -620,7 +620,6 @@ module.exports = function (Topics) {
const notification = await notifications.create({
type: 'new-topic-with-tag',
nid: `new_topic:tid:${postData.topic.tid}:uid:${exceptUid}`,
subject: bodyShort,
bodyShort: bodyShort,
bodyLong: postData.content,
pid: postData.pid,

View File

@@ -1,4 +1,4 @@
<div class="px-lg-4">
<div class="px-lg-4 digest">
<p class="lead">[[admin/manage/digest:lead]]</p>
<p>[[admin/manage/digest:disclaimer]]</p>
<p>[[admin/manage/digest:disclaimer-continued]]</p>
@@ -8,7 +8,7 @@
<div class="mb-3">
<div class="mb-2"><em>[[admin/manage/digest:default-help, {default}]]</em></div>
<div class="d-flex gap-1">
<div class="d-flex gap-1 align-items-center">
<div>[[admin/manage/digest:manual-run]]</div>
<button class="btn btn-sm btn-outline-secondary" data-action="resend-day">[[admin/settings/user:digest-freq.daily]]</button>
<button class="btn btn-sm btn-outline-secondary" data-action="resend-week">[[admin/settings/user:digest-freq.weekly]]</button>

View File

@@ -30,6 +30,19 @@ describe('Set methods', () => {
assert.deepStrictEqual(members, []);
assert(!exists);
});
it('should not error with parallel adds', async () => {
await Promise.all([
db.setAdd('parallelset', 1),
db.setAdd('parallelset', 2),
db.setAdd('parallelset', 3),
]);
const members = await db.getSetMembers('parallelset');
assert.strictEqual(members.length, 3);
assert(members.includes('1'));
assert(members.includes('2'));
assert(members.includes('3'));
});
});
describe('getSetMembers()', () => {