2024-07-08 17:34:16 +02:00
|
|
|
import assert from "minimalistic-assert";
|
2024-10-09 06:45:26 +02:00
|
|
|
import {z} from "zod";
|
2024-06-21 20:32:29 +02:00
|
|
|
|
2021-03-16 23:38:59 +01:00
|
|
|
import * as blueslip from "./blueslip";
|
2021-02-28 00:42:57 +01:00
|
|
|
import {FoldDict} from "./fold_dict";
|
2023-04-10 12:56:36 +02:00
|
|
|
import * as group_permission_settings from "./group_permission_settings";
|
2024-07-08 19:00:08 +02:00
|
|
|
import {$t} from "./i18n";
|
2024-07-10 05:59:00 +02:00
|
|
|
import {page_params} from "./page_params";
|
2023-01-09 12:04:10 +01:00
|
|
|
import * as settings_config from "./settings_config";
|
2024-10-09 06:45:26 +02:00
|
|
|
import type {GroupPermissionSetting, GroupSettingValue, StateData} from "./state_data";
|
|
|
|
import {current_user, raw_user_group_schema, realm} from "./state_data";
|
2024-01-03 05:40:03 +01:00
|
|
|
import type {UserOrMention} from "./typeahead_helper";
|
2024-01-11 21:16:54 +01:00
|
|
|
import type {UserGroupUpdateEvent} from "./types";
|
2019-02-08 11:56:33 +01:00
|
|
|
|
2024-10-09 06:45:26 +02:00
|
|
|
type UserGroupRaw = z.infer<typeof raw_user_group_schema>;
|
2021-08-03 18:26:39 +02:00
|
|
|
|
2024-10-09 06:45:26 +02:00
|
|
|
export const user_group_schema = raw_user_group_schema.extend({
|
2024-10-23 19:57:28 +02:00
|
|
|
// These are delivered via the API as lists, but converted to sets
|
|
|
|
// during initialization for more convenient manipulation.
|
2024-10-09 06:45:26 +02:00
|
|
|
members: z.set(z.number()),
|
|
|
|
direct_subgroup_ids: z.set(z.number()),
|
|
|
|
});
|
|
|
|
export type UserGroup = z.infer<typeof user_group_schema>;
|
2021-08-03 18:26:39 +02:00
|
|
|
|
2024-10-03 07:13:43 +02:00
|
|
|
export type UserGroupForDropdownListWidget = {
|
2023-01-09 12:04:10 +01:00
|
|
|
name: string;
|
2023-07-19 16:18:20 +02:00
|
|
|
unique_id: number;
|
2023-01-09 12:04:10 +01:00
|
|
|
};
|
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
let user_group_name_dict: FoldDict<UserGroup>;
|
|
|
|
let user_group_by_id_dict: Map<number, UserGroup>;
|
2017-11-07 08:58:46 +01:00
|
|
|
|
|
|
|
// We have an init() function so that our automated tests
|
|
|
|
// can easily clear data.
|
2021-08-03 18:26:39 +02:00
|
|
|
export function init(): void {
|
2019-12-26 15:34:17 +01:00
|
|
|
user_group_name_dict = new FoldDict();
|
2021-08-03 18:26:39 +02:00
|
|
|
user_group_by_id_dict = new Map<number, UserGroup>();
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
|
|
|
// WE INITIALIZE DATA STRUCTURES HERE!
|
2021-02-28 00:42:57 +01:00
|
|
|
init();
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2024-09-09 11:41:46 +02:00
|
|
|
export function add(user_group_raw: UserGroupRaw): UserGroup {
|
2020-01-16 20:12:06 +01:00
|
|
|
// Reformat the user group members structure to be a set.
|
2021-08-03 18:26:39 +02:00
|
|
|
const user_group = {
|
|
|
|
description: user_group_raw.description,
|
|
|
|
id: user_group_raw.id,
|
|
|
|
name: user_group_raw.name,
|
2024-06-04 12:36:52 +02:00
|
|
|
creator_id: user_group_raw.creator_id,
|
|
|
|
date_created: user_group_raw.date_created,
|
2021-08-03 18:26:39 +02:00
|
|
|
members: new Set(user_group_raw.members),
|
2021-09-16 20:54:12 +02:00
|
|
|
is_system_group: user_group_raw.is_system_group,
|
2022-05-16 17:02:44 +02:00
|
|
|
direct_subgroup_ids: new Set(user_group_raw.direct_subgroup_ids),
|
2024-10-08 16:37:34 +02:00
|
|
|
can_add_members_group: user_group_raw.can_add_members_group,
|
2024-10-02 13:13:47 +02:00
|
|
|
can_join_group: user_group_raw.can_join_group,
|
2024-10-14 13:29:10 +02:00
|
|
|
can_leave_group: user_group_raw.can_leave_group,
|
2023-08-04 15:45:37 +02:00
|
|
|
can_manage_group: user_group_raw.can_manage_group,
|
2023-07-14 06:50:33 +02:00
|
|
|
can_mention_group: user_group_raw.can_mention_group,
|
2024-09-12 13:51:13 +02:00
|
|
|
deactivated: user_group_raw.deactivated,
|
2021-08-03 18:26:39 +02:00
|
|
|
};
|
|
|
|
|
2017-11-07 08:58:46 +01:00
|
|
|
user_group_name_dict.set(user_group.name, user_group);
|
|
|
|
user_group_by_id_dict.set(user_group.id, user_group);
|
2024-09-09 11:41:46 +02:00
|
|
|
return user_group;
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function remove(user_group: UserGroup): void {
|
2020-02-03 07:41:38 +01:00
|
|
|
user_group_name_dict.delete(user_group.name);
|
|
|
|
user_group_by_id_dict.delete(user_group.id);
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-12-06 20:00:23 +01:00
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function get_user_group_from_id(group_id: number): UserGroup {
|
2021-08-03 18:21:15 +02:00
|
|
|
const user_group = user_group_by_id_dict.get(group_id);
|
|
|
|
if (!user_group) {
|
2021-09-22 23:34:58 +02:00
|
|
|
throw new Error(`Unknown group_id in get_user_group_from_id: ${group_id}`);
|
2017-11-07 08:58:46 +01:00
|
|
|
}
|
2021-08-03 18:21:15 +02:00
|
|
|
return user_group;
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2024-02-09 14:34:09 +01:00
|
|
|
export function maybe_get_user_group_from_id(group_id: number): UserGroup | undefined {
|
|
|
|
return user_group_by_id_dict.get(group_id);
|
|
|
|
}
|
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function update(event: UserGroupUpdateEvent): void {
|
2021-02-28 00:42:57 +01:00
|
|
|
const group = get_user_group_from_id(event.group_id);
|
2018-03-14 17:52:38 +01:00
|
|
|
if (event.data.name !== undefined) {
|
2020-02-03 07:41:38 +01:00
|
|
|
user_group_name_dict.delete(group.name);
|
2023-12-10 08:43:23 +01:00
|
|
|
group.name = event.data.name;
|
2018-03-15 15:48:48 +01:00
|
|
|
user_group_name_dict.set(group.name, group);
|
2018-03-14 17:52:38 +01:00
|
|
|
}
|
|
|
|
if (event.data.description !== undefined) {
|
|
|
|
group.description = event.data.description;
|
2020-02-03 07:41:38 +01:00
|
|
|
user_group_name_dict.delete(group.name);
|
2018-03-15 15:48:48 +01:00
|
|
|
user_group_name_dict.set(group.name, group);
|
2018-03-14 17:52:38 +01:00
|
|
|
}
|
2023-11-27 11:36:07 +01:00
|
|
|
|
2024-09-12 15:24:30 +02:00
|
|
|
if (event.data.deactivated !== undefined) {
|
|
|
|
group.deactivated = event.data.deactivated;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
|
|
|
|
2024-10-08 16:37:34 +02:00
|
|
|
if (event.data.can_add_members_group !== undefined) {
|
|
|
|
group.can_add_members_group = event.data.can_add_members_group;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
|
|
|
|
2023-11-27 11:36:07 +01:00
|
|
|
if (event.data.can_mention_group !== undefined) {
|
|
|
|
group.can_mention_group = event.data.can_mention_group;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
2023-08-04 15:45:37 +02:00
|
|
|
|
|
|
|
if (event.data.can_manage_group !== undefined) {
|
|
|
|
group.can_manage_group = event.data.can_manage_group;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
2024-10-02 13:13:47 +02:00
|
|
|
|
|
|
|
if (event.data.can_join_group !== undefined) {
|
|
|
|
group.can_join_group = event.data.can_join_group;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
2024-10-14 13:29:10 +02:00
|
|
|
|
|
|
|
if (event.data.can_leave_group !== undefined) {
|
|
|
|
group.can_leave_group = event.data.can_leave_group;
|
|
|
|
user_group_name_dict.delete(group.name);
|
|
|
|
user_group_name_dict.set(group.name, group);
|
|
|
|
}
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2018-03-14 17:52:38 +01:00
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function get_user_group_from_name(name: string): UserGroup | undefined {
|
2017-11-07 08:58:46 +01:00
|
|
|
return user_group_name_dict.get(name);
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2024-09-12 13:51:13 +02:00
|
|
|
export function get_realm_user_groups(include_deactivated = false): UserGroup[] {
|
2023-03-02 01:58:25 +01:00
|
|
|
const user_groups = [...user_group_by_id_dict.values()].sort((a, b) => a.id - b.id);
|
2024-09-12 13:51:13 +02:00
|
|
|
return user_groups.filter((group) => {
|
|
|
|
if (group.is_system_group) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!include_deactivated && group.deactivated) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2024-08-29 16:09:06 +02:00
|
|
|
// This is only used for testing currently, but would be used in
|
|
|
|
// future when we use system groups more and probably show them
|
|
|
|
// in the UI as well.
|
|
|
|
export function get_all_realm_user_groups(): UserGroup[] {
|
|
|
|
const user_groups = [...user_group_by_id_dict.values()].sort((a, b) => a.id - b.id);
|
|
|
|
return user_groups;
|
|
|
|
}
|
|
|
|
|
2023-06-15 18:34:55 +02:00
|
|
|
export function get_user_groups_allowed_to_mention(): UserGroup[] {
|
|
|
|
const user_groups = get_realm_user_groups();
|
|
|
|
return user_groups.filter((group) => {
|
2023-07-14 06:50:33 +02:00
|
|
|
const can_mention_group_id = group.can_mention_group;
|
2024-10-16 06:59:07 +02:00
|
|
|
return is_user_in_setting_group(can_mention_group_id, current_user.user_id);
|
2023-06-15 18:34:55 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-05-05 10:04:57 +02:00
|
|
|
export function is_direct_member_of(user_id: number, user_group_id: number): boolean {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
2019-01-15 00:39:03 +01:00
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2019-01-15 00:39:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
2018-03-04 17:44:12 +01:00
|
|
|
return user_group.members.has(user_id);
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-23 07:32:30 +01:00
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function add_members(user_group_id: number, user_ids: number[]): void {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
2021-08-03 18:26:39 +02:00
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2021-08-03 18:26:39 +02:00
|
|
|
return;
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
|
|
|
|
for (const user_id of user_ids) {
|
2020-01-16 20:12:06 +01:00
|
|
|
user_group.members.add(user_id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2018-03-04 17:39:54 +01:00
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function remove_members(user_group_id: number, user_ids: number[]): void {
|
2019-11-02 00:06:25 +01:00
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
2021-08-03 18:26:39 +02:00
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2021-08-03 18:26:39 +02:00
|
|
|
return;
|
|
|
|
}
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
|
|
|
|
for (const user_id of user_ids) {
|
2020-01-16 20:12:06 +01:00
|
|
|
user_group.members.delete(user_id);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2018-03-04 17:39:54 +01:00
|
|
|
|
2022-05-02 16:41:51 +02:00
|
|
|
export function add_subgroups(user_group_id: number, subgroup_ids: number[]): void {
|
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2022-05-02 16:41:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const subgroup_id of subgroup_ids) {
|
2022-05-16 17:02:44 +02:00
|
|
|
user_group.direct_subgroup_ids.add(subgroup_id);
|
2022-05-02 16:41:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function remove_subgroups(user_group_id: number, subgroup_ids: number[]): void {
|
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2022-05-02 16:41:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const subgroup_id of subgroup_ids) {
|
2022-05-16 17:02:44 +02:00
|
|
|
user_group.direct_subgroup_ids.delete(subgroup_id);
|
2022-05-02 16:41:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-21 20:32:29 +02:00
|
|
|
export function initialize(params: StateData["user_groups"]): void {
|
2020-02-25 12:16:26 +01:00
|
|
|
for (const user_group of params.realm_user_groups) {
|
2021-02-28 00:42:57 +01:00
|
|
|
add(user_group);
|
js: Automatically convert _.each to for…of.
This commit was automatically generated by the following script,
followed by lint --fix and a few small manual lint-related cleanups.
import * as babelParser from "recast/parsers/babel";
import * as recast from "recast";
import * as tsParser from "recast/parsers/typescript";
import { builders as b, namedTypes as n } from "ast-types";
import { Context } from "ast-types/lib/path-visitor";
import K from "ast-types/gen/kinds";
import { NodePath } from "ast-types/lib/node-path";
import assert from "assert";
import fs from "fs";
import path from "path";
import process from "process";
const checkExpression = (node: n.Node): node is K.ExpressionKind =>
n.Expression.check(node);
const checkStatement = (node: n.Node): node is K.StatementKind =>
n.Statement.check(node);
for (const file of process.argv.slice(2)) {
console.log("Parsing", file);
const ast = recast.parse(fs.readFileSync(file, { encoding: "utf8" }), {
parser: path.extname(file) === ".ts" ? tsParser : babelParser,
});
let changed = false;
let inLoop = false;
let replaceReturn = false;
const visitLoop = (...args: string[]) =>
function(this: Context, path: NodePath) {
for (const arg of args) {
this.visit(path.get(arg));
}
const old = { inLoop };
inLoop = true;
this.visit(path.get("body"));
inLoop = old.inLoop;
return false;
};
recast.visit(ast, {
visitDoWhileStatement: visitLoop("test"),
visitExpressionStatement(path) {
const { expression, comments } = path.node;
let valueOnly;
if (
n.CallExpression.check(expression) &&
n.MemberExpression.check(expression.callee) &&
!expression.callee.computed &&
n.Identifier.check(expression.callee.object) &&
expression.callee.object.name === "_" &&
n.Identifier.check(expression.callee.property) &&
["each", "forEach"].includes(expression.callee.property.name) &&
[2, 3].includes(expression.arguments.length) &&
checkExpression(expression.arguments[0]) &&
(n.FunctionExpression.check(expression.arguments[1]) ||
n.ArrowFunctionExpression.check(expression.arguments[1])) &&
[1, 2].includes(expression.arguments[1].params.length) &&
n.Identifier.check(expression.arguments[1].params[0]) &&
((valueOnly = expression.arguments[1].params[1] === undefined) ||
n.Identifier.check(expression.arguments[1].params[1])) &&
(expression.arguments[2] === undefined ||
n.ThisExpression.check(expression.arguments[2]))
) {
const old = { inLoop, replaceReturn };
inLoop = false;
replaceReturn = true;
this.visit(
path
.get("expression")
.get("arguments")
.get(1)
.get("body")
);
inLoop = old.inLoop;
replaceReturn = old.replaceReturn;
const [right, { body, params }] = expression.arguments;
const loop = b.forOfStatement(
b.variableDeclaration("let", [
b.variableDeclarator(
valueOnly ? params[0] : b.arrayPattern([params[1], params[0]])
),
]),
valueOnly
? right
: b.callExpression(
b.memberExpression(right, b.identifier("entries")),
[]
),
checkStatement(body) ? body : b.expressionStatement(body)
);
loop.comments = comments;
path.replace(loop);
changed = true;
}
this.traverse(path);
},
visitForStatement: visitLoop("init", "test", "update"),
visitForInStatement: visitLoop("left", "right"),
visitForOfStatement: visitLoop("left", "right"),
visitFunction(path) {
this.visit(path.get("params"));
const old = { replaceReturn };
replaceReturn = false;
this.visit(path.get("body"));
replaceReturn = old.replaceReturn;
return false;
},
visitReturnStatement(path) {
if (replaceReturn) {
assert(!inLoop); // could use labeled continue if this ever fires
const { argument, comments } = path.node;
if (argument === null) {
const s = b.continueStatement();
s.comments = comments;
path.replace(s);
} else {
const s = b.expressionStatement(argument);
s.comments = comments;
path.replace(s, b.continueStatement());
}
return false;
}
this.traverse(path);
},
visitWhileStatement: visitLoop("test"),
});
if (changed) {
console.log("Writing", file);
fs.writeFileSync(file, recast.print(ast).code, { encoding: "utf8" });
}
}
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-06 06:19:47 +01:00
|
|
|
}
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2017-11-07 08:58:46 +01:00
|
|
|
|
2024-01-03 05:40:03 +01:00
|
|
|
export function is_user_group(
|
|
|
|
item: (UserOrMention & {members: undefined}) | UserGroup,
|
|
|
|
): item is UserGroup {
|
2020-05-27 00:50:02 +02:00
|
|
|
return item.members !== undefined;
|
2021-02-28 00:42:57 +01:00
|
|
|
}
|
2021-06-01 14:49:13 +02:00
|
|
|
|
2024-07-11 03:03:54 +02:00
|
|
|
export function is_empty_group(user_group_id: number): boolean {
|
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
|
|
|
if (user_group === undefined) {
|
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (user_group.members.size > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if all the recursive subgroups are empty.
|
|
|
|
// Correctness of this algorithm relying on the ES6 Set
|
|
|
|
// implementation having the property that a `for of` loop will
|
|
|
|
// visit all items that are added to the set during the loop.
|
|
|
|
const subgroup_ids = new Set(user_group.direct_subgroup_ids);
|
|
|
|
for (const subgroup_id of subgroup_ids) {
|
|
|
|
const subgroup = user_group_by_id_dict.get(subgroup_id);
|
|
|
|
if (subgroup === undefined) {
|
|
|
|
blueslip.error("Could not find subgroup", {subgroup_id});
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (subgroup.members.size > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (const direct_subgroup_id of subgroup.direct_subgroup_ids) {
|
|
|
|
subgroup_ids.add(direct_subgroup_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-08-03 18:26:39 +02:00
|
|
|
export function get_user_groups_of_user(user_id: number): UserGroup[] {
|
2021-06-01 14:49:13 +02:00
|
|
|
const user_groups_realm = get_realm_user_groups();
|
2022-05-04 13:34:25 +02:00
|
|
|
const groups_of_user = user_groups_realm.filter((group) =>
|
2022-05-05 10:04:57 +02:00
|
|
|
is_direct_member_of(user_id, group.id),
|
2022-05-04 13:34:25 +02:00
|
|
|
);
|
2021-06-01 14:49:13 +02:00
|
|
|
return groups_of_user;
|
|
|
|
}
|
2022-05-04 13:16:50 +02:00
|
|
|
|
2022-05-17 16:17:04 +02:00
|
|
|
export function get_recursive_subgroups(target_user_group: UserGroup): Set<number> | undefined {
|
2022-05-04 13:16:50 +02:00
|
|
|
// Correctness of this algorithm relying on the ES6 Set
|
|
|
|
// implementation having the property that a `for of` loop will
|
|
|
|
// visit all items that are added to the set during the loop.
|
2022-05-16 17:02:44 +02:00
|
|
|
const subgroup_ids = new Set(target_user_group.direct_subgroup_ids);
|
2022-05-04 13:16:50 +02:00
|
|
|
for (const subgroup_id of subgroup_ids) {
|
|
|
|
const subgroup = user_group_by_id_dict.get(subgroup_id);
|
|
|
|
if (subgroup === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find subgroup", {subgroup_id});
|
2022-05-04 13:16:50 +02:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
2022-05-16 17:02:44 +02:00
|
|
|
for (const direct_subgroup_id of subgroup.direct_subgroup_ids) {
|
2022-05-04 13:16:50 +02:00
|
|
|
subgroup_ids.add(direct_subgroup_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return subgroup_ids;
|
|
|
|
}
|
2022-05-02 11:55:30 +02:00
|
|
|
|
2024-07-08 17:34:16 +02:00
|
|
|
export function get_recursive_group_members(target_user_group: UserGroup): Set<number> {
|
|
|
|
const members = new Set(target_user_group.members);
|
|
|
|
const subgroup_ids = get_recursive_subgroups(target_user_group);
|
|
|
|
|
|
|
|
if (subgroup_ids === undefined) {
|
|
|
|
return members;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const subgroup_id of subgroup_ids) {
|
|
|
|
const subgroup = user_group_by_id_dict.get(subgroup_id);
|
|
|
|
assert(subgroup !== undefined);
|
|
|
|
for (const member of subgroup.members) {
|
|
|
|
members.add(member);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return members;
|
|
|
|
}
|
|
|
|
|
2024-10-25 11:38:36 +02:00
|
|
|
export function check_group_can_be_subgroup(
|
|
|
|
subgroup: UserGroup,
|
|
|
|
target_user_group: UserGroup,
|
|
|
|
): boolean {
|
2024-10-23 18:00:10 +02:00
|
|
|
// This logic could be optimized if we maintained a reverse map
|
|
|
|
// from each group to the groups containing it, which might be a
|
|
|
|
// useful data structure for other code paths as well.
|
2024-10-25 15:30:00 +02:00
|
|
|
if (subgroup.deactivated) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-10-23 18:00:10 +02:00
|
|
|
const already_subgroup_ids = target_user_group.direct_subgroup_ids;
|
2024-10-25 11:38:36 +02:00
|
|
|
if (subgroup.id === target_user_group.id) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-23 18:00:10 +02:00
|
|
|
|
2024-10-25 11:38:36 +02:00
|
|
|
if (already_subgroup_ids.has(subgroup.id)) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-10-23 18:00:10 +02:00
|
|
|
|
2024-10-25 11:38:36 +02:00
|
|
|
const recursive_subgroup_ids = get_recursive_subgroups(subgroup);
|
|
|
|
assert(recursive_subgroup_ids !== undefined);
|
|
|
|
if (recursive_subgroup_ids.has(target_user_group.id)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function get_potential_subgroups(target_user_group_id: number): UserGroup[] {
|
|
|
|
const target_user_group = get_user_group_from_id(target_user_group_id);
|
|
|
|
return get_all_realm_user_groups().filter((user_group) =>
|
|
|
|
check_group_can_be_subgroup(user_group, target_user_group),
|
|
|
|
);
|
2024-10-23 18:00:10 +02:00
|
|
|
}
|
|
|
|
|
2024-10-23 09:00:59 +02:00
|
|
|
export function get_direct_subgroups_of_group(target_user_group: UserGroup): UserGroup[] {
|
|
|
|
const direct_subgroups = [];
|
|
|
|
const subgroup_ids = target_user_group.direct_subgroup_ids;
|
|
|
|
for (const subgroup_id of subgroup_ids) {
|
|
|
|
const subgroup = user_group_by_id_dict.get(subgroup_id);
|
|
|
|
assert(subgroup !== undefined);
|
|
|
|
direct_subgroups.push(subgroup);
|
|
|
|
}
|
|
|
|
return direct_subgroups;
|
|
|
|
}
|
|
|
|
|
2024-10-16 11:17:58 +02:00
|
|
|
export function is_user_in_group(
|
|
|
|
user_group_id: number,
|
|
|
|
user_id: number,
|
|
|
|
direct_member_only = false,
|
|
|
|
): boolean {
|
2022-05-02 11:55:30 +02:00
|
|
|
const user_group = user_group_by_id_dict.get(user_group_id);
|
|
|
|
if (user_group === undefined) {
|
2023-04-24 15:57:45 +02:00
|
|
|
blueslip.error("Could not find user group", {user_group_id});
|
2022-05-02 11:55:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (is_direct_member_of(user_id, user_group_id)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-10-16 11:17:58 +02:00
|
|
|
if (direct_member_only) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-05-17 16:17:04 +02:00
|
|
|
const subgroup_ids = get_recursive_subgroups(user_group);
|
2022-05-02 11:55:30 +02:00
|
|
|
if (subgroup_ids === undefined) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const group_id of subgroup_ids) {
|
|
|
|
if (is_direct_member_of(user_id, group_id)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2023-01-09 12:04:10 +01:00
|
|
|
|
2024-08-28 16:22:27 +02:00
|
|
|
export function is_user_in_setting_group(
|
2024-10-11 13:06:58 +02:00
|
|
|
setting_group: GroupSettingValue,
|
2024-08-28 16:22:27 +02:00
|
|
|
user_id: number,
|
|
|
|
): boolean {
|
|
|
|
if (typeof setting_group === "number") {
|
|
|
|
return is_user_in_group(setting_group, user_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
const direct_members = setting_group.direct_members;
|
|
|
|
if (direct_members.includes(user_id)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const direct_subgroups = setting_group.direct_subgroups;
|
|
|
|
for (const direct_subgroup_id of direct_subgroups) {
|
|
|
|
if (is_user_in_group(direct_subgroup_id, user_id)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-07-08 19:00:08 +02:00
|
|
|
function get_display_name_for_system_group_option(setting_name: string, name: string): string {
|
|
|
|
// We use a special label for the "Nobody" system group for clarity.
|
|
|
|
if (setting_name === "direct_message_permission_group" && name === "Nobody") {
|
|
|
|
return $t({defaultMessage: "Direct messages disabled"});
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2024-09-10 13:37:01 +02:00
|
|
|
export function check_system_user_group_allowed_for_setting(
|
|
|
|
group_name: string,
|
|
|
|
group_setting_config: GroupPermissionSetting,
|
2024-08-29 16:09:06 +02:00
|
|
|
for_new_settings_ui: boolean,
|
2024-09-10 13:37:01 +02:00
|
|
|
): boolean {
|
|
|
|
const {
|
|
|
|
allow_internet_group,
|
|
|
|
allow_owners_group,
|
|
|
|
allow_nobody_group,
|
|
|
|
allow_everyone_group,
|
|
|
|
allowed_system_groups,
|
|
|
|
} = group_setting_config;
|
|
|
|
|
|
|
|
if (!allow_internet_group && group_name === "role:internet") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!allow_owners_group && group_name === "role:owners") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-29 16:02:17 +02:00
|
|
|
if ((!allow_nobody_group || for_new_settings_ui) && group_name === "role:nobody") {
|
2024-09-10 13:37:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!allow_everyone_group && group_name === "role:everyone") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allowed_system_groups.length && !allowed_system_groups.includes(group_name)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-29 16:02:17 +02:00
|
|
|
if (
|
|
|
|
group_name === "role:fullmembers" &&
|
|
|
|
for_new_settings_ui &&
|
|
|
|
realm.realm_waiting_period_threshold === 0
|
|
|
|
) {
|
|
|
|
// We hide the full members group in the typeahead when
|
|
|
|
// there is no separation between member and full member
|
|
|
|
// users due to organization not having set a waiting
|
|
|
|
// period for member users to become full members.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-09-10 13:37:01 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-08-29 18:09:47 +02:00
|
|
|
export function get_realm_user_groups_for_setting(
|
2023-04-10 12:56:36 +02:00
|
|
|
setting_name: string,
|
2023-10-31 04:36:05 +01:00
|
|
|
setting_type: "realm" | "stream" | "group",
|
2024-08-29 16:09:06 +02:00
|
|
|
for_new_settings_ui = false,
|
2024-08-29 18:09:47 +02:00
|
|
|
): UserGroup[] {
|
2023-10-31 04:36:05 +01:00
|
|
|
const group_setting_config = group_permission_settings.get_group_permission_setting_config(
|
|
|
|
setting_name,
|
|
|
|
setting_type,
|
|
|
|
);
|
2023-04-10 12:56:36 +02:00
|
|
|
|
|
|
|
if (group_setting_config === undefined) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2023-02-17 08:20:12 +01:00
|
|
|
const system_user_groups = settings_config.system_user_groups_list
|
2024-09-10 13:37:01 +02:00
|
|
|
.filter((group) =>
|
2024-08-29 16:09:06 +02:00
|
|
|
check_system_user_group_allowed_for_setting(
|
|
|
|
group.name,
|
|
|
|
group_setting_config,
|
|
|
|
for_new_settings_ui,
|
|
|
|
),
|
2024-09-10 13:37:01 +02:00
|
|
|
)
|
2023-01-09 12:04:10 +01:00
|
|
|
.map((group) => {
|
|
|
|
const user_group = get_user_group_from_name(group.name);
|
|
|
|
if (!user_group) {
|
|
|
|
throw new Error(`Unknown group name: ${group.name}`);
|
|
|
|
}
|
2024-08-29 18:09:47 +02:00
|
|
|
return user_group;
|
2023-01-09 12:04:10 +01:00
|
|
|
});
|
2023-02-17 08:20:12 +01:00
|
|
|
|
2024-10-16 06:59:07 +02:00
|
|
|
if (!page_params.development_environment || group_setting_config.require_system_group) {
|
2023-02-17 08:20:12 +01:00
|
|
|
return system_user_groups;
|
|
|
|
}
|
|
|
|
|
2024-08-29 18:09:47 +02:00
|
|
|
const user_groups_excluding_system_groups = get_realm_user_groups();
|
2023-02-17 08:20:12 +01:00
|
|
|
|
2023-03-02 01:58:25 +01:00
|
|
|
return [...system_user_groups, ...user_groups_excluding_system_groups];
|
2023-01-09 12:04:10 +01:00
|
|
|
}
|
2024-07-08 18:32:25 +02:00
|
|
|
|
2024-08-29 18:09:47 +02:00
|
|
|
export function get_realm_user_groups_for_dropdown_list_widget(
|
|
|
|
setting_name: string,
|
|
|
|
setting_type: "realm" | "stream" | "group",
|
|
|
|
): UserGroupForDropdownListWidget[] {
|
|
|
|
const allowed_setting_groups = get_realm_user_groups_for_setting(setting_name, setting_type);
|
|
|
|
|
|
|
|
return allowed_setting_groups.map((group) => {
|
|
|
|
if (!group.is_system_group) {
|
|
|
|
return {
|
|
|
|
name: group.name,
|
|
|
|
unique_id: group.id,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const display_name = settings_config.system_user_groups_list.find(
|
|
|
|
(system_group) => system_group.name === group.name,
|
user_groups: Do not show "role:" prefix for system groups.
Instead of showing the actual names like "role:everyone",
"role:moderators", etc. for system groups, we show
"Everyone", "Moderators", etc. for system user group in
pills, typeaheads and popovers.
Though system groups are not shown in typeahead as of
this commit, we update the typeahead code as well to
not conside "role:" prefix while matching with the
query as we would soon show system groups in typeahead
when we would add new UI for group-based settings.
Previously, only "role:everyone" group was shown as
"Everyone" in popover and pills but for other system
groups their original names for shown and this commit
changes that behavior to be same for all system groups.
The original display_name field for
settings_config.system_user_groups_list objects, which
was used for dropdown widgets, is also renamed so that
we can use display_name field for the names to be used
in other places.
2024-09-02 16:05:45 +02:00
|
|
|
)!.dropdown_option_name;
|
2024-08-29 18:09:47 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
name: get_display_name_for_system_group_option(setting_name, display_name),
|
|
|
|
unique_id: group.id,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
user_groups: Do not show "role:" prefix for system groups.
Instead of showing the actual names like "role:everyone",
"role:moderators", etc. for system groups, we show
"Everyone", "Moderators", etc. for system user group in
pills, typeaheads and popovers.
Though system groups are not shown in typeahead as of
this commit, we update the typeahead code as well to
not conside "role:" prefix while matching with the
query as we would soon show system groups in typeahead
when we would add new UI for group-based settings.
Previously, only "role:everyone" group was shown as
"Everyone" in popover and pills but for other system
groups their original names for shown and this commit
changes that behavior to be same for all system groups.
The original display_name field for
settings_config.system_user_groups_list objects, which
was used for dropdown widgets, is also renamed so that
we can use display_name field for the names to be used
in other places.
2024-09-02 16:05:45 +02:00
|
|
|
export function get_display_group_name(group_name: string): string {
|
|
|
|
const group = settings_config.system_user_groups_list.find(
|
|
|
|
(system_group) => system_group.name === group_name,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (group === undefined) {
|
|
|
|
return group_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return group.display_name;
|
2024-07-08 18:32:25 +02:00
|
|
|
}
|