# -- # Copyright (C) 2001-2019 OTRS AG, https://otrs.com/ # -- # $origin: otrs - b9cf29ede488bbc3bf5bd0d49f422ecc65668a0c - Kernel/Modules/CustomerTicketProcess.pm # -- # 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::CustomerTicketProcess; ## nofilter(TidyAll::Plugin::OTRS::Perl::DBObject) use strict; use warnings; use Kernel::System::VariableCheck qw(:all); use Kernel::Language qw(Translatable); our $ObjectManagerDisabled = 1; sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless $Self, $Type; # global config hash for id dissolution $Self->{NameToID} = { Title => 'Title', State => 'StateID', StateID => 'StateID', Lock => 'LockID', LockID => 'LockID', Priority => 'PriorityID', PriorityID => 'PriorityID', Queue => 'QueueID', QueueID => 'QueueID', Customer => 'CustomerID', CustomerID => 'CustomerID', CustomerNo => 'CustomerID', CustomerUserID => 'CustomerUserID', Type => 'TypeID', TypeID => 'TypeID', SLA => 'SLAID', SLAID => 'SLAID', Service => 'ServiceID', ServiceID => 'ServiceID', Article => 'Article', }; # --- # ITSMIncidentProblemManagement # --- # Check if ITSMIncidentProblemManagement is used. my $OutputFilterConfig = $Kernel::OM->Get('Kernel::Config')->Get('Frontend::Output::FilterElementPost'); if ( $OutputFilterConfig->{ITSMIncidentProblemManagement} ) { $Self->{ITSMIncidentProblemManagement} = 1; } # --- return $Self; } sub Run { my ( $Self, %Param ) = @_; # get param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); my $TicketID = $ParamObject->GetParam( Param => 'TicketID' ); my $ActivityDialogEntityID = $ParamObject->GetParam( Param => 'ActivityDialogEntityID' ); my $ActivityDialogHashRef; # get needed objects my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $ActivityDialogObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog'); # some fields should be skipped for the customer interface my $SkipFields = [ 'Owner', 'Responsible', 'Lock', 'PendingTime', 'CustomerID' ]; if ($TicketID) { # include extra fields should be skipped for my $Item (qw(Service SLA Queue)) { push @{$SkipFields}, $Item; } # check if there is a configured required permission # for the ActivityDialog (if there is one) my $ActivityDialogPermission = 'rw'; if ($ActivityDialogEntityID) { $ActivityDialogHashRef = $ActivityDialogObject->ActivityDialogGet( ActivityDialogEntityID => $ActivityDialogEntityID, Interface => 'CustomerInterface', ); if ( !IsHashRefWithData($ActivityDialogHashRef) ) { return $LayoutObject->CustomerErrorScreen( Message => $LayoutObject->{LanguageObject} ->Translate( 'Couldn\'t get ActivityDialogEntityID "%s"!', $ActivityDialogEntityID ), Comment => Translatable('Please contact the administrator.'), ); } if ( $ActivityDialogHashRef->{Permission} ) { $ActivityDialogPermission = $ActivityDialogHashRef->{Permission}; } } # check permissions my $Access = $TicketObject->TicketCustomerPermission( Type => $ActivityDialogPermission, TicketID => $Self->{TicketID}, UserID => $Self->{UserID} ); # error screen, don't show ticket if ( !$Access ) { return $LayoutObject->CustomerNoPermission( Message => $LayoutObject->{LanguageObject}->Translate( 'You need %s permissions!', $ActivityDialogPermission ), WithHeader => 'yes', ); } # get ACL restrictions my %PossibleActions = ( 1 => $Self->{Action} ); my $ACL = $TicketObject->TicketAcl( Data => \%PossibleActions, Action => $Self->{Action}, TicketID => $Self->{TicketID}, ReturnType => 'Action', ReturnSubType => '-', CustomerUserID => $Self->{UserID}, ); my %AclAction = $TicketObject->TicketAclActionData(); # check if ACL restrictions exist if ( $ACL || IsHashRefWithData( \%AclAction ) ) { my %AclActionLookup = reverse %AclAction; # show error screen if ACL prohibits this action if ( !$AclActionLookup{ $Self->{Action} } ) { return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' ); } } if ( IsHashRefWithData($ActivityDialogHashRef) ) { my $PossibleActivityDialogs = { 1 => $ActivityDialogEntityID }; # get ACL restrictions my $ACL = $TicketObject->TicketAcl( Data => $PossibleActivityDialogs, ActivityDialogEntityID => $ActivityDialogEntityID, TicketID => $TicketID, ReturnType => 'ActivityDialog', ReturnSubType => '-', Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); if ($ACL) { %{$PossibleActivityDialogs} = $TicketObject->TicketAclData(); } # check if ACL resctictions exist if ( !IsHashRefWithData($PossibleActivityDialogs) ) { return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' ); } } } # list only Active processes by default my @ProcessStates = ('Active'); # set IsMainWindow and IsAjaxRequest for proper error responses, screen display and process list $Self->{IsMainWindow} = $ParamObject->GetParam( Param => 'IsMainWindow' ) || ''; $Self->{IsAjaxRequest} = $ParamObject->GetParam( Param => 'IsAjaxRequest' ) || ''; # fetch also FadeAway processes to continue working with existing tickets, but not to start new # ones if ( !$Self->{IsMainWindow} && $Self->{Subaction} ) { push @ProcessStates, 'FadeAway'; } # get process object my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process'); # get the list of processes that customer can start my $ProcessList = $ProcessObject->ProcessList( ProcessState => \@ProcessStates, Interface => ['CustomerInterface'], ); # also get the list of processes initiated by agents, as an activity dialog might be configured # for the customer interface my $FollowupProcessList = $ProcessObject->ProcessList( ProcessState => \@ProcessStates, Interface => [ 'AgentInterface', 'CustomerInterface' ], ); my $ProcessEntityID = $ParamObject->GetParam( Param => 'ProcessEntityID' ); if ( !IsHashRefWithData($ProcessList) && !IsHashRefWithData($FollowupProcessList) ) { return $LayoutObject->CustomerErrorScreen( Message => Translatable('No Process configured!'), Comment => Translatable('Please contact the administrator.'), ); } # prepare process list for ACLs, use only entities instead of names, convert from # P1 => Name to P1 => P1. As ACLs should work only against entities my %ProcessListACL = map { $_ => $_ } sort keys %{$ProcessList}; # validate the ProcessList with stored ACLs my $ACL = $TicketObject->TicketAcl( ReturnType => 'Process', ReturnSubType => '-', Data => \%ProcessListACL, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); if ( IsHashRefWithData($ProcessList) && $ACL ) { # get ACL results my %ACLData = $TicketObject->TicketAclData(); # recover process names my %ReducedProcessList = map { $_ => $ProcessList->{$_} } sort keys %ACLData; # replace original process list with the reduced one $ProcessList = \%ReducedProcessList; } # get form id $Self->{FormID} = $ParamObject->GetParam( Param => 'FormID' ); # create form id if ( !$Self->{FormID} ) { $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(); } # if we have no subaction display the process list to start a new one if ( !$Self->{Subaction} ) { # to display the process list is mandatory to have processes that customer can start if ( !IsHashRefWithData($ProcessList) ) { return $LayoutObject->CustomerErrorScreen( Message => Translatable('No Process configured!'), Comment => Translatable('Please contact the administrator.'), ); } # get process id (if any, a process should be pre-selected) $Param{ProcessID} = $ParamObject->GetParam( Param => 'ID' ); if ( $Param{ProcessID} ) { $Param{PreSelectProcess} = 1; } return $Self->_DisplayProcessList( %Param, ProcessList => $ProcessList, ProcessEntityID => $ProcessEntityID || $Param{ProcessID} ); } # check if the selected process from the list is valid, prevent tamper with process selection # list (not existing, invalid an fade away processes must not be able to start a new process # ticket) elsif ( $Self->{Subaction} eq 'DisplayActivityDialogAJAX' && !$ProcessList->{$ProcessEntityID} && $Self->{IsMainWindow} ) { # translate the error message (as it will be injected in the HTML) my $ErrorMessage = $LayoutObject->{LanguageObject}->Translate("The selected process is invalid!"); # return a predefined HTML sctructure as the AJAX call is expecting and HTML response return $LayoutObject->Attachment( ContentType => 'text/html; charset=' . $LayoutObject->{Charset}, Content => '
', Type => 'inline', NoCache => 1, ); } # if invalid process is detected on a ActivityDilog popup screen show an error message elsif ( $Self->{Subaction} eq 'DisplayActivityDialog' && !$FollowupProcessList->{$ProcessEntityID} && !$Self->{IsMainWindow} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject}->Translate( 'Process %s is invalid!', $ProcessEntityID ), Comment => Translatable('Please contact the administrator.'), ); } # Get the necessary parameters # collects a mixture of present values bottom to top: # SysConfig DefaultValues, ActivityDialog DefaultValues, TicketValues, SubmittedValues # including ActivityDialogEntityID and ProcessEntityID # is used for: # - Parameter checking before storing # - will be used for ACL checking later on my $GetParam = $Self->_GetParam( ProcessEntityID => $ProcessEntityID, ); if ( $Self->{Subaction} eq 'StoreActivityDialog' && $ProcessEntityID ) { $LayoutObject->ChallengeTokenCheck( Type => 'Customer' ); return $Self->_StoreActivityDialog( %Param, ProcessName => $ProcessList->{$ProcessEntityID}, ProcessEntityID => $ProcessEntityID, GetParam => $GetParam, ); } if ( $Self->{Subaction} eq 'DisplayActivityDialog' && $ProcessEntityID ) { return $Self->_OutputActivityDialog( %Param, ProcessEntityID => $ProcessEntityID, GetParam => $GetParam, ); } if ( $Self->{Subaction} eq 'DisplayActivityDialogAJAX' && $ProcessEntityID ) { my $ActivityDialogHTML = $Self->_OutputActivityDialog( %Param, ProcessEntityID => $ProcessEntityID, GetParam => $GetParam, ); return $LayoutObject->Attachment( ContentType => 'text/html; charset=' . $LayoutObject->{Charset}, Content => $ActivityDialogHTML, Type => 'inline', NoCache => 1, ); } elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) { return $Self->_RenderAjax( %Param, ProcessEntityID => $ProcessEntityID, GetParam => $GetParam, ); } return $LayoutObject->CustomerErrorScreen( Message => Translatable('Subaction is invalid!'), Comment => Translatable('Please contact the administrator.'), ); } sub _RenderAjax { # FatalError is safe because a JSON structure is expecting, then it will result into a # communications error my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Needed (qw(ProcessEntityID)) { if ( !$Param{$Needed} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_RenderAjax' ), ); } } my $ActivityDialogEntityID = $Param{GetParam}{ActivityDialogEntityID}; if ( !$ActivityDialogEntityID ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', 'ActivityDialogEntityID', '_RenderAjax' ), ); } my $ActivityDialog = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog')->ActivityDialogGet( ActivityDialogEntityID => $ActivityDialogEntityID, Interface => 'CustomerInterface', ); if ( !IsHashRefWithData($ActivityDialog) ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'No ActivityDialog configured for %s in _RenderAjax!', $ActivityDialogEntityID ), ); } # get list type my $TreeView = 0; if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Frontend::ListType') eq 'tree' ) { $TreeView = 1; } my %FieldsProcessed; my @JSONCollector; my $Services; # All submitted DynamicFields # get dynamic field values form http request my %DynamicFieldValues; # get backend object my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => 'Ticket', ); # reduce the dynamic fields to only the ones that are desinged for customer interface my @CustomerDynamicFields; DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; push @CustomerDynamicFields, $DynamicFieldConfig; } $DynamicField = \@CustomerDynamicFields; # get param object my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # extract the dynamic field value from the web request $DynamicFieldValues{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldValueGet( DynamicFieldConfig => $DynamicFieldConfig, ParamObject => $ParamObject, LayoutObject => $LayoutObject, ); } # convert dynamic field values into a structure for ACLs my %DynamicFieldCheckParam; DYNAMICFIELD: for my $DynamicField ( sort keys %DynamicFieldValues ) { next DYNAMICFIELD if !$DynamicField; next DYNAMICFIELD if !$DynamicFieldValues{$DynamicField}; $DynamicFieldCheckParam{ 'DynamicField_' . $DynamicField } = $DynamicFieldValues{$DynamicField}; } $Param{GetParam}->{DynamicField} = \%DynamicFieldCheckParam; # some fields should be skipped for the customer interface my $SkipFields = [ 'Owner', 'Responsible', 'Lock', 'PendingTime', 'CustomerID' ]; # Get the activity dialog's Submit Param's or Config Params DIALOGFIELD: for my $CurrentField ( @{ $ActivityDialog->{FieldOrder} } ) { # some fields should be skipped for the customer interface next DIALOGFIELD if ( grep { $_ eq $CurrentField } @{$SkipFields} ); # Skip if we're working on a field that was already done with or without ID if ( $Self->{NameToID}{$CurrentField} && $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } ) { next DIALOGFIELD; } if ( $CurrentField =~ m{^DynamicField_(.*)}xms ) { my $DynamicFieldName = $1; my $DynamicFieldConfig = ( grep { $_->{Name} eq $DynamicFieldName } @{$DynamicField} )[0]; next DIALOGFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsACLReducible = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); next DIALOGFIELD if !$IsACLReducible; my $PossibleValues = $BackendObject->PossibleValuesGet( DynamicFieldConfig => $DynamicFieldConfig, ); # convert possible values key => value to key => key for ACLs using a Hash slice my %AclData = %{$PossibleValues}; @AclData{ keys %AclData } = keys %AclData; # get ticket object my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); # set possible values filter from ACLs my $ACL = $TicketObject->TicketAcl( %{ $Param{GetParam} }, ReturnType => 'Ticket', ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => \%AclData, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); if ($ACL) { my %Filter = $TicketObject->TicketAclData(); # convert Filer key => key back to key => value using map %{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter; } my $DataValues = $BackendObject->BuildSelectionDataGet( DynamicFieldConfig => $DynamicFieldConfig, PossibleValues => $PossibleValues, Value => $Param{GetParam}{ 'DynamicField_' . $DynamicFieldConfig->{Name} }, ) || $PossibleValues; # add dynamic field to the JSONCollector push( @JSONCollector, { Name => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => $DataValues, SelectedID => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, Translation => $DynamicFieldConfig->{Config}->{TranslatableValues} || 0, Max => 100, } ); } elsif ( $Self->{NameToID}{$CurrentField} eq 'QueueID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; my $Data = $Self->_GetQueues( %{ $Param{GetParam} }, ); # add Queue to the JSONCollector push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $Param{GetParam}{ $Self->{NameToID}{$CurrentField} }, PossibleNone => 1, Translation => 0, TreeView => $TreeView, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } elsif ( $Self->{NameToID}{$CurrentField} eq 'StateID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; my $Data = $Self->_GetStates( %{ $Param{GetParam} }, ); # add State to the JSONCollector push( @JSONCollector, { Name => 'StateID', Data => $Data, SelectedID => $Param{GetParam}{ $Self->{NameToID}{$CurrentField} }, Translation => 1, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } elsif ( $Self->{NameToID}{$CurrentField} eq 'PriorityID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; my $Data = $Self->_GetPriorities( %{ $Param{GetParam} }, ); # --- # ITSMIncidentProblemManagement # --- # check if priority needs to be recalculated if ( $Self->{ITSMIncidentProblemManagement} && ( $Param{GetParam}->{ElementChanged} eq 'ServiceID' || $Param{GetParam}->{ElementChanged} eq 'DynamicField_ITSMImpact' ) && $Param{GetParam}->{ServiceID} && $Param{GetParam}->{DynamicField_ITSMImpact} ) { my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceGet( ServiceID => $Param{GetParam}->{ServiceID}, UserID => $Self->{UserID}, ); # calculate priority from the CIP matrix my $PriorityIDFromImpact = $Kernel::OM->Get('Kernel::System::ITSMCIPAllocate')->PriorityAllocationGet( Criticality => $Service{Criticality}, Impact => $Param{GetParam}->{DynamicField_ITSMImpact}, ); # add Priority to the JSONCollector push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $PriorityIDFromImpact, Translation => 1, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}->{$CurrentField} } = 1; next DIALOGFIELD; } # --- # add Priority to the JSONCollector push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $Param{GetParam}{ $Self->{NameToID}{$CurrentField} }, Translation => 1, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } elsif ( $Self->{NameToID}{$CurrentField} eq 'ServiceID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; my $Data = $Self->_GetServices( %{ $Param{GetParam} }, ); $Services = $Data; # add Service to the JSONCollector (Use ServiceID from web request) push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $ParamObject->GetParam( Param => 'ServiceID' ) || '', PossibleNone => 1, Translation => 0, TreeView => $TreeView, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } elsif ( $Self->{NameToID}{$CurrentField} eq 'SLAID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; # if SLA is render before service (by it order in the fields) it needs to create # the service list if ( !IsHashRefWithData($Services) ) { $Services = $Self->_GetServices( %{ $Param{GetParam} }, ); } my $Data = $Self->_GetSLAs( %{ $Param{GetParam} }, Services => $Services, ServiceID => $ParamObject->GetParam( Param => 'ServiceID' ) || '', ); # add SLA to the JSONCollector (Use SelectedID from web request) push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $ParamObject->GetParam( Param => 'SLAID' ) || '', PossibleNone => 1, Translation => 0, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } elsif ( $Self->{NameToID}{$CurrentField} eq 'TypeID' ) { next DIALOGFIELD if $FieldsProcessed{ $Self->{NameToID}{$CurrentField} }; my $Data = $Self->_GetTypes( %{ $Param{GetParam} }, ); # Add Type to the JSONCollector (Use SelectedID from web request). push( @JSONCollector, { Name => $Self->{NameToID}{$CurrentField}, Data => $Data, SelectedID => $ParamObject->GetParam( Param => 'TypeID' ) || '', PossibleNone => 1, Translation => 0, Max => 100, }, ); $FieldsProcessed{ $Self->{NameToID}{$CurrentField} } = 1; } } my $JSON = $LayoutObject->BuildSelectionJSON( [@JSONCollector] ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON, Type => 'inline', NoCache => 1, ); } # =item _GetParam() # # returns the current data state of the submitted information # # This contains the following data for the different callers: # # Initial call with selected Process: # ProcessEntityID # ActivityDialogEntityID # DefaultValues for the configured Fields in that ActivityDialog # DefaultValues for the 4 required Fields Queue State Lock Priority # # First Store call submitting an Activity Dialog: # ProcessEntityID # ActivityDialogEntityID # SubmittedValues for the current ActivityDialog # ActivityDialog DefaultValues for invisible fields of that ActivityDialog # DefaultValues for the 4 required Fields Queue State Lock Priority # if not configured in the ActivityDialog # # ActivityDialog fillout request on existing Ticket: # ProcessEntityID # ActivityDialogEntityID # TicketValues # # ActivityDialog store request or AjaxUpdate request on existing Tickets: # ProcessEntityID # ActivityDialogEntityID # TicketValues for all not-Submitted Values # Submitted Values # # my $GetParam = _GetParam( # ProcessEntityID => $ProcessEntityID, # ); # # =cut sub _GetParam { my ( $Self, %Param ) = @_; #my $IsAJAXUpdate = $Param{AJAX} || ''; # get needed objects my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); for my $Needed (qw(ProcessEntityID)) { if ( !$Param{$Needed} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_GetParam' ), ); } } my %GetParam; my %Ticket; my $ProcessEntityID = $Param{ProcessEntityID}; my $TicketID = $ParamObject->GetParam( Param => 'TicketID' ); my $ActivityDialogEntityID = $ParamObject->GetParam( Param => 'ActivityDialogEntityID', ); my $ActivityEntityID; my %ValuesGotten; my $Value; # If we got no ActivityDialogEntityID and no TicketID # we have to get the Processes' Startpoint if ( !$ActivityDialogEntityID && !$TicketID ) { my $ActivityActivityDialog = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process')->ProcessStartpointGet( ProcessEntityID => $ProcessEntityID, ); if ( !$ActivityActivityDialog->{ActivityDialog} || !$ActivityActivityDialog->{Activity} ) { my $Message = $LayoutObject->{LanguageObject}->Translate( 'Got no Start ActivityEntityID or Start ActivityDialogEntityID for Process: %s in _GetParam!', $ProcessEntityID ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } $ActivityDialogEntityID = $ActivityActivityDialog->{ActivityDialog}; $ActivityEntityID = $ActivityActivityDialog->{Activity}; } my $ActivityDialog = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog')->ActivityDialogGet( ActivityDialogEntityID => $ActivityDialogEntityID, Interface => 'CustomerInterface', ); if ( !IsHashRefWithData($ActivityDialog) ) { return $LayoutObject->CustomerErrorScreen( Message => $LayoutObject->{LanguageObject} ->Translate( 'Couldn\'t get ActivityDialogEntityID "%s"!', $ActivityDialogEntityID ), Comment => Translatable('Please contact the administrator.'), ); } # get config object my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # if there is a ticket then is not an AJAX request if ($TicketID) { %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet( TicketID => $TicketID, UserID => $ConfigObject->Get('CustomerPanelUserID'), DynamicFields => 1, ); %GetParam = %Ticket; if ( !IsHashRefWithData( \%GetParam ) ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Couldn\'t get Ticket for TicketID: %s in _GetParam!', $TicketID ), ); } $ActivityEntityID = $Ticket{ 'DynamicField_' . $ConfigObject->Get("Process::DynamicFieldProcessManagementActivityID") }; if ( !$ActivityEntityID ) { $LayoutObject->CustomerFatalError( Message => Translatable('Couldn\'t determine ActivityEntityID. DynamicField or Config isn\'t set properly!'), ); } } $GetParam{ActivityDialogEntityID} = $ActivityDialogEntityID; $GetParam{ActivityEntityID} = $ActivityEntityID; $GetParam{ProcessEntityID} = $ProcessEntityID; # some fields should be skipped for the customer interface my $SkipFields = [ 'Owner', 'Responsible', 'Lock', 'PendingTime', 'CustomerID' ]; my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => 'Ticket', ); # get backend object my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # reduce the dynamic fields to only the ones that are desinged for customer interface my @CustomerDynamicFields; DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; push @CustomerDynamicFields, $DynamicFieldConfig; } $DynamicField = \@CustomerDynamicFields; # Get the activitydialogs's Submit Param's or Config Params DIALOGFIELD: for my $CurrentField ( @{ $ActivityDialog->{FieldOrder} } ) { # some fields should be skipped for the customer interface next DIALOGFIELD if ( grep { $_ eq $CurrentField } @{$SkipFields} ); # Skip if we're working on a field that was already done with or without ID if ( $Self->{NameToID}{$CurrentField} && $ValuesGotten{ $Self->{NameToID}{$CurrentField} } ) { next DIALOGFIELD; } if ( $CurrentField =~ m{^DynamicField_(.*)}xms ) { my $DynamicFieldName = $1; # Get the Config of the current DynamicField (the first element of the grep result array) my $DynamicFieldConfig = ( grep { $_->{Name} eq $DynamicFieldName } @{$DynamicField} )[0]; if ( !IsHashRefWithData($DynamicFieldConfig) ) { my $Message = "DynamicFieldConfig missing for field: $Param{FieldName}, or is not a Ticket Dynamic Field!"; # log error but does not stop the execution as it could be an old Article # DynamicField, see bug#11666 $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => $Message, ); next DIALOGFIELD; } # Get DynamicField Values $Value = $BackendObject->EditFieldValueGet( DynamicFieldConfig => $DynamicFieldConfig, ParamObject => $ParamObject, LayoutObject => $LayoutObject, ); # --- # ITSMIncidentProblemManagement # --- # set the criticality from the service if ( $Self->{ITSMIncidentProblemManagement} && $DynamicFieldName eq 'ITSMCriticality' && $ParamObject->GetParam( Param => 'ServiceID' ) ) { # get service my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceGet( ServiceID => $ParamObject->GetParam( Param => 'ServiceID' ), UserID => $Self->{UserID}, ); # set the criticality $Value = $Service{Criticality}; } # --- # If we got a submitted param, take it and next out if ( defined $Value && ( $Value eq '' || IsStringWithData($Value) || IsArrayRefWithData($Value) || IsHashRefWithData($Value) ) ) { $GetParam{$CurrentField} = $Value; next DIALOGFIELD; } # If we didn't have a Param Value try the ticket Value # next out if it was successful if ( defined $Ticket{$CurrentField} && ( $Ticket{$CurrentField} eq '' || IsStringWithData( $Ticket{$CurrentField} ) || IsArrayRefWithData( $Ticket{$CurrentField} ) || IsHashRefWithData( $Ticket{$CurrentField} ) ) ) { $GetParam{$CurrentField} = $Ticket{$CurrentField}; next DIALOGFIELD; } # If we had neighter submitted nor ticket param get the ActivityDialog's default Value # next out if it was successful $Value = $ActivityDialog->{Fields}{$CurrentField}{DefaultValue}; if ($Value) { $GetParam{$CurrentField} = $Value; next DIALOGFIELD; } # If we had no submitted, ticket or ActivityDialog default value # use the DynamicField's default value and next out $Value = $DynamicFieldConfig->{Config}{DefaultValue}; if ($Value) { $GetParam{$CurrentField} = $Value; next DIALOGFIELD; } # if all that failed then the field should not have a defined value otherwise # if a value (even empty) is sent, fields like Date or DateTime will mark the field as # used with the field display value, this could lead to unwanted field sets, # see bug#9159 next DIALOGFIELD; } # get article fields if ( $CurrentField eq 'Article' ) { $GetParam{Subject} = $ParamObject->GetParam( Param => 'Subject' ); $GetParam{Body} = $ParamObject->GetParam( Param => 'Body' ); @{ $GetParam{InformUserID} } = $ParamObject->GetArray( Param => 'InformUserID', ); $ValuesGotten{Article} = 1 if ( $GetParam{Subject} && $GetParam{Body} ); } if ( $CurrentField eq 'CustomerID' ) { $GetParam{Customer} = $ParamObject->GetParam( Param => 'SelectedCustomerUser', ) || ''; $GetParam{CustomerUserID} = $ParamObject->GetParam( Param => 'SelectedCustomerUser', ) || ''; } # Non DynamicFields # 1. try to get the required param my $Value = $ParamObject->GetParam( Param => $Self->{NameToID}{$CurrentField} ); if ($Value) { # if we have an ID field make sure the value without ID won't be in the # %GetParam Hash any more if ( $Self->{NameToID}{$CurrentField} =~ m{(.*)ID$}xms ) { $GetParam{$1} = undef; } $GetParam{ $Self->{NameToID}{$CurrentField} } = $Value; $ValuesGotten{ $Self->{NameToID}{$CurrentField} } = 1; next DIALOGFIELD; } # If we got ticket params, the GetParam Hash was already filled before the loop # and we can next out if ( $GetParam{ $Self->{NameToID}{$CurrentField} } ) { $ValuesGotten{ $Self->{NameToID}{$CurrentField} } = 1; next DIALOGFIELD; } # if no Submitted nore Ticket Param get ActivityDialog Config's Param if ( $CurrentField ne 'CustomerID' ) { $Value = $ActivityDialog->{Fields}{$CurrentField}{DefaultValue}; } if ($Value) { $ValuesGotten{ $Self->{NameToID}{$CurrentField} } = 1; $GetParam{$CurrentField} = $Value; next DIALOGFIELD; } } REQUIREDFIELDLOOP: for my $CurrentField (qw(Queue State Lock Priority)) { $Value = undef; if ( !$ValuesGotten{ $Self->{NameToID}{$CurrentField} } ) { $Value = $ConfigObject->Get("Process::Default$CurrentField"); if ( !$Value ) { my $Message = $LayoutObject->{LanguageObject} ->Translate( 'Process::Default%s Config Value missing!', $CurrentField ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } $GetParam{$CurrentField} = $Value; $ValuesGotten{ $Self->{NameToID}{$CurrentField} } = 1; } } # get also the IDs for the Required files (if they are not present) if ( $GetParam{Queue} && !$GetParam{QueueID} ) { $GetParam{QueueID} = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup( Queue => $GetParam{Queue} ); } if ( $GetParam{State} && !$GetParam{StateID} ) { $GetParam{StateID} = $Kernel::OM->Get('Kernel::System::State')->StateLookup( State => $GetParam{State} ); } if ( $GetParam{Lock} && !$GetParam{LockID} ) { $GetParam{LockID} = $Kernel::OM->Get('Kernel::System::Lock')->LockLookup( Lock => $GetParam{Lock} ); } if ( $GetParam{Priority} && !$GetParam{PriorityID} ) { $GetParam{PriorityID} = $Kernel::OM->Get('Kernel::System::Priority')->PriorityLookup( Priority => $GetParam{Priority}, ); } # and finally we'll have the special parameters: $GetParam{ResponsibleAll} = $ParamObject->GetParam( Param => 'ResponsibleAll' ); $GetParam{OwnerAll} = $ParamObject->GetParam( Param => 'OwnerAll' ); # --- # ITSMIncidentProblemManagement # --- $GetParam{ElementChanged} = $ParamObject->GetParam( Param => 'ElementChanged' ); # --- return \%GetParam; } sub _OutputActivityDialog { my ( $Self, %Param ) = @_; my $TicketID = $Param{GetParam}{TicketID}; my $ActivityDialogEntityID = $Param{GetParam}{ActivityDialogEntityID}; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); # Check needed parameters: # ProcessEntityID only # TicketID ActivityDialogEntityID if ( !$Param{ProcessEntityID} || ( !$TicketID && !$ActivityDialogEntityID ) ) { my $Message = Translatable('Got no ProcessEntityID or TicketID and ActivityDialogEntityID!'); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } my $ActivityActivityDialog; my %Ticket; my %Error = (); my %ErrorMessage = (); # If we had Errors, we got an Error hash %Error = %{ $Param{Error} } if ( IsHashRefWithData( $Param{Error} ) ); %ErrorMessage = %{ $Param{ErrorMessage} } if ( IsHashRefWithData( $Param{ErrorMessage} ) ); # get process object my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); if ( !$TicketID ) { $ActivityActivityDialog = $ProcessObject->ProcessStartpointGet( ProcessEntityID => $Param{ProcessEntityID}, ); if ( !IsHashRefWithData($ActivityActivityDialog) ) { my $Message = $LayoutObject->{LanguageObject}->Translate( 'Can\'t get StartActivityDialog and StartActivityDialog for the ProcessEntityID "%s"!', $Param{ProcessEntityID} ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } } else { # no AJAX update in this part %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet( TicketID => $TicketID, UserID => $ConfigObject->Get('CustomerPanelUserID'), DynamicFields => 1, ); if ( !IsHashRefWithData( \%Ticket ) ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject}->Translate( 'Can\'t get Ticket "%s"!', $Param{TicketID} ), ); } my $DynamicFieldProcessID = 'DynamicField_' . $ConfigObject->Get('Process::DynamicFieldProcessManagementProcessID'); my $DynamicFieldActivityID = 'DynamicField_' . $ConfigObject->Get('Process::DynamicFieldProcessManagementActivityID'); if ( !$Ticket{$DynamicFieldProcessID} || !$Ticket{$DynamicFieldActivityID} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Can\'t get ProcessEntityID or ActivityEntityID for Ticket "%s"!', $Param{TicketID} ), ); } $ActivityActivityDialog = { Activity => $Ticket{$DynamicFieldActivityID}, ActivityDialog => $ActivityDialogEntityID, }; } my $Activity = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity')->ActivityGet( Interface => 'CustomerInterface', ActivityEntityID => $ActivityActivityDialog->{Activity} ); if ( !$Activity ) { my $Message = $LayoutObject->{LanguageObject}->Translate( 'Can\'t get Activity configuration for ActivityEntityID "%s"!', $ActivityActivityDialog->{Activity} ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } my $ActivityDialog = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog')->ActivityDialogGet( ActivityDialogEntityID => $ActivityActivityDialog->{ActivityDialog}, Interface => 'CustomerInterface', ); if ( !IsHashRefWithData($ActivityDialog) ) { my $Message = $LayoutObject->{LanguageObject}->Translate( 'Can\'t get ActivityDialog configuration for ActivityDialogEntityID "%s"!', $ActivityActivityDialog->{ActivityDialog} ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } # grep out Overwrites if defined on the Activity my @OverwriteActivityDialogNumber = grep { ref $Activity->{ActivityDialog}{$_} eq 'HASH' && $Activity->{ActivityDialog}{$_}{ActivityDialogEntityID} && $Activity->{ActivityDialog}{$_}{ActivityDialogEntityID} eq $ActivityActivityDialog->{ActivityDialog} && IsHashRefWithData( $Activity->{ActivityDialog}{$_}{Overwrite} ) } keys %{ $Activity->{ActivityDialog} }; # let the Overwrites Overwrite the ActivityDialog's Hash values if ( $OverwriteActivityDialogNumber[0] ) { %{$ActivityDialog} = ( %{$ActivityDialog}, %{ $Activity->{ActivityDialog}{ $OverwriteActivityDialogNumber[0] }{Overwrite} } ); } # Add PageHeader, Navbar, Formheader (Process/ActivityDialogHeader) my $Output; my $MainBoxClass; if ( !$Self->{IsMainWindow} ) { $Output = $LayoutObject->CustomerHeader( Type => 'Small', Value => $Ticket{Number}, ); # display given notify messages if this is not an AJAX request if ( IsArrayRefWithData( $Param{Notify} ) ) { for my $NotifyData ( @{ $Param{Notify} } ) { $Output .= $LayoutObject->Notify( %{$NotifyData} ); } } $LayoutObject->Block( Name => 'Header', Data => { Name => $LayoutObject->{LanguageObject}->Translate( $ActivityDialog->{Name} ) || '', }, ); # show descriptions if ( $ActivityDialog->{DescriptionShort} ) { $LayoutObject->Block( Name => 'DescriptionShort', Data => { DescriptionShort => $LayoutObject->{LanguageObject}->Translate( $ActivityDialog->{DescriptionShort}, ), }, ); } if ( $ActivityDialog->{DescriptionLong} ) { $LayoutObject->Block( Name => 'DescriptionLong', Data => { DescriptionLong => $LayoutObject->{LanguageObject}->Translate( $ActivityDialog->{DescriptionLong}, ), }, ); } } elsif ( $Self->{IsMainWindow} && IsHashRefWithData( \%Error ) ) { # add rich text editor if ( $LayoutObject->{BrowserRichText} ) { # use height/width defined for this screen $Param{RichTextHeight} = $Self->{Config}->{RichTextHeight} || 0; $Param{RichTextWidth} = $Self->{Config}->{RichTextWidth} || 0; # set up customer rich text editor $LayoutObject->CustomerSetRichTextParameters( Data => \%Param, ); } # display complete header and nav bar in ajax dialogs when there is a server error $Output = $LayoutObject->CustomerHeader(); $Output .= $LayoutObject->CustomerNavigationBar(); # display original header texts (the process list maybe is not necessary) $Output .= $LayoutObject->Output( TemplateFile => 'CustomerTicketProcess', Data => {}, ); # set the MainBox class to add correct borders to the screen $MainBoxClass = 'MainBox'; } # Show descriptions if activity is a first screen. See bug#12649 for more information. if ( $Self->{IsMainWindow} ) { if ( $ActivityDialog->{DescriptionShort} ) { $LayoutObject->Block( Name => 'DescriptionShortAlt', Data => { DescriptionShort => $LayoutObject->{LanguageObject}->Translate( $ActivityDialog->{DescriptionShort}, ), }, ); } if ( $ActivityDialog->{DescriptionLong} ) { $LayoutObject->Block( Name => 'DescriptionLongAlt', Data => { DescriptionLong => $LayoutObject->{LanguageObject}->Translate( $ActivityDialog->{DescriptionLong}, ), }, ); } } # show close & cancel link if necessary if ( !$Self->{IsMainWindow} ) { if ( $Param{RenderLocked} ) { $LayoutObject->Block( Name => 'PropertiesLock', Data => { %Param, TicketID => $TicketID, }, ); } else { $LayoutObject->Block( Name => 'CancelLink', ); } } $Output .= $LayoutObject->Output( TemplateFile => 'ProcessManagement/CustomerActivityDialogHeader', Data => { FormName => 'ActivityDialogDialog' . $ActivityActivityDialog->{ActivityDialog}, FormID => $Self->{FormID}, Subaction => 'StoreActivityDialog', TicketID => $Ticket{TicketID} || '', ActivityDialogEntityID => $ActivityActivityDialog->{ActivityDialog}, ProcessEntityID => $Param{ProcessEntityID} || $Ticket{ 'DynamicField_' . $ConfigObject->Get( 'Process::DynamicFieldProcessManagementProcessID' ) }, IsMainWindow => $Self->{IsMainWindow}, MainBoxClass => $MainBoxClass || '', }, ); my %RenderedFields = (); # get the list of fields where the AJAX loader icon should appear on AJAX updates triggered # by ActivityDialog fields my $AJAXUpdatableFields = $Self->_GetAJAXUpdatableFields( ActivityDialogFields => $ActivityDialog->{Fields}, ); # some fields should be skipped for the customer interface my $SkipFields = [ 'Owner', 'Responsible', 'Lock', 'PendingTime', 'CustomerID' ]; # Loop through ActivityDialogFields and render their output DIALOGFIELD: for my $CurrentField ( @{ $ActivityDialog->{FieldOrder} } ) { # some fields should be skipped for the customer interface next DIALOGFIELD if ( grep { $_ eq $CurrentField } @{$SkipFields} ); if ( !IsHashRefWithData( $ActivityDialog->{Fields}{$CurrentField} ) ) { my $Message = $LayoutObject->{LanguageObject}->Translate( 'Can\'t get data for Field "%s" of ActivityDialog "%s"!', $CurrentField, $ActivityActivityDialog->{ActivityDialog} ); # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Message, ); } $LayoutObject->CustomerFatalError( Message => $Message, ); } my %FieldData = %{ $ActivityDialog->{Fields}{$CurrentField} }; # We render just visible ActivityDialogFields next DIALOGFIELD if !$FieldData{Display}; # render DynamicFields if ( $CurrentField =~ m{^DynamicField_(.*)}xms ) { my $DynamicFieldName = $1; my $Response = $Self->_RenderDynamicField( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $DynamicFieldName, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, ErrorMessage => \%ErrorMessage || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{$CurrentField} = 1; } # render State elsif ( $Self->{NameToID}->{$CurrentField} eq 'StateID' ) { # We don't render Fields twice, # if there was already a Config without ID, skip this field # next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderState( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{ $Self->{NameToID}->{$CurrentField} } = 1; } # render Queue elsif ( $Self->{NameToID}->{$CurrentField} eq 'QueueID' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderQueue( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{ $Self->{NameToID}->{$CurrentField} } = 1; } # render Priority elsif ( $Self->{NameToID}->{$CurrentField} eq 'PriorityID' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderPriority( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{ $Self->{NameToID}->{$CurrentField} } = 1; } # render Service elsif ( $Self->{NameToID}->{$CurrentField} eq 'ServiceID' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderService( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{$CurrentField} = 1; } # render SLA elsif ( $Self->{NameToID}->{$CurrentField} eq 'SLAID' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderSLA( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{ $Self->{NameToID}->{$CurrentField} } = 1; } # render Title elsif ( $Self->{NameToID}->{$CurrentField} eq 'Title' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderTitle( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{$CurrentField} = 1; } # render Article elsif ( $Self->{NameToID}->{$CurrentField} eq 'Article' ) { next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderArticle( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, InformAgents => $ActivityDialog->{Fields}->{Article}->{Config}->{InformAgents}, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{$CurrentField} = 1; } # render Type elsif ( $Self->{NameToID}->{$CurrentField} eq 'TypeID' ) { # We don't render Fields twice, # if there was already a Config without ID, skip this field next DIALOGFIELD if $RenderedFields{ $Self->{NameToID}->{$CurrentField} }; my $Response = $Self->_RenderType( ActivityDialogField => $ActivityDialog->{Fields}{$CurrentField}, FieldName => $CurrentField, DescriptionShort => $ActivityDialog->{Fields}{$CurrentField}{DescriptionShort}, DescriptionLong => $ActivityDialog->{Fields}{$CurrentField}{DescriptionLong}, Ticket => \%Ticket || {}, Error => \%Error || {}, FormID => $Self->{FormID}, GetParam => $Param{GetParam}, AJAXUpdatableFields => $AJAXUpdatableFields, ); if ( !$Response->{Success} ) { # does not show header and footer again if ( $Self->{IsMainWindow} ) { return $LayoutObject->CustomerError( Message => $Response->{Message}, ); } $LayoutObject->CustomerFatalError( Message => $Response->{Message}, ); } $Output .= $Response->{HTML}; $RenderedFields{ $Self->{NameToID}->{$CurrentField} } = 1; } } my $FooterCSSClass = 'Footer'; if ( $Self->{IsAjaxRequest} ) { # Due to the initial loading of # the first ActivityDialog after Process selection # we have to bind the AjaxUpdate Function on # the selects, so we get the complete JSOnDocumentComplete code # and deliver it in the FooterJS block. # This Javascript Part is executed in # CustomerTicketProcess.tt $LayoutObject->Block( Name => 'FooterJS', Data => {}, ); $FooterCSSClass = 'Centered'; } # set submit button data my $ButtonText = 'Submit'; my $ButtonTitle = 'Save'; my $ButtonID = 'Submit' . $ActivityActivityDialog->{ActivityDialog}; if ( $ActivityDialog->{SubmitButtonText} ) { $ButtonText = $ActivityDialog->{SubmitButtonText}; $ButtonTitle = $ActivityDialog->{SubmitButtonText}; } $LayoutObject->Block( Name => 'Footer', Data => { FooterCSSClass => $FooterCSSClass, ButtonText => $ButtonText, ButtonTitle => $ButtonTitle, ButtonID => $ButtonID }, ); if ( $ActivityDialog->{SubmitAdviceText} ) { $LayoutObject->Block( Name => 'SubmitAdviceText', Data => { AdviceText => $ActivityDialog->{SubmitAdviceText}, }, ); } # Add the FormFooter $Output .= $LayoutObject->Output( TemplateFile => 'ProcessManagement/CustomerActivityDialogFooter', Data => {}, ); # display regular footer only in non-ajax case if ( !$Self->{IsAjaxRequest} ) { $Output .= $LayoutObject->CustomerFooter( Type => $Self->{IsMainWindow} ? '' : 'Small' ); } return $Output; } sub _RenderDynamicField { my ( $Self, %Param ) = @_; # get layout objects my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Needed (qw(FormID FieldName)) { if ( !$Param{$Needed} ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_RenderDynamicField' ), }; } } # get backend object my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => 'Ticket', ); # reduce the dynamic fields to only the ones that are desinged for customer interface my @CustomerDynamicFields; DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; push @CustomerDynamicFields, $DynamicFieldConfig; } $DynamicField = \@CustomerDynamicFields; my $DynamicFieldConfig = ( grep { $_->{Name} eq $Param{FieldName} } @{$DynamicField} )[0]; if ( !IsHashRefWithData($DynamicFieldConfig) ) { my $Message = "DynamicFieldConfig missing for field: $Param{FieldName}, or is not a Ticket Dynamic Field!"; # log error but does not stop the execution as it could be an old Article # DynamicField, see bug#11666 $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'error', Message => $Message, ); return { Success => 1, HTML => '', }; } my $PossibleValuesFilter; my $IsACLReducible = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); if ($IsACLReducible) { # get PossibleValues my $PossibleValues = $BackendObject->PossibleValuesGet( DynamicFieldConfig => $DynamicFieldConfig, ); # All Ticket DynamicFields # used for ACL checking my %DynamicFieldCheckParam = map { $_ => $Param{GetParam}{$_} } grep {m{^DynamicField_}xms} ( keys %{ $Param{GetParam} } ); # check if field has PossibleValues property in its configuration if ( IsHashRefWithData($PossibleValues) ) { # convert possible values key => value to key => key for ACLs using a Hash slice my %AclData = %{$PossibleValues}; @AclData{ keys %AclData } = keys %AclData; # set possible values filter from ACLs my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $ACL = $TicketObject->TicketAcl( %{ $Param{GetParam} }, DynamicField => \%DynamicFieldCheckParam, Action => $Self->{Action}, ReturnType => 'Ticket', ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => \%AclData, CustomerUserID => $Self->{UserID}, ); if ($ACL) { my %Filter = $TicketObject->TicketAclData(); # convert Filer key => key back to key => value using map %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } keys %Filter; } } } my $ServerError; my $ErrorMessage; if ( IsHashRefWithData( $Param{Error} ) ) { if ( defined $Param{Error}->{ $Param{FieldName} } && $Param{Error}->{ $Param{FieldName} } ne '' ) { $ServerError = 1; if ( defined $Param{ErrorMessage}->{ $Param{FieldName} } && $Param{ErrorMessage}->{ $Param{FieldName} } ne '' ) { $ErrorMessage = $Param{ErrorMessage}->{ $Param{FieldName} }; } } } my $DynamicFieldHTML = $BackendObject->EditFieldRender( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, Value => $Param{GetParam}{ 'DynamicField_' . $Param{FieldName} }, LayoutObject => $LayoutObject, ParamObject => $Kernel::OM->Get('Kernel::System::Web::Request'), AJAXUpdate => 1, Mandatory => $Param{ActivityDialogField}->{Display} == 2, UpdatableFields => $Param{AJAXUpdatableFields}, ServerError => $ServerError, ErrorMessage => $ErrorMessage, ); my %Data = ( Name => $DynamicFieldConfig->{Name}, Label => $DynamicFieldHTML->{Label}, Content => $DynamicFieldHTML->{Field}, ); $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:DynamicField', Data => \%Data, ); if ( $Param{DescriptionShort} ) { $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:DynamicField:DescriptionShort', Data => { DescriptionShort => $Param{DescriptionShort}, }, ); } if ( $Param{DescriptionLong} ) { $LayoutObject->Block( Name => 'rw:DynamicField:DescriptionLong', Data => { DescriptionLong => $Param{DescriptionLong}, }, ); } return { Success => 1, HTML => $LayoutObject->Output( TemplateFile => 'ProcessManagement/DynamicField' ), }; } sub _RenderTitle { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Needed (qw(FormID)) { if ( !$Param{$Needed} ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_RenderTitle' ), }; } } if ( !IsHashRefWithData( $Param{ActivityDialogField} ) ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', 'ActivityDialogField', '_RenderTitle' ), }; } my %Data = ( Label => $LayoutObject->{LanguageObject}->Translate("Title"), FieldID => 'Title', FormID => $Param{FormID}, Value => $Param{GetParam}{Title}, Name => 'Title', MandatoryClass => '', ValidateRequired => '', ); # If field is required put in the necessary variables for # ValidateRequired class input field, Mandatory class for the label if ( $Param{ActivityDialogField}->{Display} && $Param{ActivityDialogField}->{Display} == 2 ) { $Data{ValidateRequired} = 'Validate_Required'; $Data{MandatoryClass} = 'Mandatory'; } # output server errors if ( IsHashRefWithData( $Param{Error} ) && $Param{Error}->{'Title'} ) { $Data{ServerError} = 'ServerError'; } $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:Title', Data => \%Data, ); # set mandatory label marker if ( $Data{MandatoryClass} && $Data{MandatoryClass} ne '' ) { $LayoutObject->Block( Name => 'LabelSpan', Data => {}, ); } if ( $Param{DescriptionShort} ) { $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:Title:DescriptionShort', Data => { DescriptionShort => $Param{DescriptionShort}, }, ); } if ( $Param{DescriptionLong} ) { $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:Title:DescriptionLong', Data => { DescriptionLong => $Param{DescriptionLong}, }, ); } return { Success => 1, HTML => $LayoutObject->Output( TemplateFile => 'ProcessManagement/Title' ), }; } sub _RenderArticle { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Needed (qw(FormID Ticket)) { if ( !$Param{$Needed} ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_RenderArticle' ), }; } } if ( !IsHashRefWithData( $Param{ActivityDialogField} ) ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', 'ActivityDialogField', '_RenderArticle' ), }; } # get all attachments meta data my @Attachments = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDGetAllFilesMeta( FormID => $Self->{FormID}, ); # show attachments ATTACHMENT: for my $Attachment (@Attachments) { if ( $Attachment->{ContentID} && $LayoutObject->{BrowserRichText} && ( $Attachment->{ContentType} =~ /image/i ) && ( $Attachment->{Disposition} eq 'inline' ) ) { next ATTACHMENT; } push @{ $Param{AttachmentList} }, $Attachment; } my %Data = ( Name => 'Article', MandatoryClass => '', ValidateRequired => '', Subject => $Param{GetParam}{Subject}, Body => $Param{GetParam}{Body}, LabelSubject => $Param{ActivityDialogField}->{Config}->{LabelSubject} || $LayoutObject->{LanguageObject}->Translate("Subject"), LabelBody => $Param{ActivityDialogField}->{Config}->{LabelBody} || $LayoutObject->{LanguageObject}->Translate("Text"), AttachmentList => $Param{AttachmentList}, ); # If field is required put in the necessary variables for # ValidateRequired class input field, Mandatory class for the label if ( $Param{ActivityDialogField}->{Display} && $Param{ActivityDialogField}->{Display} == 2 ) { $Data{ValidateRequired} = 'Validate_Required'; $Data{MandatoryClass} = 'Mandatory'; } # output server errors if ( IsHashRefWithData( $Param{Error} ) && $Param{Error}->{'ArticleSubject'} ) { $Data{SubjectServerError} = 'ServerError'; } if ( IsHashRefWithData( $Param{Error} ) && $Param{Error}->{'ArticleBody'} ) { $Data{BodyServerError} = 'ServerError'; } $LayoutObject->Block( Name => $Param{ActivityDialogField}->{LayoutBlock} || 'rw:Article', Data => \%Data, ); # set mandatory label marker if ( $Data{MandatoryClass} && $Data{MandatoryClass} ne '' ) { $LayoutObject->Block( Name => 'LabelSpanSubject', Data => {}, ); $LayoutObject->Block( Name => 'LabelSpanBody', Data => {}, ); } # add rich text editor if ( $LayoutObject->{BrowserRichText} ) { # use height/width defined for this screen $Param{RichTextHeight} = $Self->{Config}->{RichTextHeight} || 0; $Param{RichTextWidth} = $Self->{Config}->{RichTextWidth} || 0; # set up customer rich text editor $LayoutObject->CustomerSetRichTextParameters( Data => \%Param, ); } if ( $Param{DescriptionShort} ) { $LayoutObject->Block( Name => 'rw:Article:DescriptionShort', Data => { DescriptionShort => $Param{DescriptionShort}, }, ); } if ( $Param{DescriptionLong} ) { $LayoutObject->Block( Name => 'rw:Article:DescriptionLong', Data => { DescriptionLong => $Param{DescriptionLong}, }, ); } if ( $Param{InformAgents} ) { my %ShownUsers; my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList( Type => 'Long', Valid => 1, ); my $GID = $Kernel::OM->Get('Kernel::System::Queue')->GetQueueGroupID( QueueID => $Param{Ticket}->{QueueID} ); my %MemberList = $Kernel::OM->Get('Kernel::System::Group')->GroupMemberList( GroupID => $GID, Type => 'note', Result => 'HASH', Cached => 1, ); for my $UserID ( sort keys %MemberList ) { $ShownUsers{$UserID} = $AllGroupsMembers{$UserID}; } $Param{OptionStrg} = $LayoutObject->BuildSelection( Data => \%ShownUsers, SelectedID => '', Name => 'InformUserID', Multiple => 1, Size => 3, Class => 'Modernize', ); $LayoutObject->Block( Name => 'rw:Article:InformAgent', Data => \%Param, ); } return { Success => 1, HTML => $LayoutObject->Output( TemplateFile => 'ProcessManagement/Article' ), }; } sub _RenderCustomer { my ( $Self, %Param ) = @_; # get layout object my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); for my $Needed (qw(FormID)) { if ( !$Param{$Needed} ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', $Needed, '_RenderResponsible' ), }; } } if ( !IsHashRefWithData( $Param{ActivityDialogField} ) ) { return { Success => 0, Message => $LayoutObject->{LanguageObject} ->Translate( 'Parameter %s is missing in %s.', 'ActivityDialogField', '_RenderCustomer' ), }; } my %CustomerUserData = (); my $SubmittedCustomerUserID = $Param{GetParam}{CustomerUserID}; my %Data = ( LabelCustomerUser => $LayoutObject->{LanguageObject}->Translate("Customer user"), LabelCustomerID => $LayoutObject->{LanguageObject}->Translate("CustomerID"), FormID => $Param{FormID}, MandatoryClass => '', ValidateRequired => '', ); # If field is required put in the necessary variables for # ValidateRequired class input field, Mandatory class for the label if ( $Param{ActivityDialogField}->{Display} && $Param{ActivityDialogField}->{Display} == 2 ) { $Data{ValidateRequired} = 'Validate_Required'; $Data{MandatoryClass} = 'Mandatory'; } # output server errors if ( IsHashRefWithData( $Param{Error} ) && $Param{Error}->{CustomerUserID} ) { $Data{CustomerUserIDServerError} = 'ServerError'; } if ( IsHashRefWithData( $Param{Error} ) && $Param{Error}->{CustomerID} ) { $Data{CustomerIDServerError} = 'ServerError'; } if ( ( IsHashRefWithData( $Param{Ticket} ) && $Param{Ticket}->{CustomerUserID} ) || $SubmittedCustomerUserID ) { %CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet( User => $SubmittedCustomerUserID || $Param{Ticket}{CustomerUserID}, ); } # show customer field as "FirstName Lastname"