2020-06-15 14:56:05 +00:00
import { Modal , spawnReactModal } from "tc-shared/ui/react-elements/Modal" ;
import { ConnectionHandler } from "tc-shared/ConnectionHandler" ;
import { Registry } from "tc-shared/events" ;
import { useRef , useState } from "react" ;
import { FlatSelect } from "tc-shared/ui/react-elements/InputField" ;
import { Translatable } from "tc-shared/ui/react-elements/i18n" ;
import * as React from "react" ;
import { Button } from "tc-shared/ui/react-elements/Button" ;
import { GroupType } from "tc-shared/permission/GroupManager" ;
import PermissionType from "tc-shared/permission/PermissionType" ;
import { CommandResult , ErrorID } from "tc-shared/connection/ServerConnectionDeclaration" ;
import { createErrorModal , createInfoModal } from "tc-shared/ui/elements/Modal" ;
import { tra } from "tc-shared/i18n/localize" ;
const cssStyle = require ( "./ModalGroupPermissionCopy.scss" ) ;
export type GroupInfo = {
id : number ,
name : string ,
type : "query" | "template" | "normal"
} ;
export interface GroupPermissionCopyModalEvents {
action_set_source : { group : number } ,
action_set_target : { group : number }
action_cancel : { } ,
action_copy : {
source : number ;
target : number ;
}
query_available_groups : { } ,
query_available_groups_result : {
groups : GroupInfo [ ]
} ,
query_client_permissions : { } ,
notify_client_permissions : {
createTemplateGroup : boolean ,
createQueryGroup : boolean
} ,
notify_destroy : { }
}
const GroupSelector = ( props : { events : Registry < GroupPermissionCopyModalEvents > , defaultGroup : number , updateEvent : "action_set_source" | "action_set_target" , label : string , className : string } ) = > {
const [ selectedGroup , setSelectedGroup ] = useState ( undefined ) ;
const [ permissions , setPermissions ] = useState < "loading" | { createTemplate , createQuery } > ( "loading" ) ;
const [ exitingGroups , setExitingGroups ] = useState < "loading" | GroupInfo [ ] > ( "loading" ) ;
const refSelect = useRef < FlatSelect > ( ) ;
props . events . reactUse ( "notify_client_permissions" , event = > setPermissions ( {
createQuery : event.createQueryGroup ,
createTemplate : event.createTemplateGroup
} ) ) ;
props . events . reactUse ( "query_available_groups_result" , event = > setExitingGroups ( event . groups ) ) ;
props . events . reactUse ( props . updateEvent , event = > setSelectedGroup ( event . group ) ) ;
const groupName = ( group : GroupInfo ) = > {
let prefix = group . type === "template" ? "[T] " : group . type === "query" ? "[Q] " : "" ;
return prefix + group . name + " (" + group . id + ")" ;
} ;
const isLoading = exitingGroups === "loading" || permissions === "loading" ;
if ( ! isLoading && selectedGroup === undefined )
props . events . fire_async ( props . updateEvent , {
group : ( exitingGroups as GroupInfo [ ] ) . findIndex ( e = > e . id === props . defaultGroup ) === - 1 ? 0 : props.defaultGroup
} ) ;
return (
< FlatSelect
ref = { refSelect }
label = { < Translatable > { props . label } < / Translatable > }
className = { props . className }
disabled = { isLoading }
value = { isLoading || selectedGroup === undefined ? "-1" : selectedGroup . toString ( ) }
onChange = { event = > props . events . fire ( props . updateEvent , { group : parseInt ( event . target . value ) } ) }
>
< option className = { cssStyle . hiddenOption } value = { "-1" } > { tr ( "loading..." ) } < / option >
< option className = { cssStyle . hiddenOption } value = { "0" } > { tr ( "Select a group" ) } < / option >
< optgroup label = { tr ( "Query groups" ) } className = { permissions === "loading" || ! permissions . createQuery ? cssStyle . hiddenOption : "" } >
{ exitingGroups === "loading" ? undefined :
exitingGroups . filter ( e = > e . type === "query" ) . map ( e = > (
< option key = { "group-" + e . id } value = { e . id . toString ( ) } > { groupName ( e ) } < / option >
) )
}
< / optgroup >
< optgroup label = { tr ( "Template groups" ) } className = { permissions === "loading" || ! permissions . createTemplate ? cssStyle . hiddenOption : "" } >
{ exitingGroups === "loading" ? undefined :
exitingGroups . filter ( e = > e . type === "template" ) . map ( e = > (
< option key = { "group-" + e . id } value = { e . id . toString ( ) } > { groupName ( e ) } < / option >
) )
}
< / optgroup >
< optgroup label = { tr ( "Regular Groups" ) } >
{ exitingGroups === "loading" ? undefined :
exitingGroups . filter ( e = > e . type === "normal" ) . map ( e = > (
< option key = { "group-" + e . id } value = { e . id . toString ( ) } > { groupName ( e ) } < / option >
) )
}
< / optgroup >
< / FlatSelect >
)
} ;
const CopyButton = ( props : { events : Registry < GroupPermissionCopyModalEvents > } ) = > {
const [ sourceGroup , setSourceGroup ] = useState < number > ( 0 ) ;
const [ targetGroup , setTargetGroup ] = useState < number > ( 0 ) ;
props . events . reactUse ( "action_set_source" , event = > setSourceGroup ( event . group ) ) ;
props . events . reactUse ( "action_set_target" , event = > setTargetGroup ( event . group ) ) ;
return < Button color = { "green" } disabled = { sourceGroup === 0 || targetGroup === 0 || targetGroup === sourceGroup } onClick = { ( ) = > {
props . events . fire ( "action_copy" , { source : sourceGroup , target : targetGroup } ) ;
} } >
< Translatable > Copy group permissions < / Translatable >
< / Button >
} ;
class ModalGroupPermissionCopy extends Modal {
readonly events = new Registry < GroupPermissionCopyModalEvents > ( ) ;
readonly defaultSource : number ;
readonly defaultTarget : number ;
constructor ( connection : ConnectionHandler , target : "server" | "channel" , sourceGroup? : number , targetGroup? : number ) {
super ( ) ;
this . defaultSource = sourceGroup ;
this . defaultTarget = targetGroup ;
initializeGroupPermissionCopyController ( connection , this . events , target ) ;
}
protected onInitialize() {
this . modalController ( ) . events . on ( "destroy" , ( ) = > this . events . fire ( "notify_destroy" ) ) ;
this . events . fire_async ( "query_available_groups" ) ;
this . events . fire_async ( "query_client_permissions" ) ;
this . events . on ( [ "action_cancel" , "action_copy" ] , ( ) = > this . modalController ( ) . destroy ( ) ) ;
}
renderBody() {
return < div className = { cssStyle . container } >
< div className = { cssStyle . row } >
< GroupSelector events = { this . events } defaultGroup = { this . defaultSource } updateEvent = { "action_set_source" } label = { "Source group" } className = { cssStyle . sourceGroup } / >
< GroupSelector events = { this . events } defaultGroup = { this . defaultTarget } updateEvent = { "action_set_target" } label = { "Target group" } className = { cssStyle . targetGroup } / >
< / div >
< div className = { cssStyle . buttons } >
< Button color = { "red" } onClick = { ( ) = > this . events . fire ( "action_cancel" ) } > < Translatable > Cancel < / Translatable > < / Button >
< CopyButton events = { this . events } / >
< / div >
< / div > ;
}
2020-07-17 21:56:20 +00:00
title() {
return < Translatable > Copy group permissions < / Translatable > ;
2020-06-15 14:56:05 +00:00
}
}
export function spawnModalGroupPermissionCopy ( connection : ConnectionHandler , target : "channel" | "server" , sourceGroup? : number , targetGroup? : number ) {
const modal = spawnReactModal ( ModalGroupPermissionCopy , connection , target , sourceGroup , targetGroup ) ;
modal . show ( ) ;
}
const stringifyError = error = > {
if ( error instanceof CommandResult ) {
if ( error . id === ErrorID . PERMISSION_ERROR )
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 initializeGroupPermissionCopyController ( connection : ConnectionHandler , events : Registry < GroupPermissionCopyModalEvents > , target : "server" | "channel" ) {
events . on ( "query_available_groups" , event = > {
const groups = target === "server" ? connection.groups.serverGroups : connection.groups.channelGroups ;
events . fire_async ( "query_available_groups_result" , {
groups : groups.map ( e = > {
return {
name : e.name ,
id : e.id ,
type : e . type === GroupType . TEMPLATE ? "template" : e . type === GroupType . QUERY ? "query" : "normal"
}
} )
} ) ;
} ) ;
const notifyClientPermissions = ( ) = > events . fire_async ( "notify_client_permissions" , {
createQueryGroup : connection.permissions.neededPermission ( PermissionType . B_SERVERINSTANCE_MODIFY_QUERYGROUP ) . granted ( 1 ) ,
createTemplateGroup : connection.permissions.neededPermission ( PermissionType . B_SERVERINSTANCE_MODIFY_TEMPLATES ) . granted ( 1 )
} ) ;
events . on ( "query_client_permissions" , notifyClientPermissions ) ;
events . on ( "notify_destroy" , connection . permissions . events . on ( "client_permissions_changed" , notifyClientPermissions ) ) ;
events . on ( "action_copy" , event = > {
connection . serverConnection . send_command ( "servergroupcopy" , {
ssgid : event.source ,
tsgid : event.target
} ) . then ( ( ) = > {
createInfoModal ( tr ( "Group permissions have been copied" ) , tr ( "The group permissions have been successfully copied." ) ) . open ( ) ;
} ) . catch ( error = > {
if ( error instanceof CommandResult && error . id === ErrorID . PERMISSION_ERROR ) {
createErrorModal ( tr ( "Failed to copy group permissions" ) ,
tra ( "Failed to copy group permissions.\nMissing permission {}" , connection . permissions . resolveInfo ( parseInt ( error . json [ "failed_permid" ] ) ) ? . name || tr ( "unknwon" ) ) ) . open ( ) ;
return ;
}
console . warn ( tr ( "Failed to copy group permissions: %o" ) , error ) ;
createErrorModal ( tr ( "Failed to copy group permissions" ) ,
tra ( "Failed to copy group permissions.\n{}" , stringifyError ( error ) ) ) . open ( ) ;
} ) ;
} ) ;
}