Merge branch 'master' into develop

isekai-main
Julian Lam 2 years ago
commit a382e6e2e5

@ -1,3 +1,119 @@
#### v2.8.5 (2023-01-27)
##### Chores
* incrementing version number - v2.8.4 (a46b2bbc)
* update changelog for v2.8.4 (c13f0e21)
* incrementing version number - v2.8.3 (c20b20a7)
* incrementing version number - v2.8.2 (050e43f8)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
##### Bug Fixes
* import resolution within plugin modules (#11219) (f6c96948)
#### v2.8.4 (2023-01-26)
##### Chores
* incrementing version number - v2.8.3 (c20b20a7)
* update changelog for v2.8.3 (eb2841ee)
* incrementing version number - v2.8.2 (050e43f8)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
#### v2.8.3 (2023-01-25)
##### Chores
* remove extraneous lines from changelog (48c9f447)
* incrementing version number - v2.8.2 (050e43f8)
* update changelog for v2.8.2 (66aa3169)
* incrementing version number - v2.8.1 (727f879e)
* incrementing version number - v2.8.0 (8e77673d)
* incrementing version number - v2.7.0 (96cc0617)
* incrementing version number - v2.6.1 (7e52a7a5)
* incrementing version number - v2.6.0 (e7fcf482)
* incrementing version number - v2.5.8 (dec0e7de)
* incrementing version number - v2.5.7 (5836bf4a)
* incrementing version number - v2.5.6 (c7bd7dbf)
* incrementing version number - v2.5.5 (3509ed94)
* incrementing version number - v2.5.4 (e83260ca)
* incrementing version number - v2.5.3 (7e922936)
* incrementing version number - v2.5.2 (babcd17e)
* incrementing version number - v2.5.1 (ce3aa950)
* incrementing version number - v2.5.0 (01d276cb)
* incrementing version number - v2.4.5 (dd3e1a28)
* incrementing version number - v2.4.4 (d5525c87)
* incrementing version number - v2.4.3 (9c647c6c)
* incrementing version number - v2.4.2 (3aa7b855)
* incrementing version number - v2.4.1 (60cbd148)
* incrementing version number - v2.4.0 (4834cde3)
* incrementing version number - v2.3.1 (d2425942)
* incrementing version number - v2.3.0 (046ea120)
##### Bug Fixes
* import resolution within plugin modules (#11200) (89e059a0)
* #11195, allow users with admin:users privilege to delete users in acp (0bffd3d9)
* #11194, allow access to sub dashboard pages (7d04e952)
* #11136, tests, and returning the proper number of arrays (459bc523)
* #11136, only show mods of active categories when getModeratorUids is called (39e009c0)
* closes #11173, clear require cache if wrong dependency is installed (747cb1f0)
* **deps:**
* downgrade swagger-parser to v9 (00e48803)
* pinning sub dependency json-schema-ref-parser to 9.0.9 (9c250b78)
##### Reverts
* a788bd1344825ad4759e39d6e98d8bf3695bd639 (fecd84d1)
* 9c250b78b05ca2abf31a79971ed0c60ca07664ec, fix: comment out broken test for now (a788bd13)
##### Tests
* fix broken test (f295174e)
#### v2.8.2 (2023-01-13) #### v2.8.2 (2023-01-13)
##### Chores ##### Chores

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

@ -70,6 +70,7 @@
"no-user": "User does not exist", "no-user": "User does not exist",
"no-teaser": "Teaser does not exist", "no-teaser": "Teaser does not exist",
"no-flag": "Flag does not exist", "no-flag": "Flag does not exist",
"no-chat-room": "Chat room does not exist",
"no-privileges": "You do not have enough privileges for this action.", "no-privileges": "You do not have enough privileges for this action.",
"category-disabled": "Category disabled", "category-disabled": "Category disabled",
@ -182,6 +183,9 @@
"chat-deleted-already": "This chat message has already been deleted.", "chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.", "chat-restored-already": "This chat message has already been restored.",
"chat-room-does-not-exist": "Chat room does not exist.", "chat-room-does-not-exist": "Chat room does not exist.",
"cant-add-users-to-chat-room": "Can't add users to chat room.",
"cant-remove-users-from-chat-room": "Can't remove users from chat room.",
"chat-room-name-too-long": "Chat room name too long.",
"already-voting-for-this-post": "You have already voted for this post.", "already-voting-for-this-post": "You have already voted for this post.",
"reputation-system-disabled": "Reputation system is disabled.", "reputation-system-disabled": "Reputation system is disabled.",

@ -5,7 +5,6 @@ const _ = require('lodash');
const db = require('../database'); const db = require('../database');
const user = require('../user'); const user = require('../user');
const groups = require('../groups');
const plugins = require('../plugins'); const plugins = require('../plugins');
const privileges = require('../privileges'); const privileges = require('../privileges');
const cache = require('../cache'); const cache = require('../cache');
@ -99,39 +98,7 @@ Categories.getModerators = async function (cid) {
}; };
Categories.getModeratorUids = async function (cids) { Categories.getModeratorUids = async function (cids) {
// Only check active categories return await privileges.categories.getUidsWithPrivilege(cids, 'moderate');
const disabled = (await Categories.getCategoriesFields(cids, ['disabled'])).map(obj => obj.disabled);
// cids = cids.filter((_, idx) => !disabled[idx]);
const groupNames = cids.reduce((memo, cid) => {
memo.push(`cid:${cid}:privileges:moderate`);
memo.push(`cid:${cid}:privileges:groups:moderate`);
return memo;
}, []);
const memberSets = await groups.getMembersOfGroups(groupNames);
// Every other set is actually a list of user groups, not uids, so convert those to members
const sets = memberSets.reduce((memo, set, idx) => {
if (idx % 2) {
memo.groupNames.push(set);
} else {
memo.uids.push(set);
}
return memo;
}, { groupNames: [], uids: [] });
const uniqGroups = _.uniq(_.flatten(sets.groupNames));
const groupUids = await groups.getMembersOfGroups(uniqGroups);
const map = _.zipObject(uniqGroups, groupUids);
const moderatorUids = cids.map((cid, index) => {
if (disabled[index]) {
return [];
}
return _.uniq(sets.uids[index].concat(_.flatten(sets.groupNames[index].map(g => map[g]))));
});
return moderatorUids;
}; };
Categories.getCategories = async function (cids, uid) { Categories.getCategories = async function (cids, uid) {

@ -45,10 +45,11 @@ notificationsController.get = async function (req, res, next) {
{ separator: true }, { separator: true },
]).concat(filters.moderatorFilters); ]).concat(filters.moderatorFilters);
} }
const selectedFilter = allFilters.find((filterData) => {
allFilters.forEach((filterData) => {
filterData.selected = filterData.filter === filter; filterData.selected = filterData.filter === filter;
return filterData.selected;
}); });
const selectedFilter = allFilters.find(filterData => filterData.selected);
if (!selectedFilter) { if (!selectedFilter) {
return next(); return next();
} }

@ -340,7 +340,7 @@ authenticationController.doLogin = async function (req, uid) {
return; return;
} }
const loginAsync = util.promisify(req.login).bind(req); const loginAsync = util.promisify(req.login).bind(req);
await loginAsync({ uid: uid }, { keepSessionInfo: req.res.locals !== false }); await loginAsync({ uid: uid }, { keepSessionInfo: req.res.locals.reroll !== false });
await authenticationController.onSuccessfulLogin(req, uid); await authenticationController.onSuccessfulLogin(req, uid);
}; };

@ -1,6 +1,9 @@
'use strict'; 'use strict';
const _ = require('lodash');
const user = require('../user'); const user = require('../user');
const groups = require('../groups');
const posts = require('../posts'); const posts = require('../posts');
const flags = require('../flags'); const flags = require('../flags');
const analytics = require('../analytics'); const analytics = require('../analytics');
@ -110,7 +113,6 @@ modsController.flags.detail = async function (req, res, next) {
isAdminOrGlobalMod: user.isAdminOrGlobalMod(req.uid), isAdminOrGlobalMod: user.isAdminOrGlobalMod(req.uid),
moderatedCids: user.getModeratedCids(req.uid), moderatedCids: user.getModeratedCids(req.uid),
flagData: flags.get(req.params.flagId), flagData: flags.get(req.params.flagId),
assignees: user.getAdminsandGlobalModsandModerators(),
privileges: Promise.all(['global', 'admin'].map(async type => privileges[type].get(req.uid))), privileges: Promise.all(['global', 'admin'].map(async type => privileges[type].get(req.uid))),
}); });
results.privileges = { ...results.privileges[0], ...results.privileges[1] }; results.privileges = { ...results.privileges[0], ...results.privileges[1] };
@ -119,6 +121,28 @@ modsController.flags.detail = async function (req, res, next) {
return next(); // 404 return next(); // 404
} }
async function getAssignees(flagData) {
let uids = [];
const [admins, globalMods] = await Promise.all([
groups.getMembers('administrators', 0, -1),
groups.getMembers('Global Moderators', 0, -1),
]);
if (flagData.type === 'user') {
uids = await privileges.admin.getUidsWithPrivilege('admin:users');
uids = _.uniq(admins.concat(uids));
} else if (flagData.type === 'post') {
const cid = await posts.getCidByPid(flagData.targetId);
if (!cid) {
return [];
}
uids = (await privileges.categories.getUidsWithPrivilege([cid], 'moderate'))[0];
uids = _.uniq(admins.concat(globalMods).concat(uids));
}
const userData = await user.getUsersData(uids);
return userData.filter(u => u && u.userslug);
}
const assignees = await getAssignees(results.flagData);
results.flagData.history = results.isAdminOrGlobalMod ? (await flags.getHistory(req.params.flagId)) : null; results.flagData.history = results.isAdminOrGlobalMod ? (await flags.getHistory(req.params.flagId)) : null;
if (results.flagData.type === 'user') { if (results.flagData.type === 'user') {
@ -128,7 +152,7 @@ modsController.flags.detail = async function (req, res, next) {
} }
res.render('flags/detail', Object.assign(results.flagData, { res.render('flags/detail', Object.assign(results.flagData, {
assignees: results.assignees, assignees: assignees,
type_bool: ['post', 'user', 'empty'].reduce((memo, cur) => { type_bool: ['post', 'user', 'empty'].reduce((memo, cur) => {
if (cur !== 'empty') { if (cur !== 'empty') {
memo[cur] = results.flagData.type === cur && ( memo[cur] = results.flagData.type === cur && (

@ -40,14 +40,24 @@ async function linkModules() {
await Promise.all(Object.keys(modules).map(async (relPath) => { await Promise.all(Object.keys(modules).map(async (relPath) => {
const srcPath = path.join(__dirname, '../../', modules[relPath]); const srcPath = path.join(__dirname, '../../', modules[relPath]);
const destPath = path.join(__dirname, '../../build/public/src/modules', relPath); const destPath = path.join(__dirname, '../../build/public/src/modules', relPath);
const destDir = path.dirname(destPath);
const [stats] = await Promise.all([ const [stats] = await Promise.all([
fs.promises.stat(srcPath), fs.promises.stat(srcPath),
mkdirp(path.dirname(destPath)), mkdirp(destDir),
]); ]);
if (stats.isDirectory()) { if (stats.isDirectory()) {
await file.linkDirs(srcPath, destPath, true); await file.linkDirs(srcPath, destPath, true);
} else { } else {
await fs.promises.copyFile(srcPath, destPath); // Get the relative path to the destination directory
const relPath = path.relative(destDir, srcPath)
// and convert to a posix path
.split(path.sep).join(path.posix.sep);
// Instead of copying file, create a new file re-exporting it
// This way, imports in modules are resolved correctly
await fs.promises.writeFile(destPath, `module.exports = require('${relPath}');`);
} }
})); }));
} }

@ -37,7 +37,7 @@ module.exports = function (middleware) {
const loginAsync = util.promisify(req.login).bind(req); const loginAsync = util.promisify(req.login).bind(req);
await loginAsync(user, { keepSessionInfo: true }); await loginAsync(user, { keepSessionInfo: true });
await controllers.authentication.onSuccessfulLogin(req, user.uid); await controllers.authentication.onSuccessfulLogin(req, user.uid);
req.uid = user.uid; req.uid = parseInt(user.uid, 10);
req.loggedIn = req.uid > 0; req.loggedIn = req.uid > 0;
return true; return true;
} }

@ -211,3 +211,8 @@ privsAdmin.groupPrivileges = async function (groupName) {
const groupPrivilegeList = await privsAdmin.getGroupPrivilegeList(); const groupPrivilegeList = await privsAdmin.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList); return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList);
}; };
privsAdmin.getUidsWithPrivilege = async function (privilege) {
const uidsByCid = await helpers.getUidsWithPrivilege([0], privilege);
return uidsByCid[0];
};

@ -218,3 +218,7 @@ privsCategories.groupPrivileges = async function (cid, groupName) {
const groupPrivilegeList = await privsCategories.getGroupPrivilegeList(); const groupPrivilegeList = await privsCategories.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(cid, groupName, groupPrivilegeList); return await helpers.userOrGroupPrivileges(cid, groupName, groupPrivilegeList);
}; };
privsCategories.getUidsWithPrivilege = async function (cids, privilege) {
return await helpers.getUidsWithPrivilege(cids, privilege);
};

@ -134,3 +134,8 @@ privsGlobal.groupPrivileges = async function (groupName) {
const groupPrivilegeList = await privsGlobal.getGroupPrivilegeList(); const groupPrivilegeList = await privsGlobal.getGroupPrivilegeList();
return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList); return await helpers.userOrGroupPrivileges(0, groupName, groupPrivilegeList);
}; };
privsGlobal.getUidsWithPrivilege = async function (privilege) {
const uidsByCid = await helpers.getUidsWithPrivilege([0], privilege);
return uidsByCid[0];
};

@ -6,6 +6,7 @@ const validator = require('validator');
const groups = require('../groups'); const groups = require('../groups');
const user = require('../user'); const user = require('../user');
const categories = require('../categories');
const plugins = require('../plugins'); const plugins = require('../plugins');
const translator = require('../translator'); const translator = require('../translator');
@ -189,4 +190,38 @@ helpers.userOrGroupPrivileges = async function (cid, uidOrGroup, privilegeList)
return _.zipObject(privilegeList, isMembers); return _.zipObject(privilegeList, isMembers);
}; };
helpers.getUidsWithPrivilege = async (cids, privilege) => {
const disabled = (await categories.getCategoriesFields(cids, ['disabled'])).map(obj => obj.disabled);
const groupNames = cids.reduce((memo, cid) => {
memo.push(`cid:${cid}:privileges:${privilege}`);
memo.push(`cid:${cid}:privileges:groups:${privilege}`);
return memo;
}, []);
const memberSets = await groups.getMembersOfGroups(groupNames);
// Every other set is actually a list of user groups, not uids, so convert those to members
const sets = memberSets.reduce((memo, set, idx) => {
if (idx % 2) {
memo.groupNames.push(set);
} else {
memo.uids.push(set);
}
return memo;
}, { groupNames: [], uids: [] });
const uniqGroups = _.uniq(_.flatten(sets.groupNames));
const groupUids = await groups.getMembersOfGroups(uniqGroups);
const map = _.zipObject(uniqGroups, groupUids);
const uidsByCid = cids.map((cid, index) => {
if (disabled[index]) {
return [];
}
return _.uniq(sets.uids[index].concat(_.flatten(sets.groupNames[index].map(g => map[g]))));
});
return uidsByCid;
};
require('../promisify')(helpers); require('../promisify')(helpers);

@ -87,14 +87,15 @@ function unpin(tid, topicData) {
} }
async function sendNotifications(uids, topicsData) { async function sendNotifications(uids, topicsData) {
const usernames = await Promise.all(uids.map(uid => user.getUserField(uid, 'username'))); const userData = await user.getUsersData(uids);
const uidToUsername = Object.fromEntries(uids.map((uid, idx) => [uid, usernames[idx]])); const uidToUserData = Object.fromEntries(uids.map((uid, idx) => [uid, userData[idx]]));
const postsData = await posts.getPostsData(topicsData.map(({ mainPid }) => mainPid)); const postsData = await posts.getPostsData(topicsData.map(t => t && t.mainPid));
postsData.forEach((postData, idx) => { postsData.forEach((postData, idx) => {
postData.user = {}; if (postData) {
postData.user.username = uidToUsername[postData.uid]; postData.user = uidToUserData[topicsData[idx].uid];
postData.topic = topicsData[idx]; postData.topic = topicsData[idx];
}
}); });
return Promise.all(topicsData.map( return Promise.all(topicsData.map(

@ -17,7 +17,7 @@
{{{ end }}} {{{ end }}}
{{{ each topics }}} {{{ each topics }}}
<tr> <tr>
<td><a href="{config.relative_path}/topics/{../slug}">{../title}</a></td> <td><a href="{config.relative_path}/topic/{../slug}">{../title}</a></td>
<td>[[topic:posted_by, {../user.username}]]</td> <td>[[topic:posted_by, {../user.username}]]</td>
<td><span class="timeago" data-title="{../timestampISO}"></span></td> <td><span class="timeago" data-title="{../timestampISO}"></span></td>
</tr> </tr>

Loading…
Cancel
Save