TeaWeb/shared/js/ui/modal/permission/ModalPermissionEditor.tsx
2020-09-24 11:24:31 +02:00

1211 lines
46 KiB
TypeScript

import {spawnReactModal} from "tc-shared/ui/react-elements/Modal";
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
import * as React from "react";
import {useState} from "react";
import {ContextDivider} from "tc-shared/ui/react-elements/ContextDivider";
import {Translatable} from "tc-shared/ui/react-elements/i18n";
import {Registry} from "tc-shared/events";
import {
EditorGroupedPermissions,
PermissionEditor,
PermissionEditorEvents
} from "tc-shared/ui/modal/permission/PermissionEditor";
import {SideBar} from "tc-shared/ui/modal/permission/TabHandler";
import {Group, GroupTarget, GroupType} from "tc-shared/permission/GroupManager";
import {createErrorModal, createInfoModal} from "tc-shared/ui/elements/Modal";
import {ClientNameInfo, CommandResult} from "tc-shared/connection/ServerConnectionDeclaration";
import {formatMessage} from "tc-shared/ui/frames/chat";
import {spawnYesNo} from "tc-shared/ui/modal/ModalYesNo";
import {tra} from "tc-shared/i18n/localize";
import {PermissionType} from "tc-shared/permission/PermissionType";
import {GroupedPermissions, PermissionValue} from "tc-shared/permission/PermissionManager";
import {spawnIconSelect} from "tc-shared/ui/modal/ModalIconSelect";
import {Settings, settings} from "tc-shared/settings";
import {
senseless_channel_group_permissions,
senseless_channel_permissions,
senseless_client_channel_permissions,
senseless_client_permissions,
senseless_server_group_permissions
} from "tc-shared/ui/modal/permission/SenselessPermissions";
import {spawnGroupCreate} from "tc-shared/ui/modal/ModalGroupCreate";
import {spawnModalGroupPermissionCopy} from "tc-shared/ui/modal/ModalGroupPermissionCopy";
import {InternalModal} from "tc-shared/ui/react-elements/internal-modal/Controller";
import {ErrorCode} from "tc-shared/connection/ErrorCode";
const cssStyle = require("./ModalPermissionEditor.scss");
export type PermissionEditorTab = "groups-server" | "groups-channel" | "channel" | "client" | "client-channel";
export type PermissionEditorSubject =
"groups-server"
| "groups-channel"
| "channel"
| "client"
| "client-channel"
| "none";
export const PermissionTabName: { [T in PermissionEditorTab]: { name: string, translated: string } } = {
"groups-server": {name: "Server Groups", translated: tr("Server Groups")},
"groups-channel": {name: "Channel Groups", translated: tr("Channel Groups")},
"channel": {name: "Channel Permissions", translated: tr("Channel Permissions")},
"client": {name: "Client Permissions", translated: tr("Client Permissions")},
"client-channel": {name: "Client Channel Permissions", translated: tr("Client Channel Permissions")},
};
export type GroupProperties = {
id: number,
type: "query" | "template" | "normal";
name: string,
iconId: number,
sortId: number;
saveDB: boolean;
needed_modify_power: number;
needed_member_add: number;
needed_member_remove: number;
};
export type GroupUpdateEntry = {
property: "name" | "icon" | "sort" | "save";
value: any
};
export type ChannelInfo = {
id: number;
iconId: number;
name: string;
depth: number;
}
export interface PermissionModalEvents {
action_activate_tab: {
tab: PermissionEditorTab,
activeGroupId?: number;
activeChannelId?: number;
activeClientDatabaseId?: number;
},
action_select_group: {
target: "server" | "channel",
id: number
},
action_select_channel: {
target: "channel" | "client-channel";
id: number
},
action_select_client: {
target: "client" | "client-channel";
id: number | string | undefined;
}
action_set_permission_editor_subject: {
mode: PermissionEditorSubject;
groupId?: number;
channelId?: number;
clientDatabaseId?: number;
}
action_create_group: { target: "server" | "channel", sourceGroup?: number },
action_rename_group: { target: "server" | "channel", id: number | "selected", newName: string },
action_rename_group_result: {
target: "server" | "channel";
id: number;
status: "success" | "error";
error?: string;
}
action_delete_group: { target: "server" | "channel", id: number | "selected", mode: "ask" | "force" },
action_delete_group_result: {
target: "server" | "channel";
id: number;
status: "success" | "error";
error?: string;
},
action_group_copy_permissions: { target: "server" | "channel", sourceGroup: number },
action_server_group_add_client: {
id: number;
client: number | string; /* string would be the unique id */
},
action_server_group_add_client_result: {
id: number;
client: number | string;
status: "success" | "error" | "no-permissions";
error?: string;
}
action_server_group_remove_client: {
id: number;
client: number;
},
action_server_group_remove_client_result: {
id: number;
client: number;
status: "success" | "error" | "no-permissions";
error?: string;
}
query_groups: {
target: "server" | "channel",
},
query_groups_result: {
target: "server" | "channel",
groups: GroupProperties[]
},
query_group_clients: {
id: number
},
query_group_clients_result: {
id: number,
status: "success" | "error" | "no-permissions",
error?: string;
clients?: {
name: string;
databaseId: number;
uniqueId: string;
}[]
},
query_channels: {},
query_channels_result: {
channels: ChannelInfo[]
}
query_client_permissions: {}, /* will cause the notify_client_permissions */
query_client_info: {
client: number | string; /* client database id or unique id */
},
query_client_info_result: {
client: number | string;
state: "success" | "error" | "no-such-client" | "no-permission";
error?: string;
info?: { name: string, uniqueId: string, databaseId: number },
failedPermission?: string;
}
notify_group_updated: {
target: "server" | "channel";
id: number;
properties: GroupUpdateEntry[];
},
notify_groups_created: {
target: "server" | "channel";
groups: GroupProperties[]
},
notify_groups_deleted: {
target: "server" | "channel";
groups: number[]
},
notify_groups_reset: {}
notify_client_permissions: {
permissionModifyPower: number;
serverGroupCreate: boolean,
channelGroupCreate: boolean,
serverGroupModifyPower: number,
channelGroupModifyPower: number,
modifyQueryGroups: boolean,
modifyTemplateGroups: boolean
serverGroupMemberAddPower: number,
serverGroupMemberRemovePower: number,
serverGroupPermissionList: boolean,
channelGroupPermissionList: boolean,
channelPermissionList: boolean,
clientPermissionList: boolean,
clientChannelPermissionList: boolean
},
notify_client_list_toggled: { visible: boolean },
notify_channel_updated: { id: number, property: "name" | "icon", value: any },
notify_destroy: {}
}
const ActiveTabInfo = (props: { events: Registry<PermissionModalEvents> }) => {
const [activeTab, setActiveTab] = useState<PermissionEditorTab>("groups-server");
props.events.reactUse("action_activate_tab", event => setActiveTab(event.tab));
return <div className={cssStyle.header + " " + cssStyle.activeTabInfo}>
<div className={cssStyle.entry}>
<a title={PermissionTabName[activeTab].translated}>
<Translatable trIgnore={true}>{PermissionTabName[activeTab].name}</Translatable>
</a>
</div>
</div>
};
const TabSelectorEntry = (props: { events: Registry<PermissionModalEvents>, entry: PermissionEditorTab }) => {
const [active, setActive] = useState(props.entry === "groups-server");
props.events.reactUse("action_activate_tab", event => setActive(event.tab === props.entry));
return <div className={cssStyle.entry + " " + (active ? cssStyle.selected : "")}
onClick={() => !active && props.events.fire("action_activate_tab", {tab: props.entry})}>
<a title={PermissionTabName[props.entry].translated}>
<Translatable trIgnore={true}>{PermissionTabName[props.entry].translated}</Translatable>
</a>
</div>;
};
const TabSelector = (props: { events: Registry<PermissionModalEvents> }) => {
return <div className={cssStyle.header + " " + cssStyle.tabSelector}>
<TabSelectorEntry events={props.events} entry={"groups-server"}/>
<TabSelectorEntry events={props.events} entry={"groups-channel"}/>
<TabSelectorEntry events={props.events} entry={"channel"}/>
<TabSelectorEntry events={props.events} entry={"client"}/>
<TabSelectorEntry events={props.events} entry={"client-channel"}/>
</div>;
};
export type DefaultTabValues = { groupId?: number, channelId?: number, clientDatabaseId?: number };
class PermissionEditorModal extends InternalModal {
readonly modalEvents = new Registry<PermissionModalEvents>();
readonly editorEvents = new Registry<PermissionEditorEvents>();
readonly connection: ConnectionHandler;
readonly defaultTab: PermissionEditorTab;
readonly defaultTabValues: DefaultTabValues;
constructor(connection: ConnectionHandler, defaultTab: PermissionEditorTab, defaultTabValues?: DefaultTabValues) {
super();
this.defaultTab = defaultTab;
this.defaultTabValues = defaultTabValues || {};
this.modalEvents.enableDebug("modal-permissions");
this.editorEvents.enableDebug("permissions-editor");
this.connection = connection;
initializePermissionModalResultHandlers(this.modalEvents);
initializePermissionModalController(connection, this.modalEvents);
initializePermissionEditor(connection, this.modalEvents, this.editorEvents);
this.modalEvents.on("action_activate_tab", event => this.editorEvents.fire("action_toggle_client_button", {visible: event.tab === "groups-server"}));
this.editorEvents.on("action_toggle_client_list", event => this.modalEvents.fire("notify_client_list_toggled", {visible: event.visible}));
}
protected onInitialize() {
this.modalEvents.fire("query_client_permissions");
this.modalEvents.fire("action_activate_tab", {
tab: this.defaultTab,
activeChannelId: this.defaultTabValues?.channelId,
activeGroupId: this.defaultTabValues?.groupId,
activeClientDatabaseId: this.defaultTabValues?.clientDatabaseId
});
}
protected onDestroy() {
this.modalEvents.fire("notify_destroy");
this.modalEvents.destroy();
}
renderBody() {
return (
<div className={cssStyle.container}>
<ContextDivider id={"permission-editor"} defaultValue={25} direction={"horizontal"}>
<div className={cssStyle.contextContainer + " " + cssStyle.left}>
<ActiveTabInfo events={this.modalEvents}/>
<SideBar modalEvents={this.modalEvents} editorEvents={this.editorEvents}
connection={this.connection}/>
</div>
<div className={cssStyle.contextContainer + " " + cssStyle.right}>
<TabSelector events={this.modalEvents}/>
<PermissionEditor events={this.editorEvents} connection={this.connection}/>
</div>
</ContextDivider>
</div>
);
}
title(): React.ReactElement<Translatable> {
return <Translatable>Server permissions</Translatable>;
}
}
export function spawnPermissionEditorModal(connection: ConnectionHandler, defaultTab: PermissionEditorTab = "groups-server", values?: DefaultTabValues) {
const modal = spawnReactModal(PermissionEditorModal, connection, defaultTab, values);
modal.show();
}
function initializePermissionModalResultHandlers(events: Registry<PermissionModalEvents>) {
events.on("action_rename_group_result", event => {
if (event.status === "error") {
createErrorModal(tr("Failed to rename group"), formatMessage(tr("Failed to rename group:{:br:}"), event.error)).open();
} else {
createInfoModal(tr("Group renamed"), tr("The group has been renamed.")).open();
}
});
events.on("action_delete_group", event => {
if (event.mode === "force")
return;
spawnYesNo(tr("Are you sure?"), formatMessage(tr("Do you really want to delete this group?")), result => {
if (result !== true)
return;
events.fire("action_delete_group", {id: event.id, mode: "force", target: event.target});
});
});
events.on("action_delete_group_result", event => {
if (event.status === "success") {
createInfoModal(tr("Group deleted"), tr("The channel group has been deleted.")).open();
} else {
createErrorModal(tr("Failed to delete group"), tra("Failed to delete group:\n{}", event.error)).open();
}
});
events.on("action_server_group_remove_client_result", event => {
if (event.status === "error") {
createErrorModal(tr("Failed to remove client"), tra("Failed to remove client from server group:\n{}", event.error)).open();
} else if (event.status === "no-permissions") {
createErrorModal(tr("Failed to remove client"), tra("Failed to remove client from server group:\nNo permissions.")).open();
}
});
events.on("action_server_group_add_client_result", event => {
if (event.status === "error") {
createErrorModal(tr("Failed to add client"), tra("Failed to add client to server group:\n{}", event.error)).open();
} else if (event.status === "no-permissions") {
createErrorModal(tr("Failed to add client"), tra("Failed to add client to group:\nNo permissions.")).open();
}
});
}
const stringifyError = error => {
if (error instanceof CommandResult) {
if (error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS)
return tr("insufficient permissions");
else
return error.message + (error.extra_message ? " (" + error.extra_message + ")" : "");
} else if (error instanceof Error) {
return error.message;
} else if (typeof error !== "string") {
return tr("Lookup the console");
}
return error;
};
function initializePermissionModalController(connection: ConnectionHandler, events: Registry<PermissionModalEvents>) {
events.on("query_groups", event => {
const groups = event.target === "server" ? connection.groups.serverGroups : connection.groups.channelGroups;
events.fire_async("query_groups_result", {
target: event.target, groups: groups.map(group => {
return {
id: group.id,
name: group.name,
iconId: group.properties.iconid,
sortId: group.properties.sortid,
saveDB: group.properties.savedb,
type: group.type === GroupType.QUERY ? "query" : group.type === GroupType.TEMPLATE ? "template" : "normal",
needed_member_add: group.requiredMemberAddPower,
needed_member_remove: group.requiredMemberRemovePower,
needed_modify_power: group.requiredModifyPower
} as GroupProperties
})
});
});
/* group update listener */
{
const initializeGroupListener = (group: Group) => {
let unregister = group.events.on("notify_properties_updated", event => {
let updates: GroupUpdateEntry[] = [];
for (const update of event.updated_properties) {
switch (update) {
case "name":
updates.push({property: "name", value: group.name});
break;
case "icon":
updates.push({property: "icon", value: group.properties.iconid});
break;
case "sort-id":
updates.push({property: "sort", value: group.properties.sortid});
break;
case "save-db":
updates.push({property: "save", value: group.properties.savedb});
break;
default:
break;
}
}
events.fire("notify_group_updated", {
target: group.target === GroupTarget.SERVER ? "server" : "channel",
id: group.id,
properties: updates
});
});
const doUnregister = () => {
unregister && unregister();
unregister = undefined;
};
group.events.on("notify_group_deleted", doUnregister);
events.on("notify_destroy", doUnregister);
};
events.on("notify_destroy", connection.groups.events.on("notify_reset", () => {
events.fire("notify_groups_reset");
}));
events.on("notify_destroy", connection.groups.events.on("notify_groups_created", event => {
const channelGroups: GroupProperties[] = [];
const serverGroups: GroupProperties[] = [];
for (const group of event.groups) {
(group.target === GroupTarget.SERVER ? serverGroups : channelGroups).push({
iconId: group.properties.iconid,
id: group.id,
name: group.name,
saveDB: group.properties.savedb,
sortId: group.properties.sortid,
type: group.type === GroupType.QUERY ? "query" : group.type === GroupType.TEMPLATE ? "template" : "normal",
needed_member_add: group.requiredMemberAddPower,
needed_member_remove: group.requiredMemberRemovePower,
needed_modify_power: group.requiredModifyPower
});
initializeGroupListener(group);
}
if (channelGroups.length > 0)
events.fire("notify_groups_created", {groups: channelGroups, target: "channel"});
if (serverGroups.length > 0)
events.fire("notify_groups_created", {groups: serverGroups, target: "server"});
}));
events.on("notify_destroy", connection.groups.events.on("notify_groups_deleted", event => {
const channelGroups: number[] = [];
const serverGroups: number[] = [];
for (const group of event.groups)
(group.target === GroupTarget.SERVER ? serverGroups : channelGroups).push(group.id);
if (channelGroups.length > 0)
events.fire("notify_groups_deleted", {groups: channelGroups, target: "channel"});
if (serverGroups.length > 0)
events.fire("notify_groups_deleted", {groups: serverGroups, target: "server"});
}));
connection.groups.serverGroups.forEach(initializeGroupListener);
connection.groups.channelGroups.forEach(initializeGroupListener);
}
{
/* group actions */
let selectedChannelGroup = 0, selectedServerGroup = 0;
events.on("action_select_group", event => {
if (event.target === "channel")
selectedChannelGroup = event.id;
else
selectedServerGroup = event.id;
});
events.on("action_rename_group", event => {
const groupId = event.id === "selected" ? event.target === "channel" ? selectedChannelGroup : selectedServerGroup : event.id;
let payload = {name: event.newName} as any;
if (event.target === "channel")
payload.cgid = groupId;
else
payload.sgid = groupId;
connection.serverConnection.send_command(event.target + "grouprename", payload).then(() => {
events.fire("action_rename_group_result", {id: groupId, status: "success", target: event.target});
}).catch(error => {
console.warn(tr("Failed to rename group: %o"), error);
events.fire("action_rename_group_result", {
id: groupId,
status: "error",
target: event.target,
error: stringifyError(error)
});
});
});
events.on("action_delete_group", event => {
/* ask will be handled within the modal */
if (event.mode === "ask")
return;
const groupId = event.id === "selected" ? event.target === "channel" ? selectedChannelGroup : selectedServerGroup : event.id;
let payload = {force: true} as any;
if (event.target === "channel")
payload.cgid = groupId;
else
payload.sgid = groupId;
connection.serverConnection.send_command(event.target + "groupdel", payload).then(() => {
events.fire("action_delete_group_result", {id: groupId, status: "success", target: event.target});
}).catch(error => {
console.warn(tr("Failed to delete group: %o"), error);
events.fire("action_delete_group_result", {
id: groupId,
status: "error",
target: event.target,
error: stringifyError(error)
});
});
});
events.on("action_create_group", event => spawnGroupCreate(connection, event.target, event.sourceGroup));
events.on("action_group_copy_permissions", event => spawnModalGroupPermissionCopy(connection, event.target, event.sourceGroup));
}
/* general permissions */
{
const sendClientPermissions = () => {
events.fire("notify_client_permissions", {
permissionModifyPower: connection.permissions.neededPermission(PermissionType.I_PERMISSION_MODIFY_POWER).valueOr(0),
modifyQueryGroups: connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1),
modifyTemplateGroups: connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1),
channelGroupCreate: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_CREATE).granted(1),
serverGroupCreate: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_CREATE).granted(1),
channelGroupModifyPower: connection.permissions.neededPermission(PermissionType.I_CHANNEL_GROUP_MODIFY_POWER).valueOr(0),
serverGroupModifyPower: connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MODIFY_POWER).valueOr(0),
serverGroupMemberAddPower: connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MEMBER_ADD_POWER).valueOr(0),
serverGroupMemberRemovePower: connection.permissions.neededPermission(PermissionType.I_SERVER_GROUP_MEMBER_REMOVE_POWER).valueOr(0),
serverGroupPermissionList: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1),
channelGroupPermissionList: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1),
channelPermissionList: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1),
clientPermissionList: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1),
clientChannelPermissionList: connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST).granted(1),
});
};
events.on("query_client_permissions", () => sendClientPermissions());
events.on("notify_destroy", connection.permissions.events.on("client_permissions_changed", sendClientPermissions));
}
events.on("query_group_clients", event => {
connection.serverConnection.command_helper.requestClientsByServerGroup(event.id).then(clients => {
events.fire("query_group_clients_result", {
id: event.id, status: "success", clients: clients.map(e => {
return {
name: e.client_nickname,
uniqueId: e.client_unique_identifier,
databaseId: e.client_database_id
};
})
});
}).catch(error => {
if (error instanceof CommandResult && error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
events.fire("query_group_clients_result", {id: event.id, status: "no-permissions"});
return;
}
console.warn(tr("Failed to request server group client list: %o"), error);
events.fire("query_group_clients_result", {id: event.id, status: "error", error: stringifyError(error)});
});
});
events.on("action_server_group_add_client", event => {
Promise.resolve(event.client).then(client => {
if (typeof client === "number")
return Promise.resolve(client);
return connection.serverConnection.command_helper.getInfoFromUniqueId(client.trim()).then(info => info[0].clientDatabaseId);
}).then(clientDatabaseId => connection.serverConnection.send_command("servergroupaddclient", {
sgid: event.id,
cldbid: clientDatabaseId
})).then(() => {
events.fire("action_server_group_add_client_result", {
id: event.id,
client: event.client,
status: "success"
});
}).catch(error => {
if (error instanceof CommandResult && error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
events.fire("action_server_group_add_client_result", {
id: event.id,
client: event.client,
status: "no-permissions"
});
return;
}
console.warn(tr("Failed to add client %s to server group %d: %o"), event.client.toString(), event.id, error);
events.fire("action_server_group_add_client_result", {
id: event.id,
client: event.client,
status: "error",
error: stringifyError(error)
});
})
});
events.on("action_server_group_remove_client", event => {
connection.serverConnection.send_command("servergroupdelclient", {
sgid: event.id,
cldbid: event.client
}).then(() => {
events.fire("action_server_group_remove_client_result", {
id: event.id,
client: event.client,
status: "success"
});
}).catch(error => {
console.log(tr("Failed to delete client %d from server group %d: %o"), event.client, event.id, error);
events.fire("action_server_group_remove_client_result", {
id: event.id,
client: event.client,
status: "success"
});
});
});
events.on("notify_destroy", connection.channelTree.events.on("notify_channel_updated", event => {
if ('channel_icon_id' in event.updatedProperties)
events.fire("notify_channel_updated", {
id: event.channel.channelId,
property: "icon",
value: event.updatedProperties.channel_icon_id
});
if ('channel_name' in event.updatedProperties)
events.fire("notify_channel_updated", {
id: event.channel.channelId,
property: "name",
value: event.updatedProperties.channel_name
});
}));
events.on("query_channels", () => {
events.fire_async("query_channels_result", {
channels: connection.channelTree.channelsOrdered().map(e => {
return {
id: e.channelId,
name: e.channelName(),
depth: e.channelDepth(),
iconId: e.properties.channel_icon_id
};
})
});
});
events.on("query_client_info", event => {
let promise: Promise<ClientNameInfo[]>;
if (typeof event.client === "number") {
promise = connection.serverConnection.command_helper.getInfoFromClientDatabaseId(event.client);
} else {
promise = connection.serverConnection.command_helper.getInfoFromUniqueId(event.client.trim());
}
promise.then(result => {
if (result.length === 0) {
events.fire("query_client_info_result", {
client: event.client,
state: "no-such-client"
});
return;
}
events.fire("query_client_info_result", {
client: event.client,
state: "success",
info: {
name: result[0].clientNickname,
databaseId: result[0].clientDatabaseId,
uniqueId: result[0].clientUniqueId
}
});
}).catch(error => {
if (error instanceof CommandResult) {
events.fire("query_client_info_result", {
client: event.client,
state: "no-permission",
failedPermission: connection.permissions.resolveInfo(parseInt(error.json["failed_permid"]))?.name || tr("unknwon")
});
return;
}
console.warn(tr("Failed to query client info for %o: %o"), event.client, error);
events.fire("query_client_info_result", {
client: event.client,
state: "error",
error: stringifyError(error)
});
});
});
}
function initializePermissionEditor(connection: ConnectionHandler, modalEvents: Registry<PermissionModalEvents>, events: Registry<PermissionEditorEvents>) {
let clientDatabaseId = 0;
let channelId = 0;
let groupId = 0;
let mode: PermissionEditorSubject = "groups-server";
let serverGroupPermissionList = false;
let channelGroupPermissionList = false;
let channelPermissionList = false;
let clientPermissionList = false;
let clientChannelPermissionList = false;
modalEvents.on("action_activate_tab", event => {
clientDatabaseId = 0;
channelId = 0;
groupId = 0;
events.fire("action_set_mode", {mode: "unset"});
switch (event.tab) {
case "groups-server":
events.fire("action_set_senseless_permissions", {permissions: senseless_server_group_permissions});
break;
case "groups-channel":
events.fire("action_set_senseless_permissions", {permissions: senseless_channel_group_permissions});
break;
case "channel":
events.fire("action_set_senseless_permissions", {permissions: senseless_channel_permissions});
break;
case "client":
events.fire("action_set_senseless_permissions", {permissions: senseless_client_permissions});
break;
case "client-channel":
events.fire("action_set_senseless_permissions", {permissions: senseless_client_channel_permissions});
break;
}
});
const failedPermission = (): string => {
switch (mode) {
case "groups-server":
return serverGroupPermissionList ? undefined : PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST;
case "groups-channel":
return channelGroupPermissionList ? undefined : PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST;
case "client":
return clientPermissionList ? undefined : PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST;
case "channel":
return channelPermissionList ? undefined : PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST;
case "client-channel":
return clientChannelPermissionList ? undefined : PermissionType.B_VIRTUALSERVER_CHANNELCLIENT_PERMISSION_LIST;
default:
return undefined;
}
};
modalEvents.on("notify_client_permissions", event => {
serverGroupPermissionList = event.serverGroupPermissionList;
channelGroupPermissionList = event.channelGroupPermissionList;
channelPermissionList = event.channelPermissionList;
clientPermissionList = event.clientPermissionList;
clientChannelPermissionList = event.clientChannelPermissionList;
const failed = failedPermission();
if (failed) {
events.fire("action_set_mode", {mode: "no-permissions", failedPermission: failed});
}
events.fire("action_set_default_value", {value: event.permissionModifyPower});
});
modalEvents.on("action_set_permission_editor_subject", event => {
channelId = typeof event.channelId === "number" ? event.channelId : channelId;
clientDatabaseId = typeof event.clientDatabaseId === "number" ? event.clientDatabaseId : clientDatabaseId;
groupId = typeof event.groupId === "number" ? event.groupId : groupId;
mode = event.mode;
let editorMode: "unset" | "normal" = "unset";
switch (mode) {
case "none":
editorMode = "unset";
break;
case "groups-server":
case "groups-channel":
editorMode = groupId === 0 ? "unset" : "normal";
break;
case "channel":
editorMode = channelId === 0 ? "unset" : "normal";
break;
case "client":
editorMode = clientDatabaseId === 0 ? "unset" : "normal";
break;
case "client-channel":
editorMode = clientDatabaseId === 0 || channelId === 0 ? "unset" : "normal";
break;
}
const failed = failedPermission();
events.fire("action_set_mode", {mode: failed ? "no-permissions" : editorMode, failedPermission: failed});
if (!failed && editorMode === "normal")
events.fire("query_permission_values");
});
events.on("query_permission_list", () => {
const groups = connection.permissions.groupedPermissions();
const visitGroup = (group: GroupedPermissions): EditorGroupedPermissions => {
return {
groupId: group.group.name + " - " + group.group.begin.toString(),
groupName: group.group.name,
permissions: group.permissions.map(e => {
return {id: e.id, name: e.name, description: e.description}
}),
children: (group.children || []).map(visitGroup)
};
};
events.fire_async("query_permission_list_result", {
hideSenselessPermissions: !settings.static_global(Settings.KEY_PERMISSIONS_SHOW_ALL),
permissions: (groups || []).map(visitGroup)
});
});
events.on("query_permission_values", () => {
let promise: Promise<PermissionValue[]>;
switch (mode) {
case "none":
promise = Promise.reject(tr("Invalid subject"));
break;
case "groups-server":
case "groups-channel":
if (!groupId) {
promise = Promise.reject(tr("Invalid server group"));
break;
}
const group = mode === "groups-server" ? connection.groups.findServerGroup(groupId) : connection.groups.findChannelGroup(groupId);
if (!group) {
promise = Promise.reject(tr("Invalid server group"));
break;
}
promise = connection.groups.request_permissions(group);
break;
case "channel":
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
promise = connection.permissions.requestChannelPermissions(channelId);
break;
case "client":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
promise = connection.permissions.requestClientPermissions(clientDatabaseId);
break;
case "client-channel":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
promise = connection.permissions.requestClientChannelPermissions(clientDatabaseId, channelId);
break;
}
promise.then(permissions => {
events.fire("query_permission_values_result", {
status: "success", permissions: permissions.map(e => {
return {
value: e.value,
name: e.type.name,
granted: e.granted_value,
flagNegate: e.flag_negate,
flagSkip: e.flag_skip
}
})
});
}).catch(error => {
if (error instanceof CommandResult && error.id === ErrorCode.SERVER_INSUFFICIENT_PERMISSIONS) {
events.fire("action_set_mode", {
mode: "no-permissions",
failedPermission: connection.permissions.resolveInfo(parseInt(error.json["failed_permid"]))?.name || tr("unknwon")
});
return;
}
console.warn(tr("Failed to query permissions: %o"), error);
events.fire("query_permission_values_result", {status: "error", error: stringifyError(error)});
});
});
const granted_permission_name = (name: string) => "i_needed_modify_power_" + name.substr(2);
events.on("action_set_permissions", event => {
let promise: Promise<CommandResult>;
let payload = [];
event.permissions.forEach(permission => payload.push({
permsid: permission.mode === "grant" ? granted_permission_name(permission.name) : permission.name,
permvalue: permission.value | 0, /* signed values */
permskip: permission.mode === "grant" ? false : permission.flagSkip,
permnegated: permission.mode === "grant" ? false : permission.flagNegate
}));
switch (mode) {
case "none":
promise = Promise.reject(tr("Invalid subject"));
break;
case "groups-server":
case "groups-channel": {
if (!groupId) {
promise = Promise.reject(tr("Invalid server group"));
break;
}
let prefix = mode === "groups-server" ? "server" : "channel";
if (mode === "groups-server")
payload[0].sgid = groupId;
else
payload[0].cgid = groupId;
promise = connection.serverConnection.send_command(prefix + "groupaddperm", payload);
break;
}
case "channel":
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
payload[0].cid = channelId;
promise = connection.serverConnection.send_command("channeladdperm", payload);
break;
case "client":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
payload[0].cldbid = clientDatabaseId;
promise = connection.serverConnection.send_command("clientaddperm", payload);
break;
case "client-channel":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
payload[0].cldbid = clientDatabaseId;
payload[0].cid = channelId;
promise = connection.serverConnection.send_command("channelclientaddperm", payload);
break;
}
promise.then(result => {
throw result;
}).catch(error => {
if (error instanceof CommandResult) {
if (error.getBulks().length === event.permissions.length) {
events.fire("action_set_permissions_result", {
permissions: error.getBulks().map((e, index) => {
return {
name: event.permissions[index].name,
mode: event.permissions[index].mode,
newValue: e.success ? event.permissions[index].value : undefined,
flagSkip: e.success ? event.permissions[index].flagSkip : undefined,
flagNegate: e.success ? event.permissions[index].flagNegate : undefined
}
})
});
return;
}
}
console.warn(tr("Failed to set permissions: %o"), error);
events.fire("action_set_permissions_result", {
permissions: event.permissions.map(permission => {
return {
name: permission.name,
mode: permission.mode,
newValue: undefined,
flagSkip: undefined,
flagNegate: undefined
}
})
});
})
});
events.on("action_remove_permissions", event => {
let promise: Promise<CommandResult>;
let payload = [];
event.permissions.forEach(permission => payload.push({
permsid: permission.mode === "grant" ? granted_permission_name(permission.name) : permission.name,
}));
switch (mode) {
case "none":
promise = Promise.reject(tr("Invalid subject"));
break;
case "groups-server":
case "groups-channel": {
if (!groupId) {
promise = Promise.reject(tr("Invalid server group"));
break;
}
let prefix = mode === "groups-server" ? "server" : "channel";
if (mode === "groups-server")
payload[0].sgid = groupId;
else
payload[0].cgid = groupId;
promise = connection.serverConnection.send_command(prefix + "groupdelperm", payload);
break;
}
case "channel":
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
payload[0].cid = channelId;
promise = connection.serverConnection.send_command("channeldelperm", payload);
break;
case "client":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
payload[0].cldbid = clientDatabaseId;
promise = connection.serverConnection.send_command("clientdelperm", payload);
break;
case "client-channel":
if (!clientDatabaseId) {
promise = Promise.reject(tr("Invalid client database id"));
break;
}
if (!channelId) {
promise = Promise.reject(tr("Invalid channel id"));
break;
}
payload[0].cid = channelId;
payload[0].cldbid = clientDatabaseId;
promise = connection.serverConnection.send_command("channelclientdelperm", payload);
break;
}
promise.then(result => {
throw result;
}).catch(error => {
if (error instanceof CommandResult) {
if (error.getBulks().length === event.permissions.length) {
events.fire("action_remove_permissions_result", {
permissions: error.getBulks().map((e, index) => {
return {
name: event.permissions[index].name,
mode: event.permissions[index].mode,
success: e.success
}
})
});
return;
}
}
console.warn(tr("Failed to remove permissions: %o"), error);
events.fire("action_remove_permissions_result", {
permissions: event.permissions.map(permission => {
return {
name: permission.name,
mode: permission.mode,
success: false
}
})
});
})
});
events.on("action_open_icon_select", event => {
spawnIconSelect(connection,
id => events.fire("action_set_permissions", {
permissions: [{
mode: "value",
name: PermissionType.I_ICON_ID,
flagSkip: false,
flagNegate: false,
value: id
}]
}),
event.iconId);
});
}