2021-02-09 09:11:40 +00:00
import { spawnReactModal } from "tc-shared/ui/react-elements/modal" ;
2020-06-15 14:56:05 +00:00
import { ConnectionHandler } from "tc-shared/ConnectionHandler" ;
import { Registry } from "tc-shared/events" ;
2021-01-10 16:36:57 +00:00
import * as React from "react" ;
2020-06-15 14:56:05 +00:00
import { useRef , useState } from "react" ;
2020-11-07 12:16:07 +00:00
import { Select } from "tc-shared/ui/react-elements/InputField" ;
2020-06-15 14:56:05 +00:00
import { Translatable } from "tc-shared/ui/react-elements/i18n" ;
import { Button } from "tc-shared/ui/react-elements/Button" ;
import { GroupType } from "tc-shared/permission/GroupManager" ;
import PermissionType from "tc-shared/permission/PermissionType" ;
2020-08-22 20:23:19 +00:00
import { CommandResult } from "tc-shared/connection/ServerConnectionDeclaration" ;
2020-06-15 14:56:05 +00:00
import { createErrorModal , createInfoModal } from "tc-shared/ui/elements/Modal" ;
import { tra } from "tc-shared/i18n/localize" ;
2020-08-22 20:23:19 +00:00
import { ErrorCode } from "tc-shared/connection/ErrorCode" ;
2021-01-10 16:36:57 +00:00
import { LogCategory , logWarn } from "tc-shared/log" ;
2021-03-16 14:14:03 +00:00
import { InternalModal } from "tc-shared/ui/react-elements/modal/Definitions" ;
2020-06-15 14:56:05 +00:00
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 : { }
}
2020-08-01 12:54:44 +00:00
const GroupSelector = ( props : { events : Registry < GroupPermissionCopyModalEvents > , defaultGroup : number , updateEvent : "action_set_source" | "action_set_target" , label : React.ReactElement , className : string } ) = > {
2020-06-15 14:56:05 +00:00
const [ selectedGroup , setSelectedGroup ] = useState ( undefined ) ;
const [ permissions , setPermissions ] = useState < "loading" | { createTemplate , createQuery } > ( "loading" ) ;
const [ exitingGroups , setExitingGroups ] = useState < "loading" | GroupInfo [ ] > ( "loading" ) ;
2020-11-07 12:16:07 +00:00
const refSelect = useRef < Select > ( ) ;
2020-06-15 14:56:05 +00:00
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 )
2020-11-07 12:16:07 +00:00
props . events . fire_react ( props . updateEvent , {
2020-06-15 14:56:05 +00:00
group : ( exitingGroups as GroupInfo [ ] ) . findIndex ( e = > e . id === props . defaultGroup ) === - 1 ? 0 : props.defaultGroup
} ) ;
return (
2020-11-07 12:16:07 +00:00
< Select
2020-06-15 14:56:05 +00:00
ref = { refSelect }
2020-08-01 12:54:44 +00:00
label = { props . label }
2020-06-15 14:56:05 +00:00
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 >
2020-11-07 12:16:07 +00:00
< / Select >
2020-06-15 14:56:05 +00:00
)
} ;
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 >
} ;
2020-08-07 11:40:11 +00:00
class ModalGroupPermissionCopy extends InternalModal {
readonly events : Registry < GroupPermissionCopyModalEvents > ;
2020-06-15 14:56:05 +00:00
readonly defaultSource : number ;
readonly defaultTarget : number ;
2020-08-07 11:40:11 +00:00
constructor ( connection : ConnectionHandler , events : Registry < GroupPermissionCopyModalEvents > , target : "server" | "channel" , sourceGroup? : number , targetGroup? : number ) {
2020-06-15 14:56:05 +00:00
super ( ) ;
2020-08-07 11:40:11 +00:00
this . events = events ;
2020-06-15 14:56:05 +00:00
this . defaultSource = sourceGroup ;
this . defaultTarget = targetGroup ;
initializeGroupPermissionCopyController ( connection , this . events , target ) ;
}
protected onInitialize() {
2020-11-07 12:16:07 +00:00
this . events . fire_react ( "query_available_groups" ) ;
this . events . fire_react ( "query_client_permissions" ) ;
2020-08-07 11:40:11 +00:00
}
2020-06-15 14:56:05 +00:00
2020-08-07 11:40:11 +00:00
protected onDestroy() {
this . events . fire ( "notify_destroy" ) ;
this . events . destroy ( ) ;
2020-06-15 14:56:05 +00:00
}
renderBody() {
return < div className = { cssStyle . container } >
< div className = { cssStyle . row } >
2020-08-01 12:54:44 +00:00
< GroupSelector events = { this . events } defaultGroup = { this . defaultSource } updateEvent = { "action_set_source" } label = { < Translatable > Source group < / Translatable > } className = { cssStyle . sourceGroup } / >
< GroupSelector events = { this . events } defaultGroup = { this . defaultTarget } updateEvent = { "action_set_target" } label = { < Translatable > Target group < / Translatable > } className = { cssStyle . targetGroup } / >
2020-06-15 14:56:05 +00:00
< / div >
< div className = { cssStyle . buttons } >
< Button color = { "red" } onClick = { ( ) = > this . events . fire ( "action_cancel" ) } > < Translatable > Cancel < / Translatable > < / Button >
< CopyButton events = { this . events } / >
< / div >
< / div > ;
}
2021-01-17 22:11:21 +00:00
renderTitle() {
2020-07-17 21:56:20 +00:00
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 ) {
2020-08-07 11:40:11 +00:00
const events = new Registry < GroupPermissionCopyModalEvents > ( ) ;
const modal = spawnReactModal ( ModalGroupPermissionCopy , connection , events , target , sourceGroup , targetGroup ) ;
2020-06-15 14:56:05 +00:00
modal . show ( ) ;
2020-08-07 11:40:11 +00:00
events . on ( [ "action_cancel" , "action_copy" ] , ( ) = > modal . destroy ( ) ) ;
2020-06-15 14:56:05 +00:00
}
const stringifyError = error = > {
if ( error instanceof CommandResult ) {
2020-08-22 20:23:19 +00:00
if ( error . id === ErrorCode . SERVER_INSUFFICIENT_PERMISSIONS )
2020-06-15 14:56:05 +00:00
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 ;
2020-11-07 12:16:07 +00:00
events . fire_react ( "query_available_groups_result" , {
2020-06-15 14:56:05 +00:00
groups : groups.map ( e = > {
return {
name : e.name ,
id : e.id ,
type : e . type === GroupType . TEMPLATE ? "template" : e . type === GroupType . QUERY ? "query" : "normal"
}
} )
} ) ;
} ) ;
2020-11-07 12:16:07 +00:00
const notifyClientPermissions = ( ) = > events . fire_react ( "notify_client_permissions" , {
2020-06-15 14:56:05 +00:00
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 = > {
2020-08-22 20:23:19 +00:00
if ( error instanceof CommandResult && error . id === ErrorCode . SERVER_INSUFFICIENT_PERMISSIONS ) {
2020-06-15 14:56:05 +00:00
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 ;
}
2021-01-10 16:36:57 +00:00
logWarn ( LogCategory . PERMISSIONS , tr ( "Failed to copy group permissions: %o" ) , error ) ;
2020-06-15 14:56:05 +00:00
createErrorModal ( tr ( "Failed to copy group permissions" ) ,
tra ( "Failed to copy group permissions.\n{}" , stringifyError ( error ) ) ) . open ( ) ;
} ) ;
} ) ;
}