/// /// /// /* TODO: Check needed permissions and may not even try to request, because we dont have the permission. Permissions: b_virtualserver_servergroup_permission_list b_virtualserver_channel_permission_list b_virtualserver_client_permission_list b_virtualserver_channelgroup_permission_list b_virtualserver_channelclient_permission_list b_virtualserver_playlist_permission_list */ interface JQuery { dropdown: any; } namespace Modals { namespace PermissionEditor { export interface PermissionEntry { tag: JQuery; tag_value: JQuery; tag_grant: JQuery; tag_flag_negate: JQuery; tag_flag_skip: JQuery; id: number; filter: string; is_bool: boolean; } export interface PermissionValue { remove: boolean; /* if set remove the set permission (value or granted) */ granted?: number; value?: number; flag_skip?: boolean; flag_negate?: boolean; } export type change_listener_t = (permission: PermissionInfo, value?: PermissionEditor.PermissionValue) => Promise; } enum PermissionEditorMode { VISIBLE, NO_PERMISSION, UNSET } class PermissionEditor { readonly permissions: GroupedPermissions[]; container: JQuery; private mode_container_permissions: JQuery; private mode_container_error_permission: JQuery; private mode_container_unset: JQuery; /* references within the container tag */ private permission_value_map: {[key:number]:PermissionValue} = {}; private listener_change: PermissionEditor.change_listener_t = () => Promise.resolve(); private listener_update: () => any; private entry_editor: ui.PermissionEditor; icon_resolver: (id: number) => Promise; icon_selector: (current_id: number) => Promise; constructor(permissions: GroupedPermissions[]) { this.permissions = permissions; this.entry_editor = new ui.PermissionEditor(permissions); } build_tag() { this.container = $("#tmpl_permission_editor").renderTag(); /* search for that as long we've not that much nodes */ this.mode_container_permissions = this.container.find(".container-mode-permissions"); this.mode_container_error_permission = this.container.find(".container-mode-no-permissions"); this.mode_container_unset = this.container.find(".container-mode-unset"); this.set_mode(PermissionEditorMode.UNSET); /* the filter */ { const tag_filter_input = this.container.find(".filter-input"); const tag_filter_granted = this.container.find(".filter-granted"); tag_filter_granted.on('change', event => tag_filter_input.trigger('change')); tag_filter_input.on('keyup change', event => { let filter_mask = tag_filter_input.val() as string; let req_granted = tag_filter_granted.prop("checked"); for(const entry of this.entry_editor.permission_entries()) { const permission = entry.permission(); let shown = filter_mask.length == 0 || permission.name.indexOf(filter_mask) != -1; if(shown && req_granted) { const value: PermissionValue = this.permission_value_map[permission.id]; shown = value && (value.hasValue() || value.hasGrant()); } entry.hidden = !shown; } this.entry_editor.request_draw(true); }); } /* update button */ { this.container.find(".button-update").on('click', this.trigger_update.bind(this)); } /* global context menu listener */ { this.container.on('contextmenu', event => { if(event.isDefaultPrevented()) return; event.preventDefault(); /* TODO allow collapse and expend all */ }); } { const tag_container = this.container.find(".entry-editor-container"); tag_container.append(this.entry_editor.canvas_container); tag_container.parent().on('contextmenu', event => { if(event.isDefaultPrevented()) return; event.preventDefault(); contextmenu.spawn_context_menu(event.pageX, event.pageY, { type: contextmenu.MenuEntryType.ENTRY, name: tr("Expend all"), callback: () => this.entry_editor.expend_all() }, { type: contextmenu.MenuEntryType.ENTRY, name: tr("Collapse all"), callback: () => this.entry_editor.collapse_all() }); }); } /* setup the permissions */ for(const entry of this.entry_editor.permission_entries()) { const permission = entry.permission(); entry.on_change = () => { const flag_remove = typeof(entry.value) !== "number"; this.listener_change(permission, { remove: flag_remove, flag_negate: entry.flag_negate, flag_skip: entry.flag_skip, value: flag_remove ? -2 : entry.value }).then(() => { if(flag_remove) { const element = this.permission_value_map[permission.id]; if(!element) return; /* This should never happen, if so how are we displaying this permission?! */ element.value = undefined; element.flag_negate = false; element.flag_skip = false; } else { const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); element.value = entry.value; element.flag_skip = entry.flag_skip; element.flag_negate = entry.flag_negate; } if(permission.name === "i_icon_id") { this.icon_resolver(entry.value).then(e => { entry.set_icon_id_image(e); entry.request_full_draw(); this.entry_editor.request_draw(false); }).catch(error => { console.warn(tr("Failed to load icon for permission editor: %o"), error); }); } entry.request_full_draw(); this.entry_editor.request_draw(false); }).catch(() => { const element = this.permission_value_map[permission.id]; entry.value = element && element.hasValue() ? element.value : undefined; entry.flag_skip = element && element.flag_skip; entry.flag_negate = element && element.flag_negate; entry.request_full_draw(); this.entry_editor.request_draw(false); }); }; entry.on_grant_change = () => { const flag_remove = typeof(entry.granted) !== "number"; this.listener_change(permission, { remove: flag_remove, granted: flag_remove ? -2 : entry.granted, }).then(() => { if(flag_remove) { const element = this.permission_value_map[permission.id]; if (!element) return; /* This should never happen, if so how are we displaying this permission?! */ element.granted_value = undefined; } else { const element = this.permission_value_map[permission.id] || (this.permission_value_map[permission.id] = new PermissionValue(permission)); element.granted_value = entry.granted; } entry.request_full_draw(); this.entry_editor.request_draw(false); }).catch(() => { const element = this.permission_value_map[permission.id]; entry.granted = element && element.hasGrant() ? element.granted_value : undefined; entry.request_full_draw(); this.entry_editor.request_draw(false); }); }; entry.on_context_menu = (x, y) => { let entries: contextmenu.MenuEntry[] = []; if(typeof(entry.value) === "undefined") { entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Add permission"), callback: () => entry.trigger_value_assign() }); } else { entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Remove permission"), callback: () => { entry.value = undefined; entry.on_change(); } }); } if(typeof(entry.granted) === "undefined") { entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Add grant permission"), callback: () => entry.trigger_grant_assign() }); } else { entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Remove grant permission"), callback: () => { entry.granted = undefined; entry.on_grant_change(); } }); } entries.push(contextmenu.Entry.HR()); entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Expend all"), callback: () => this.entry_editor.expend_all() }); entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Collapse all"), callback: () => this.entry_editor.collapse_all() }); entries.push(contextmenu.Entry.HR()); entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Show permission description"), callback: () => { createInfoModal( tr("Permission description"), tr("Permission description for permission ") + permission.name + ":
" + permission.description ).open(); } }); entries.push({ type: contextmenu.MenuEntryType.ENTRY, name: tr("Copy permission name"), callback: () => { copy_to_clipboard(permission.name); } }); contextmenu.spawn_context_menu(x, y, ...entries); } } } set_permissions(permissions?: PermissionValue[]) { permissions = permissions || []; this.permission_value_map = {}; for(const permission of permissions) this.permission_value_map[permission.type.id] = permission; for(const entry of this.entry_editor.permission_entries()) { const permission = entry.permission(); const value: PermissionValue = this.permission_value_map[permission.id]; if(value && value.hasValue()) { entry.value = value.value; entry.flag_skip = value.flag_skip; entry.flag_negate = value.flag_negate; if(permission.name === "i_icon_id") { this.icon_resolver(value.value).then(e => { entry.set_icon_id_image(e); entry.request_full_draw(); this.entry_editor.request_draw(false); }).catch(error => { console.warn(tr("Failed to load icon for permission editor: %o"), error); }); entry.on_icon_select = this.icon_selector; } } else { entry.value = undefined; entry.flag_skip = false; entry.flag_negate = false; entry.set_icon_id_image(undefined); } if(value && value.hasGrant()) { entry.granted = value.granted_value; } else { entry.granted = undefined; } } this.entry_editor.request_draw(true); } set_listener(listener?: PermissionEditor.change_listener_t) { this.listener_change = listener || (() => Promise.resolve()); } set_listener_update(listener?: () => any) { this.listener_update = listener; } trigger_update() { if(this.listener_update) this.listener_update(); } set_mode(mode: PermissionEditorMode) { this.mode_container_permissions.css('display', mode == PermissionEditorMode.VISIBLE ? 'flex' : 'none'); this.mode_container_error_permission.css('display', mode == PermissionEditorMode.NO_PERMISSION ? 'flex' : 'none'); this.mode_container_unset.css('display', mode == PermissionEditorMode.UNSET ? 'block' : 'none'); if(mode == PermissionEditorMode.VISIBLE) this.entry_editor.draw(true); } update_ui() { this.entry_editor.draw(true); } } export function spawnPermissionEdit(connection: ConnectionHandler) : Modal { const modal = createModal({ header: function() { return tr("Server Permissions"); }, body: function () { let properties: any = {}; let tag = $("#tmpl_server_permissions").renderTag(properties); const pe = new PermissionEditor(connection.permissions.groupedPermissions()); pe.build_tag(); pe.icon_resolver = id => connection.fileManager.icons.resolve_icon(id).then(async icon => { if(!icon) return undefined; const tag = document.createElement("img"); await new Promise((resolve, reject) => { tag.onerror = reject; tag.onload = resolve; tag.src = icon.url; }); return tag; }); pe.icon_selector = current_icon => new Promise(resolve => { spawnIconSelect(connection, id => resolve(new Int32Array([id])[0]), current_icon); }); /* initialisation */ { const pe_server = tag.find("permission-editor.group-server"); pe_server.append(pe.container); /* fuck off workaround to initialize form listener */ } setTimeout(() => { pe.update_ui(); }, 500); apply_server_groups(connection, pe, tag.find(".tab-group-server")); apply_channel_groups(connection, pe, tag.find(".tab-group-channel")); apply_channel_permission(connection, pe, tag.find(".tab-channel")); apply_client_permission(connection, pe, tag.find(".tab-client")); apply_client_channel_permission(connection, pe, tag.find(".tab-client-channel")); return tag.tabify(false); }, footer: undefined, width: "90%", height: "80%", trigger_tab: false, full_size: true }); const tag = modal.htmlTag; tag.find(".btn_close").on('click', () => { modal.close(); }); return modal; } function build_channel_tree(connection: ConnectionHandler, channel_list: JQuery, select_callback: (channel: ChannelEntry, icon_update: (id: number) => any) => any) { const root = connection.channelTree.get_first_channel(); if(!root) return; const build_channel = (channel: ChannelEntry) => { let tag = $.spawn("div").addClass("channel").attr("channel-id", channel.channelId); let icon_tag = connection.fileManager.icons.generateTag(channel.properties.channel_icon_id); icon_tag.appendTo(tag); const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); { let name = $.spawn("a").text(channel.channelName() + " (" + channel.channelId + ")").addClass("name"); //if(connection.channelTree.server.properties. == group.id) // name.addClass("default"); name.appendTo(tag); } tag.on('click', event => { channel_list.find(".selected").removeClass("selected"); tag.addClass("selected"); select_callback(channel, _update_icon); }); return tag; }; const build_channels = (root: ChannelEntry) => { build_channel(root).appendTo(channel_list); for(const child of root.children()) build_channels(child); while(root.channel_next) { root = root.channel_next; build_channel(root).appendTo(channel_list); } }; build_channels(root); setTimeout(() => channel_list.find('.channel').first().trigger('click'), 0); } function apply_client_channel_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) { let current_cldbid: number = 0; let current_channel: ChannelEntry; /* the editor */ { const pe_client = tab_tag.find("permission-editor.client-channel"); tab_tag.on('show', event => { console.error("Channel tab show"); pe_client.append(editor.container); if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { if(current_cldbid && current_channel) editor.set_mode(PermissionEditorMode.VISIBLE); else editor.set_mode(PermissionEditorMode.UNSET); } else { editor.set_mode(PermissionEditorMode.NO_PERMISSION); return; } editor.set_listener_update(() => { if(!current_cldbid || !current_channel) return; connection.permissions.requestClientChannelPermissions(current_cldbid, current_channel.channelId).then(result => { editor.set_permissions(result); editor.set_mode(PermissionEditorMode.VISIBLE); }).catch(error => { console.log(error); //TODO handling? }); }); editor.set_listener((permission, value) => { if (!current_cldbid) return Promise.reject("unset client"); if (!current_channel) return Promise.reject("unset channel"); if (value.remove) { /* remove the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Removing client channel permission %s. permission.id: %o"), permission.name, permission.id, ); return connection.serverConnection.send_command("channelclientdelperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id, }); } else { log.info(LogCategory.PERMISSIONS, tr("Removing client channel grant permission %s. permission.id: %o"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channelclientdelperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id_grant(), }); } } else { /* add the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate ); return connection.serverConnection.send_command("channelclientaddperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id, permvalue: value.value, permskip: value.flag_skip, permnegate: value.flag_negate }); } else { log.info(LogCategory.PERMISSIONS, tr("Adding or updating client channel grant permission %s. permission.{id: %o, value: %o}"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channelclientaddperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id_grant(), permvalue: value.granted, permskip: false, permnegate: false }); } } }); /* FIXME: Use cached permissions */ editor.trigger_update(); }); } build_channel_tree(connection, tab_tag.find(".list-channel .entries"), channel => { if(current_channel == channel) return; current_channel = channel; /* TODO: Test for visibility */ editor.trigger_update(); }); { const tag_select_uid = tab_tag.find(".client-select input"); const tag_select_error = tab_tag.find(".client-select .invalid-feedback"); const tag_client_name = tab_tag.find(".client-name"); const tag_client_uid = tab_tag.find(".client-uid"); const tag_client_dbid = tab_tag.find(".client-dbid"); const resolve_client = () => { let client_uid = tag_select_uid.val() as string; connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { if(!result || result.length == 0) return Promise.reject("invalid data"); tag_select_uid.attr('pattern', null).removeClass('is-invalid'); tag_client_name.val(result[0].client_nickname ); tag_client_uid.val(result[0].client_unique_id); tag_client_dbid.val(result[0].client_database_id); current_cldbid = result[0].client_database_id; editor.trigger_update(); }).catch(error => { console.log(error); if(error instanceof CommandResult) { if(error.id == ErrorID.EMPTY_RESULT) error = "unknown client"; else error = error.extra_message || error.message; } tag_client_name.val(""); tag_client_uid.val(""); tag_client_dbid.val(""); tag_select_error.text(error); tag_select_uid.attr('pattern', '^[a]{1000}$').addClass('is-invalid'); editor.set_mode(PermissionEditorMode.UNSET); }); }; tab_tag.find(".client-select-uid").on('change', event => resolve_client()); } } function apply_client_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) { let current_cldbid: number = 0; /* the editor */ { const pe_client = tab_tag.find("permission-editor.client"); tab_tag.on('show', event => { console.error("Channel tab show"); pe_client.append(editor.container); if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CLIENT_PERMISSION_LIST).granted(1)) { if(current_cldbid) editor.set_mode(PermissionEditorMode.VISIBLE); else editor.set_mode(PermissionEditorMode.UNSET); } else { editor.set_mode(PermissionEditorMode.NO_PERMISSION); return; } editor.set_listener_update(() => { if(!current_cldbid) return; connection.permissions.requestClientPermissions(current_cldbid).then(result => { editor.set_permissions(result); editor.set_mode(PermissionEditorMode.VISIBLE); }).catch(error => { console.log(error); //TODO handling? }); }); editor.set_listener((permission, value) => { if (!current_cldbid) return Promise.reject("unset client"); if (value.remove) { /* remove the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Removing client permission %s. permission.id: %o"), permission.name, permission.id, ); return connection.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id, }); } else { log.info(LogCategory.PERMISSIONS, tr("Removing client grant permission %s. permission.id: %o"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id_grant(), }); } } else { /* add the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Adding or updating client permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate ); return connection.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id, permvalue: value.value, permskip: value.flag_skip, permnegate: value.flag_negate }); } else { log.info(LogCategory.PERMISSIONS, tr("Adding or updating client grant permission %s. permission.{id: %o, value: %o}"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id_grant(), permvalue: value.granted, permskip: false, permnegate: false }); } } }); /* FIXME: Use cached permissions */ editor.trigger_update(); }); } const tag_select_uid = tab_tag.find(".client-select input"); const tag_select_error = tab_tag.find(".client-select .invalid-feedback"); const tag_client_name = tab_tag.find(".client-name"); const tag_client_uid = tab_tag.find(".client-uid"); const tag_client_dbid = tab_tag.find(".client-dbid"); const resolve_client = () => { let client_uid = tag_select_uid.val() as string; connection.serverConnection.command_helper.info_from_uid(client_uid).then(result => { if(!result || result.length == 0) return Promise.reject("invalid data"); tag_select_uid.attr('pattern', null).removeClass('is-invalid'); tag_client_name.val(result[0].client_nickname ); tag_client_uid.val(result[0].client_unique_id); tag_client_dbid.val(result[0].client_database_id); current_cldbid = result[0].client_database_id; editor.trigger_update(); }).catch(error => { console.log(error); if(error instanceof CommandResult) { if(error.id == ErrorID.EMPTY_RESULT) error = "unknown client"; else error = error.extra_message || error.message; } tag_client_name.val(""); tag_client_uid.val(""); tag_client_dbid.val(""); tag_select_error.text(error); tag_select_uid.attr('pattern', '^[a]{1000}$').addClass('is-invalid'); editor.set_mode(PermissionEditorMode.UNSET); }); }; tab_tag.find(".client-select-uid").on('change', event => resolve_client()); } function apply_channel_permission(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) { let current_channel: ChannelEntry | undefined; let update_channel_icon: (id: number) => any; /* the editor */ { const pe_channel = tab_tag.find("permission-editor.channel"); tab_tag.on('show', event => { console.error("Channel tab show"); pe_channel.append(editor.container); if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNEL_PERMISSION_LIST).granted(1)) editor.set_mode(PermissionEditorMode.VISIBLE); else { editor.set_mode(PermissionEditorMode.NO_PERMISSION); return; } editor.set_listener_update(() => { if(!current_channel) return; connection.permissions.requestChannelPermissions(current_channel.channelId).then(result => editor.set_permissions(result)).catch(error => { console.log(error); //TODO handling? }); }); editor.set_listener((permission, value) => { if (!current_channel) return Promise.reject("unset channel"); if (value.remove) { /* remove the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Removing channel permission %s. permission.id: %o"), permission.name, permission.id, ); return connection.serverConnection.send_command("channeldelperm", { cid: current_channel.channelId, permid: permission.id, }).then(e => { if(permission.name === "i_icon_id" && update_channel_icon) update_channel_icon(undefined); return e; }); } else { /* TODO Remove this because its totally useless. Remove this from the UI as well */ log.info(LogCategory.PERMISSIONS, tr("Removing channel grant permission %s. permission.id: %o"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channeldelperm", { cid: current_channel.channelId, permid: permission.id_grant(), }); } } else { /* add the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate ); return connection.serverConnection.send_command("channeladdperm", { cid: current_channel.channelId, permid: permission.id, permvalue: value.value, permskip: value.flag_skip, permnegate: value.flag_negate }).then(e => { if(permission.name === "i_icon_id" && update_channel_icon) update_channel_icon(value.value); return e; }); } else { /* TODO Remove this because its totally useless. Remove this from the UI as well */ log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel grant permission %s. permission.{id: %o, value: %o}"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channeladdperm", { cid: current_channel.channelId, permid: permission.id_grant(), permvalue: value.granted, permskip: false, permnegate: false }); } } }); /* FIXME: Use cached permissions */ editor.trigger_update(); }); } let channel_list = tab_tag.find(".list-channel .entries"); build_channel_tree(connection, channel_list, (channel, update) => { current_channel = channel; update_channel_icon = update; editor.trigger_update(); }); } function apply_channel_groups(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) { let current_group; let update_group_icon: (id: number) => any; /* the editor */ { const pe_server = tab_tag.find("permission-editor.group-channel"); tab_tag.on('show', event => { console.error("Channel group tab show"); pe_server.append(editor.container); if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_CHANNELGROUP_PERMISSION_LIST).granted(1)) editor.set_mode(PermissionEditorMode.VISIBLE); else { editor.set_mode(PermissionEditorMode.NO_PERMISSION); return; } editor.set_listener_update(() => { if(!current_group) return; connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { console.log(error); //TODO handling? }); }); editor.set_listener((permission, value) => { if (!current_group) return Promise.reject("unset channel group"); if (value.remove) { /* remove the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Removing channel group permission %s. permission.id: %o"), permission.name, permission.id, ); return connection.serverConnection.send_command("channelgroupdelperm", { cgid: current_group.id, permid: permission.id, }).then(e => { if(permission.name === "i_icon_id" && update_group_icon) update_group_icon(undefined); return e; }); } else { log.info(LogCategory.PERMISSIONS, tr("Removing channel group grant permission %s. permission.id: %o"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channelgroupdelperm", { cgid: current_group.id, permid: permission.id_grant(), }); } } else { /* add the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate ); return connection.serverConnection.send_command("channelgroupaddperm", { cgid: current_group.id, permid: permission.id, permvalue: value.value, permskip: value.flag_skip, permnegate: value.flag_negate }).then(e => { if(permission.name === "i_icon_id" && update_group_icon) update_group_icon(value.value); return e; }); } else { log.info(LogCategory.PERMISSIONS, tr("Adding or updating channel group grant permission %s. permission.{id: %o, value: %o}"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("channelgroupaddperm", { cgid: current_group.id, permid: permission.id_grant(), permvalue: value.granted, permskip: false, permnegate: false }); } } }); /* FIXME: Use cached permissions */ editor.trigger_update(); }); } /* list all channel groups */ { let group_list = tab_tag.find(".list-group-channel .entries"); const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); for(let group of connection.groups.channelGroups.sort(GroupManager.sorter())) { if(group.type == GroupType.QUERY) { if(!allow_query_groups) continue; } else if(group.type == GroupType.TEMPLATE) { if(!allow_template_groups) continue; } let tag = $.spawn("div").addClass("group").attr("group-id", group.id); let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); icon_tag.appendTo(tag); const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); { let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name"); if(group.properties.savedb) name.addClass("savedb"); if(connection.channelTree.server.properties.virtualserver_default_channel_group == group.id) name.addClass("default"); name.appendTo(tag); } tag.appendTo(group_list); tag.on('click', event => { current_group = group; update_group_icon = _update_icon; group_list.find(".selected").removeClass("selected"); tag.addClass("selected"); //TODO trigger only if the editor is in channel group mode! editor.trigger_update(); }); } /* because the server menu is the first which will be shown */ setTimeout(() => group_list.find('.group').first().trigger('click'), 0); } } /* b_virtualserver_servergroup_permission_list b_virtualserver_channel_permission_list b_virtualserver_client_permission_list b_virtualserver_channelgroup_permission_list b_virtualserver_channelclient_permission_list b_virtualserver_playlist_permission_list */ function apply_server_groups(connection: ConnectionHandler, editor: PermissionEditor, tab_tag: JQuery) { let current_group: Group; let current_group_changed; /* list all groups */ let update_icon: (icon_id: number) => any; { let group_list = tab_tag.find(".list-group-server .entries"); const allow_query_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_QUERYGROUP).granted(1); const allow_template_groups = connection.permissions.neededPermission(PermissionType.B_SERVERINSTANCE_MODIFY_TEMPLATES).granted(1); for(const group of connection.groups.serverGroups.sort(GroupManager.sorter())) { if(group.type == GroupType.QUERY) { if(!allow_query_groups) continue; } else if(group.type == GroupType.TEMPLATE) { if(!allow_template_groups) continue; } let tag = $.spawn("div").addClass("group").attr("group-id", group.id); let icon_tag = connection.fileManager.icons.generateTag(group.properties.iconid); icon_tag.appendTo(tag); const _update_icon = icon_id => icon_tag.replaceWith(icon_tag = connection.fileManager.icons.generateTag(icon_id)); { let name = $.spawn("a").text(group.name + " (" + group.id + ")").addClass("name"); if(group.properties.savedb) name.addClass("savedb"); if(connection.channelTree.server.properties.virtualserver_default_server_group == group.id) name.addClass("default"); name.appendTo(tag); } tag.appendTo(group_list); tag.on('click', event => { if(current_group === group) return; current_group = group; update_icon = _update_icon; current_group_changed(); group_list.find(".selected").removeClass("selected"); tag.addClass("selected"); editor.trigger_update(); }); } /* because the server menu is the first which will be shown */ setTimeout(() => group_list.find('.group').first().trigger('click'), 0); } /* the editor */ { const pe_server = tab_tag.find("permission-editor.group-server"); tab_tag.on('show', event => { console.error("Server tab show"); pe_server.append(editor.container); if(connection.permissions.neededPermission(PermissionType.B_VIRTUALSERVER_SERVERGROUP_PERMISSION_LIST).granted(1)) editor.set_mode(PermissionEditorMode.VISIBLE); else { editor.set_mode(PermissionEditorMode.NO_PERMISSION); return; } editor.set_listener_update(() => { connection.groups.request_permissions(current_group).then(result => editor.set_permissions(result)).catch(error => { console.log(error); //TODO handling? }); }); editor.set_listener((permission, value) => { if (!current_group) return Promise.reject("unset server group"); if (value.remove) { /* remove the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Removing server group permission %s. permission.id: %o"), permission.name, permission.id, ); return connection.serverConnection.send_command("servergroupdelperm", { sgid: current_group.id, permid: permission.id, }).then(e => { if(permission.name === "i_icon_id" && update_icon) update_icon(0); return e; }); } else { log.info(LogCategory.PERMISSIONS, tr("Removing server group grant permission %s. permission.id: %o"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("servergroupdelperm", { sgid: current_group.id, permid: permission.id_grant(), }); } } else { /* add the permission */ if (typeof (value.value) !== "undefined") { log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group permission %s. permission.{id: %o, value: %o, flag_skip: %o, flag_negate: %o}"), permission.name, permission.id, value.value, value.flag_skip, value.flag_negate ); return connection.serverConnection.send_command("servergroupaddperm", { sgid: current_group.id, permid: permission.id, permvalue: value.value, permskip: value.flag_skip, permnegate: value.flag_negate }).then(e => { if(permission.name === "i_icon_id" && update_icon) update_icon(value.value); return e; }); } else { log.info(LogCategory.PERMISSIONS, tr("Adding or updating server group grant permission %s. permission.{id: %o, value: %o}"), permission.name, permission.id_grant(), value.granted, ); return connection.serverConnection.send_command("servergroupaddperm", { sgid: current_group.id, permid: permission.id_grant(), permvalue: value.granted, permskip: false, permnegate: false }); } } }); editor.trigger_update(); }); } /* client list */ { //filter-client-list const container_clients = tab_tag.find(".container-clients"); const container_client_list = container_clients.find(".list-clients"); const input_filter = container_clients.find(".filter-client-list"); const update_filter = () => { const filter_text = (input_filter.val() || "").toString().toLowerCase(); if(!filter_text) { container_client_list.find(".entry").css('display', 'block'); } else { const entries = container_client_list.find(".entry"); for(const _entry of entries) { const entry = $(_entry); if(entry.attr("search-string").toLowerCase().indexOf(filter_text) !== -1) entry.css('display', 'block'); else entry.css('display', 'none'); } } }; const update_client_list = () => { container_client_list.empty(); connection.serverConnection.command_helper.request_clients_by_server_group(current_group.id).then(clients => { for(const client of clients) { const tag = $.spawn("div").addClass("entry").text(client.client_nickname); tag.attr("search-string", client.client_nickname + "-" + client.client_unique_identifier + "-" + client.client_database_id); container_client_list.append(tag); tag.on('click contextmenu', event => { container_client_list.find(".selected").removeClass("selected"); tag.addClass("selected"); }); tag.on('contextmenu', event => { if(event.isDefaultPrevented()) return; event.preventDefault(); contextmenu.spawn_context_menu(event.pageX, event.pageY, { type: contextmenu.MenuEntryType.ENTRY, name: tr("Remove client"), icon_class: 'client-delete', callback: () => { connection.serverConnection.send_command("servergroupdelclient", { sgid: current_group.id, cldbid: client.client_database_id }).then(() => { tag.detach(); }); } }, { type: contextmenu.MenuEntryType.ENTRY, name: tr("Copy unique id"), icon_class: 'client-copy', callback: () => copy_to_clipboard(client.client_unique_identifier) }) }); } update_filter(); }).catch(error => { if(error instanceof CommandResult && error.id === ErrorID.PERMISSION_ERROR) return; console.warn(tr("Failed to receive server group clients for group %d: %o"), current_group.id, error); }) }; input_filter.on('change keyup', event => update_filter()); current_group_changed = update_client_list; } } }