mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-02-26 00:21:16 +01:00
* feat: wip categories pagination * feat: add subCategoriesPerPage setting * feat: add load more sub categories button to category page * fix: openapi spec * feat: show sub categories left on category page hide button when no more categories left * breaking: rename categories to allCategories on /search categories contains the search results * fix: spec * refactor: remove cidsPerPage * fix: tests * feat: use component for subcategories * fix: prevent negative subCategoriesLeft * feat: new category filter/search WIP * feat: remove categories from /tag * fix: dont load all categories when showing move modal * feat: allow adding custom categories to list * breaking: dont load entire category tree on post queue removed unused code add hooks to filter/selector add options to filter/selector * feat: make selector modal work again * feat: replace old search module * fix: topic move selector * feat: dont load all categories on create category modal * fix: fix more categorySelectors * feat: dont load entire category tree on group details page * feat: dont load all categories on home page and user settings page * feat: add pagination to /user/:userslug/categories * fix: update schemas * fix: more tests * fix: test * feat: flags page, dont return entire category tree * fix: flag test * feat: categories manage page dont load all categories allow changing root category clear caches properly * fix: spec * feat: admins&mods page dont load all categories * fix: spec * fix: dont load all children when opening dropdown * fix: on search results dont return all children * refactor: pass all options, rename options.cids to options.selectedCids * fix: #9266 * fix: index 0 * fix: spec * feat: #9265, add setObjectBulk * refactor: shoter updateOrder * feat: selectors on categories/category * fix: tests and search filter * fix: category update test * feat: pagination on acp categories page show order in set order modal * fix: allow drag&drop on pages > 1 in /admin/manage/categories * fix: teasers for deep nested categories fix sub category display on /category page * fix: spec * refactor: use eslint-disable-next-line * refactor: shorter
144 lines
4.6 KiB
JavaScript
144 lines
4.6 KiB
JavaScript
'use strict';
|
|
|
|
const winston = require('winston');
|
|
|
|
const meta = require('../meta');
|
|
const emailer = require('../emailer');
|
|
const db = require('../database');
|
|
const groups = require('../groups');
|
|
const privileges = require('../privileges');
|
|
|
|
module.exports = function (User) {
|
|
User.bans = {};
|
|
|
|
User.bans.ban = async function (uid, until, reason) {
|
|
// "until" (optional) is unix timestamp in milliseconds
|
|
// "reason" (optional) is a string
|
|
until = until || 0;
|
|
reason = reason || '';
|
|
|
|
const now = Date.now();
|
|
|
|
until = parseInt(until, 10);
|
|
if (isNaN(until)) {
|
|
throw new Error('[[error:ban-expiry-missing]]');
|
|
}
|
|
|
|
const banKey = 'uid:' + uid + ':ban:' + now;
|
|
const banData = {
|
|
uid: uid,
|
|
timestamp: now,
|
|
expire: until > now ? until : 0,
|
|
};
|
|
if (reason) {
|
|
banData.reason = reason;
|
|
}
|
|
|
|
// Leaving all other system groups to have privileges constrained to the "banned-users" group
|
|
const systemGroups = groups.systemGroups.filter(group => group !== groups.BANNED_USERS);
|
|
await groups.leave(systemGroups, uid);
|
|
await groups.join(groups.BANNED_USERS, uid);
|
|
await db.sortedSetAdd('users:banned', now, uid);
|
|
await db.sortedSetAdd('uid:' + uid + ':bans:timestamp', now, banKey);
|
|
await db.setObject(banKey, banData);
|
|
await User.setUserField(uid, 'banned:expire', banData.expire);
|
|
if (until > now) {
|
|
await db.sortedSetAdd('users:banned:expire', until, uid);
|
|
} else {
|
|
await db.sortedSetRemove('users:banned:expire', uid);
|
|
}
|
|
|
|
// Email notification of ban
|
|
const username = await User.getUserField(uid, 'username');
|
|
const siteTitle = meta.config.title || 'NodeBB';
|
|
|
|
const data = {
|
|
subject: '[[email:banned.subject, ' + siteTitle + ']]',
|
|
username: username,
|
|
until: until ? (new Date(until)).toUTCString().replace(/,/g, '\\,') : false,
|
|
reason: reason,
|
|
};
|
|
await emailer.send('banned', uid, data).catch(err => winston.error('[emailer.send] ' + err.stack));
|
|
|
|
return banData;
|
|
};
|
|
|
|
User.bans.unban = async function (uids) {
|
|
uids = Array.isArray(uids) ? uids : [uids];
|
|
const userData = await User.getUsersFields(uids, ['email:confirmed']);
|
|
|
|
await db.setObject(uids.map(uid => 'user:' + uid), { 'banned:expire': 0 });
|
|
|
|
/* eslint-disable no-await-in-loop */
|
|
for (const user of userData) {
|
|
const systemGroupsToJoin = [
|
|
'registered-users',
|
|
(parseInt(user['email:confirmed'], 10) === 1 ? 'verified-users' : 'unverified-users'),
|
|
];
|
|
await groups.leave(groups.BANNED_USERS, user.uid);
|
|
// An unbanned user would lost its previous "Global Moderator" status
|
|
await groups.join(systemGroupsToJoin, user.uid);
|
|
}
|
|
|
|
await db.sortedSetRemove(['users:banned', 'users:banned:expire'], uids);
|
|
};
|
|
|
|
User.bans.isBanned = async function (uids) {
|
|
const isArray = Array.isArray(uids);
|
|
uids = isArray ? uids : [uids];
|
|
const result = await User.bans.unbanIfExpired(uids);
|
|
return isArray ? result.map(r => r.banned) : result[0].banned;
|
|
};
|
|
|
|
User.bans.canLoginIfBanned = async function (uid) {
|
|
let canLogin = true;
|
|
|
|
const banned = (await User.bans.unbanIfExpired([uid]))[0].banned;
|
|
// Group privilege overshadows individual one
|
|
if (banned) {
|
|
canLogin = await privileges.global.canGroup('local:login', groups.BANNED_USERS);
|
|
}
|
|
if (banned && !canLogin) {
|
|
// Checking a single privilege of user
|
|
canLogin = await groups.isMember(uid, 'cid:0:privileges:local:login');
|
|
}
|
|
|
|
return canLogin;
|
|
};
|
|
|
|
User.bans.unbanIfExpired = async function (uids) {
|
|
// loading user data will unban if it has expired -barisu
|
|
const userData = await User.getUsersFields(uids, ['banned:expire']);
|
|
return User.bans.calcExpiredFromUserData(userData);
|
|
};
|
|
|
|
User.bans.calcExpiredFromUserData = async function (userData) {
|
|
const isArray = Array.isArray(userData);
|
|
userData = isArray ? userData : [userData];
|
|
const banned = await groups.isMembers(userData.map(u => u.uid), groups.BANNED_USERS);
|
|
userData = userData.map((userData, index) => ({
|
|
banned: banned[index],
|
|
'banned:expire': userData && userData['banned:expire'],
|
|
banExpired: userData && userData['banned:expire'] <= Date.now() && userData['banned:expire'] !== 0,
|
|
}));
|
|
return isArray ? userData : userData[0];
|
|
};
|
|
|
|
User.bans.filterBanned = async function (uids) {
|
|
const isBanned = await User.bans.isBanned(uids);
|
|
return uids.filter((uid, index) => !isBanned[index]);
|
|
};
|
|
|
|
User.bans.getReason = async function (uid) {
|
|
if (parseInt(uid, 10) <= 0) {
|
|
return '';
|
|
}
|
|
const keys = await db.getSortedSetRevRange('uid:' + uid + ':bans:timestamp', 0, 0);
|
|
if (!keys.length) {
|
|
return '';
|
|
}
|
|
const banObj = await db.getObject(keys[0]);
|
|
return banObj && banObj.reason ? banObj.reason : '';
|
|
};
|
|
};
|