You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
7.4 KiB
JavaScript

'use strict';
const user = require('../user');
const db = require('../database');
const plugins = require('../plugins');
const slugify = require('../slugify');
9 years ago
const Groups = module.exports;
8 years ago
6 years ago
require('./data')(Groups);
require('./create')(Groups);
require('./delete')(Groups);
require('./update')(Groups);
require('./invite')(Groups);
6 years ago
require('./membership')(Groups);
require('./ownership')(Groups);
require('./search')(Groups);
require('./cover')(Groups);
require('./posts')(Groups);
require('./user')(Groups);
require('./join')(Groups);
require('./leave')(Groups);
require('./cache')(Groups);
8 years ago
Groups.BANNED_USERS = 'banned-users';
8 years ago
7 years ago
Groups.ephemeralGroups = ['guests', 'spiders'];
8 years ago
Groups.systemGroups = [
'registered-users',
'verified-users',
'unverified-users',
Groups.BANNED_USERS,
'administrators',
'Global Moderators',
];
8 years ago
Groups.getEphemeralGroup = function (groupName) {
return {
name: groupName,
slug: slugify(groupName),
8 years ago
description: '',
hidden: 0,
system: 1,
8 years ago
};
};
8 years ago
Groups.removeEphemeralGroups = function (groups) {
for (let x = groups.length; x >= 0; x -= 1) {
if (Groups.ephemeralGroups.includes(groups[x])) {
8 years ago
groups.splice(x, 1);
}
}
return groups;
};
const isPrivilegeGroupRegex = /^cid:\d+:privileges:[\w\-:]+$/;
8 years ago
Groups.isPrivilegeGroup = function (groupName) {
return isPrivilegeGroupRegex.test(groupName);
};
Groups.getGroupsFromSet = async function (set, start, stop) {
let groupNames;
if (set === 'groups:visible:name') {
groupNames = await db.getSortedSetRangeByLex(set, '-', '+', start, stop - start + 1);
} else {
groupNames = await db.getSortedSetRevRange(set, start, stop);
}
if (set === 'groups:visible:name') {
groupNames = groupNames.map(name => name.split(':')[1]);
}
return await Groups.getGroupsAndMembers(groupNames);
8 years ago
};
Groups.getGroupsBySort = async function (sort, start, stop) {
let set = 'groups:visible:name';
if (sort === 'count') {
set = 'groups:visible:memberCount';
} else if (sort === 'date') {
set = 'groups:visible:createtime';
}
return await Groups.getGroupsFromSet(set, start, stop);
};
Groups.getNonPrivilegeGroups = async function (set, start, stop) {
let groupNames = await db.getSortedSetRevRange(set, start, stop);
groupNames = groupNames.concat(Groups.ephemeralGroups).filter(groupName => !Groups.isPrivilegeGroup(groupName));
const groupsData = await Groups.getGroupsData(groupNames);
return groupsData.filter(Boolean);
6 years ago
};
Groups.getGroups = async function (set, start, stop) {
return await db.getSortedSetRevRange(set, start, stop);
8 years ago
};
Groups.getGroupsAndMembers = async function (groupNames) {
const [groups, members] = await Promise.all([
Groups.getGroupsData(groupNames),
Groups.getMemberUsers(groupNames, 0, 3),
]);
groups.forEach((group, index) => {
if (group) {
group.members = members[index] || [];
group.truncated = group.memberCount > group.members.length;
}
});
return groups;
8 years ago
};
Groups.get = async function (groupName, options) {
8 years ago
if (!groupName) {
throw new Error('[[error:invalid-group]]');
8 years ago
}
let stop = -1;
8 years ago
if (options.truncateUserList) {
stop = (parseInt(options.userListCount, 10) || 4) - 1;
}
10 years ago
Categories refactor (#9257) * 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
4 years ago
const [groupData, members, pending, invited, isMember, isPending, isInvited, isOwner] = await Promise.all([
Groups.getGroupData(groupName),
Groups.getOwnersAndMembers(groupName, options.uid, 0, stop),
Groups.getUsersFromSet(`group:${groupName}:pending`, ['username', 'userslug', 'picture']),
Groups.getUsersFromSet(`group:${groupName}:invited`, ['username', 'userslug', 'picture']),
Groups.isMember(options.uid, groupName),
Groups.isPending(options.uid, groupName),
Groups.isInvited(options.uid, groupName),
Groups.ownership.isOwner(options.uid, groupName),
]);
if (!groupData) {
return null;
}
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;
};
Groups.getOwners = async function (groupName) {
return await db.getSetMembers(`group:${groupName}:owners`);
};
Groups.getOwnersAndMembers = async function (groupName, uid, start, stop) {
const ownerUids = await db.getSetMembers(`group:${groupName}:owners`);
const countToReturn = stop - start + 1;
const ownerUidsOnPage = ownerUids.slice(start, stop !== -1 ? stop + 1 : undefined);
const owners = await user.getUsers(ownerUidsOnPage, uid);
owners.forEach((user) => {
if (user) {
user.isOwner = true;
}
});
10 years ago
let done = false;
let returnUsers = owners;
5 years ago
let memberStart = start - ownerUids.length;
let memberStop = memberStart + countToReturn - 1;
memberStart = Math.max(0, memberStart);
memberStop = Math.max(0, memberStop);
async function addMembers(start, stop) {
let batch = await user.getUsersFromSet(`group:${groupName}:members`, uid, start, stop);
if (!batch.length) {
done = true;
}
batch = batch.filter(user => user && user.uid && !ownerUids.includes(user.uid.toString()));
returnUsers = returnUsers.concat(batch);
}
if (stop === -1) {
await addMembers(memberStart, -1);
} else {
while (returnUsers.length < countToReturn && !done) {
/* eslint-disable no-await-in-loop */
await addMembers(memberStart, memberStop);
5 years ago
memberStart = memberStop + 1;
memberStop = memberStart + countToReturn - 1;
}
}
returnUsers = countToReturn > 0 ? returnUsers.slice(0, countToReturn) : returnUsers;
const result = await plugins.hooks.fire('filter:group.getOwnersAndMembers', {
users: returnUsers,
uid: uid,
start: start,
stop: stop,
});
return result.users;
8 years ago
};
Groups.getByGroupslug = async function (slug, options) {
options = options || {};
const groupName = await db.getObjectField('groupslug:groupname', slug);
if (!groupName) {
throw new Error('[[error:no-group]]');
}
return await Groups.get(groupName, options);
8 years ago
};
Groups.getGroupNameByGroupSlug = async function (slug) {
return await db.getObjectField('groupslug:groupname', slug);
8 years ago
};
Groups.isPrivate = async function (groupName) {
return await isFieldOn(groupName, 'private');
8 years ago
};
Groups.isHidden = async function (groupName) {
return await isFieldOn(groupName, 'hidden');
};
async function isFieldOn(groupName, field) {
const value = await db.getObjectField(`group:${groupName}`, field);
return parseInt(value, 10) === 1;
}
Groups.exists = async function (name) {
8 years ago
if (Array.isArray(name)) {
const slugs = name.map(groupName => slugify(groupName));
const isMembersOfRealGroups = await db.isSortedSetMembers('groups:createtime', name);
const isMembersOfEphemeralGroups = slugs.map(slug => Groups.ephemeralGroups.includes(slug));
return name.map((n, index) => isMembersOfRealGroups[index] || isMembersOfEphemeralGroups[index]);
8 years ago
}
const slug = slugify(name);
const isMemberOfRealGroups = await db.isSortedSetMember('groups:createtime', name);
const isMemberOfEphemeralGroups = Groups.ephemeralGroups.includes(slug);
return isMemberOfRealGroups || isMemberOfEphemeralGroups;
8 years ago
};
Groups.existsBySlug = async function (slug) {
8 years ago
if (Array.isArray(slug)) {
return await db.isObjectFields('groupslug:groupname', slug);
8 years ago
}
return await db.isObjectField('groupslug:groupname', slug);
8 years ago
};
require('../promisify')(Groups);