# -- # Copyright (C) 2001-2019 OTRS AG, https://otrs.com/ # -- # This software comes with ABSOLUTELY NO WARRANTY. For details, see # the enclosed file COPYING for license information (GPL). If you # did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. # -- package Kernel::Modules::AdminITSMStateMachine; use strict; use warnings; use Kernel::Language qw(Translatable); our $ObjectManagerDisabled = 1; sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless( $Self, $Type ); return $Self; } sub Run { my ( $Self, %Param ) = @_; my %Error; # get param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # store commonly needed parameters in %GetParam my %GetParam; for my $ParamName (qw(StateID NextStateID Class Status)) { $GetParam{$ParamName} = $ParamObject->GetParam( Param => $ParamName ); } # read the config, my $Config = $Kernel::OM->Get('Kernel::Config')->Get("ITSMStateMachine::Object") || {}; # prepare the config for lookup by class # the hash keys of $Config are not significant $Self->{ConfigByClass} = {}; for my $HashRef ( values %{$Config} ) { $Self->{ConfigByClass}->{ $HashRef->{Class} } = $HashRef; } # translate from name to class and vice versa if ( $GetParam{Class} ) { $GetParam{ClassShortName} = $Self->{ConfigByClass}->{ $GetParam{Class} }->{Name}; } # error messages are added to this variable my $Note = ''; if ( $Self->{Subaction} eq 'StateTransitionUpdate' ) { # provide form for changing the next state return $Self->_StateTransitionUpdatePageGet( Action => 'StateTransitionUpdate', %GetParam, ); } elsif ( $Self->{Subaction} eq 'StateTransitionAdd' ) { if ( !$GetParam{Class} ) { $Error{'ClassInvalid'} = 'ServerError'; } else { # provide form for adding a state transition return $Self->_StateTransitionAddPageGet( Action => 'StateTransitionAdd', %GetParam, ); } } elsif ( $Self->{Subaction} eq 'StateTransitionDelete' ) { # show confirmation page for state deletion return $Self->_StateTransitionDeletePageGet( Action => 'StateTransitionDelete', %GetParam, ); } # Build drop-down for the class selection on the left side. my @ArrHashRef; for my $Class ( sort keys %{ $Self->{ConfigByClass} } ) { push @ArrHashRef, { Key => $Class, Value => $Class, }; } # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $GetParam{ClassSelectionString} = $LayoutObject->BuildSelection( Name => 'Class', Data => \@ArrHashRef, SelectedID => $GetParam{Class}, Class => 'Modernize Validate_Required W100pc ' . ( $Error{ClassInvalid} || '' ), PossibleNone => 1, Translation => 0, ); # perform actions my $ActionIsOk = 1; # get state machine object my $StateMachineObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine'); # update the next state of a state transition if ( $Self->{Subaction} eq 'StateTransitionUpdateAction' ) { # params specific to StateTransitionUpdate for my $ParamName (qw(NewNextStateID)) { $GetParam{$ParamName} = $ParamObject->GetParam( Param => $ParamName ); } # Update the state transition $ActionIsOk = $StateMachineObject->StateTransitionUpdate( Class => $GetParam{Class}, StateID => $GetParam{StateID}, NextStateID => $GetParam{NextStateID}, NewNextStateID => $GetParam{NewNextStateID}, ); if ($ActionIsOk) { my $ContinueAfterSave = $ParamObject->GetParam( Param => 'ContinueAfterSave' ); if ($ContinueAfterSave) { return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Subaction=StateTransitionUpdate;Class=$GetParam{Class};StateID=$GetParam{StateID};NextStateID=$GetParam{NewNextStateID};Status=Updated" ); } return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Status=Updated" ); } } elsif ( $Self->{Subaction} eq 'StateTransitionAddAction' ) { my $IsValid = 1; %Error = (); # we need to distinguish between empty string '' and the string '0'. # '' indicates that no value was selected, which is invalid # '0' indicated '*START*' or '*END*' if ( $GetParam{StateID} eq '' ) { $IsValid = 0; $Error{'StateInvalid'} = 'ServerError'; } if ( $GetParam{NextStateID} eq '' ) { $IsValid = 0; $Error{'NextStateInvalid'} = 'ServerError'; } if ( !$IsValid ) { return $Self->_StateTransitionAddPageGet( %GetParam, %Error, Action => 'StateTransitionAdd', Note => $Note, ); } # Add the state transition $ActionIsOk = $StateMachineObject->StateTransitionAdd( Class => $GetParam{Class}, StateID => $GetParam{StateID}, NextStateID => $GetParam{NextStateID}, ); if ($ActionIsOk) { my $ContinueAfterSave = $ParamObject->GetParam( Param => 'ContinueAfterSave' ); if ($ContinueAfterSave) { return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Subaction=StateTransitionUpdate;Class=$GetParam{Class};StateID=$GetParam{StateID};NextStateID=$GetParam{NextStateID};Status=Added" ); } return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Status=Added" ); } } elsif ( $Self->{Subaction} eq 'StateTransitionDeleteAction' ) { # Delete the state transition $ActionIsOk = $StateMachineObject->StateTransitionDelete( StateID => $GetParam{StateID}, NextStateID => $GetParam{NextStateID}, ); } # Show list of state transitions for the class if ( $GetParam{Class} ) { $Note .= $ActionIsOk ? '' : $LayoutObject->Notify( Priority => 'Error' ); return $Self->_OverviewStateTransitionsPageGet( %GetParam, Note => $Note, ); } # present a list of all configured classes, as the class is not known yet return $Self->_OverviewClassesPageGet( %GetParam, Note => $Note, ); } # provide a form for changing the next state of a transition sub _StateTransitionUpdatePageGet { my ( $Self, %Param ) = @_; # get state machine object my $StateMachineObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine'); $Param{StateName} = $StateMachineObject->StateLookup( Class => $Param{Class}, StateID => $Param{StateID}, ) || '*START*'; # ArrayHashRef with all states for a catalog class my $AllStatesArrayHashRef = $StateMachineObject->StateList( Class => $Param{Class}, UserID => $Self->{UserID}, ); # Add the special final state push @{$AllStatesArrayHashRef}, { Key => '0', Value => '*END*' }; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $Param{NextStateSelectionString} = $LayoutObject->BuildSelection( Data => $AllStatesArrayHashRef, Name => 'NewNextStateID', SelectedID => $Param{NextStateID}, Class => 'Modernize', ); $LayoutObject->Block( Name => 'Overview', Data => \%Param, ); $LayoutObject->Block( Name => 'ActionOverview', ); $LayoutObject->Block( Name => 'StateTransitionUpdate', Data => \%Param, ); my $Status = $Param{'Status'} || ''; my %Notification; if ( $Status eq 'Updated' ) { %Notification = ( Info => Translatable('State Transition Updated!'), ); } elsif ( $Status eq 'Added' ) { %Notification = ( Info => Translatable('State Transition Added!'), ); } # generate HTML my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); if (%Notification) { $Output .= $LayoutObject->Notify(%Notification) || ''; } $Output .= $Param{Note} || ''; $Output .= $LayoutObject->Output( TemplateFile => 'AdminITSMStateMachine', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # provide a form for adding a new state transition sub _StateTransitionAddPageGet { my ( $Self, %Param ) = @_; # ArrayHashRef with all states for a catalog class my $AllStatesArrayHashRef = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine')->StateList( Class => $Param{Class}, UserID => $Self->{UserID}, ); # Add the special final state push @{$AllStatesArrayHashRef}, { Key => '0', Value => '*START*', }; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # dropdown menu, where the state can be selected for addition $Param{StateSelectionString} = $LayoutObject->BuildSelection( Data => $AllStatesArrayHashRef, Name => 'StateID', SelectedID => $Param{StateID}, PossibleNone => 1, Class => 'Modernize Validate_Required ' . ( $Param{StateInvalid} || '' ), ); # dropdown menu, where the next state can be selected for addition $AllStatesArrayHashRef->[-1] = { Key => '0', Value => '*END*', }; $Param{NextStateSelectionString} = $LayoutObject->BuildSelection( Data => $AllStatesArrayHashRef, Name => 'NextStateID', SelectedID => $Param{NextStateID}, PossibleNone => 1, Class => 'Modernize Validate_Required ' . ( $Param{NextStateInvalid} || '' ), ); $LayoutObject->Block( Name => 'Overview', Data => \%Param, ); $LayoutObject->Block( Name => 'ActionOverview' ); $LayoutObject->Block( Name => 'StateTransitionAdd', Data => \%Param, ); # generate HTML my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $Param{Note} || ''; $Output .= $LayoutObject->Output( TemplateFile => 'AdminITSMStateMachine', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # provide a form for confirming the deletion of a state transition sub _StateTransitionDeletePageGet { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $LayoutObject->Block( Name => 'Overview', Data => \%Param, ); $LayoutObject->Block( Name => 'ActionOverview', ); # get state machine object my $StateMachineObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine'); $Param{StateName} = $StateMachineObject->StateLookup( Class => $Param{Class}, StateID => $Param{StateID}, ) || '*START*'; $Param{NextStateName} = $StateMachineObject->StateLookup( Class => $Param{Class}, StateID => $Param{NextStateID}, ) || '*END*'; $LayoutObject->Block( Name => 'StateTransitionDelete', Data => \%Param, ); # generate HTML my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $Param{Note} || ''; $Output .= $LayoutObject->Output( TemplateFile => 'AdminITSMStateMachine', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # Show a table of all state transitions sub _OverviewStateTransitionsPageGet { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $LayoutObject->Block( Name => 'Overview', Data => \%Param, ); $LayoutObject->Block( Name => 'ActionOverview', ); $LayoutObject->Block( Name => 'OverviewStateTransitions', Data => \%Param, ); # get state machine object my $StateMachineObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMStateMachine'); # lookup for state names my %NextStateIDs = %{ $StateMachineObject->StateTransitionList( Class => $Param{Class} ) || {} }; # loop over all 'State' and 'NextState' pairs for the catalog class for my $StateID ( sort keys %NextStateIDs ) { for my $NextStateID ( @{ $NextStateIDs{$StateID} } ) { # state names my $StateName = $StateMachineObject->StateLookup( Class => $Param{Class}, StateID => $StateID, ) || '*START*'; my $NextStateName = $StateMachineObject->StateLookup( Class => $Param{Class}, StateID => $NextStateID, ) || '*END*'; $LayoutObject->Block( Name => 'StateTransitionRow', Data => { %Param, StateID => $StateID, StateName => $StateName, NextStateID => $NextStateID, NextStateName => $NextStateName, }, ); # only show the delete link if it is not the start state if ( $StateName ne '*START*' ) { $LayoutObject->Block( Name => 'StateTransitionDeleteButton', Data => { %Param, StateID => $StateID, StateName => $StateName, NextStateID => $NextStateID, NextStateName => $NextStateName, }, ); } } } # generate HTML my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); $Output .= $Param{Note} || ''; $Output .= $LayoutObject->Output( TemplateFile => 'AdminITSMStateMachine', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } # Show a list of relevant object types sub _OverviewClassesPageGet { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); $LayoutObject->Block( Name => 'Overview', Data => \%Param, ); $LayoutObject->Block( Name => 'ActionAddState', Data => \%Param, ); $LayoutObject->Block( Name => 'OverviewClasses', Data => \%Param, ); # read the config, my $Config = $Kernel::OM->Get('Kernel::Config')->Get("ITSMStateMachine::Object") || {}; # prepare the config for lookup by class # the hash keys of $Config are not significant $Self->{ConfigByClass} = {}; for my $HashRef ( values %{$Config} ) { $Self->{ConfigByClass}->{ $HashRef->{Class} } = $HashRef; } for my $Class ( sort keys %{ $Self->{ConfigByClass} } ) { $LayoutObject->Block( Name => 'OverviewClassesRow', Data => { ClassShortName => $Self->{ConfigByClass}->{$Class}->{Name}, Class => $Class, }, ); } my $Status = $Param{'Status'} || ''; my %Notification; if ( $Status eq 'Updated' ) { %Notification = ( Info => Translatable('State Transition Updated!'), ); } elsif ( $Status eq 'Added' ) { %Notification = ( Info => Translatable('State Transition Added!'), ); } # generate HTML my $Output = $LayoutObject->Header(); $Output .= $LayoutObject->NavigationBar(); if (%Notification) { $Output .= $LayoutObject->Notify(%Notification) || ''; } $Output .= $Param{Note} || ''; $Output .= $LayoutObject->Output( TemplateFile => 'AdminITSMStateMachine', Data => \%Param, ); $Output .= $LayoutObject->Footer(); return $Output; } 1;