# -- # 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::CustomerTicketMessage; use strict; use warnings; our $ObjectManagerDisabled = 1; use Kernel::System::VariableCheck qw(:all); use Kernel::Language qw(Translatable); sub new { my ( $Type, %Param ) = @_; # allocate new hash for object my $Self = {%Param}; bless( $Self, $Type ); # get form id $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormID' ); # create form id if ( !$Self->{FormID} ) { $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(); } return $Self; } sub Run { my ( $Self, %Param ) = @_; # get params my %GetParam; my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); for my $Key (qw( Subject Body PriorityID TypeID ServiceID SLAID Expand Dest FromChatID)) { $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); } # ACL compatibility translation my %ACLCompatGetParam; $ACLCompatGetParam{OwnerID} = $GetParam{NewUserID}; # get Dynamic fields from ParamObject my %DynamicFieldValues; my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}"); # get the dynamic fields for this screen my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => [ 'Ticket', 'Article' ], FieldFilter => $Config->{DynamicField} || {}, ); my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); # reduce the dynamic fields to only the ones that are designed 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; # 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 %DynamicFieldACLParameters; DYNAMICFIELD: for my $DynamicField ( sort keys %DynamicFieldValues ) { next DYNAMICFIELD if !$DynamicField; next DYNAMICFIELD if !$DynamicFieldValues{$DynamicField}; $DynamicFieldACLParameters{ 'DynamicField_' . $DynamicField } = $DynamicFieldValues{$DynamicField}; } $GetParam{DynamicField} = \%DynamicFieldACLParameters; my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); if ( $GetParam{FromChatID} ) { if ( !$ConfigObject->Get('ChatEngine::Active') ) { return $LayoutObject->FatalError( Message => Translatable('Chat is not active.'), ); } # Check chat participant my %ChatParticipant = $Kernel::OM->Get('Kernel::System::Chat')->ChatParticipantCheck( ChatID => $GetParam{FromChatID}, ChatterType => 'Customer', ChatterID => $Self->{UserID}, ); if ( !%ChatParticipant ) { return $LayoutObject->FatalError( Message => Translatable('No permission.'), ); } } if ( !$Self->{Subaction} ) { #Get default Queue ID if none is set my $QueueDefaultID; if ( !$GetParam{Dest} && !$Param{ToSelected} ) { my $QueueDefault = $Config->{'QueueDefault'} || ''; if ($QueueDefault) { $QueueDefaultID = $QueueObject->QueueLookup( Queue => $QueueDefault ); if ($QueueDefaultID) { $Param{ToSelected} = $QueueDefaultID . '||' . $QueueDefault; } $ACLCompatGetParam{QueueID} = $QueueDefaultID; } # warn if there is no (valid) default queue and the customer can't select one elsif ( !$Config->{'Queue'} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Check SysConfig setting for %s::QueueDefault.', $Self->{Action} ), Comment => Translatable('Please contact the administrator.'), ); return; } } elsif ( $GetParam{Dest} ) { my ( $QueueIDParam, $QueueParam ) = split( /\|\|/, $GetParam{Dest} ); my $QueueIDLookup = $QueueObject->QueueLookup( Queue => $QueueParam ); if ( $QueueIDLookup && $QueueIDLookup eq $QueueIDParam ) { my $CustomerPanelOwnSelection = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanelOwnSelection'); if ( %{ $CustomerPanelOwnSelection // {} } ) { $Param{ToSelected} = $QueueIDParam . '||' . $CustomerPanelOwnSelection->{$QueueParam}; } else { $Param{ToSelected} = $GetParam{Dest}; } $ACLCompatGetParam{QueueID} = $QueueIDLookup; } } # create html strings for all dynamic fields my %DynamicFieldHTML; # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $PossibleValuesFilter; my $IsACLReducible = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); if ($IsACLReducible) { # get PossibleValues my $PossibleValues = $BackendObject->PossibleValuesGet( DynamicFieldConfig => $DynamicFieldConfig, ); # 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 $ACL = $TicketObject->TicketAcl( %GetParam, %ACLCompatGetParam, Action => $Self->{Action}, TicketID => $Self->{TicketID}, 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; } } } # get field html $DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldRender( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, Mandatory => $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, LayoutObject => $LayoutObject, ParamObject => $ParamObject, AJAXUpdate => 1, UpdatableFields => $Self->_GetFieldsToUpdate(), ); } # print form ... my $Output = $LayoutObject->CustomerHeader(); $Output .= $LayoutObject->CustomerNavigationBar(); $Output .= $Self->_MaskNew( %GetParam, %ACLCompatGetParam, ToSelected => $Param{ToSelected}, DynamicFieldHTML => \%DynamicFieldHTML, FromChatID => $GetParam{FromChatID} || '', ); $Output .= $LayoutObject->CustomerFooter(); return $Output; } elsif ( $Self->{Subaction} eq 'StoreNew' ) { my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Internal' ); my $NextScreen = $Config->{NextScreenAfterNewTicket}; my %Error; # get destination queue my $Dest = $ParamObject->GetParam( Param => 'Dest' ) || ''; my ( $NewQueueID, $To ) = split( /\|\|/, $Dest ); if ( !$To ) { $NewQueueID = $ParamObject->GetParam( Param => 'NewQueueID' ) || ''; $To = 'System'; } # fallback, if no destination is given if ( !$NewQueueID ) { my $Queue = $ParamObject->GetParam( Param => 'Queue' ) || $Config->{'QueueDefault'} || ''; if ($Queue) { my $QueueID = $QueueObject->QueueLookup( Queue => $Queue ); $NewQueueID = $QueueID; $To = $Queue; } } $GetParam{NewQueueID} = $NewQueueID; # use default if ticket type is not available in screen but activated on system if ( $ConfigObject->Get('Ticket::Type') && !$Config->{'TicketType'} ) { my %TypeList = reverse $TicketObject->TicketTypeList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); $GetParam{TypeID} = $TypeList{ $Config->{'TicketTypeDefault'} }; if ( !$GetParam{TypeID} ) { $LayoutObject->CustomerFatalError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Check SysConfig setting for %s::TicketTypeDefault.', $Self->{Action} ), Comment => Translatable('Please contact the administrator.'), ); return; } } my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); # get all attachments meta data my @Attachments = $UploadCacheObject->FormIDGetAllFilesMeta( FormID => $Self->{FormID}, ); # create html strings for all dynamic fields my %DynamicFieldHTML; # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $PossibleValuesFilter; my $IsACLReducible = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); if ($IsACLReducible) { # get PossibleValues my $PossibleValues = $BackendObject->PossibleValuesGet( DynamicFieldConfig => $DynamicFieldConfig, ); # 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 $ACL = $TicketObject->TicketAcl( %GetParam, Action => $Self->{Action}, TicketID => $Self->{TicketID}, 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 $ValidationResult; # do not validate on attachment upload or GetParam Expand if ( !$GetParam{Expand} ) { $ValidationResult = $BackendObject->EditFieldValueValidate( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, ParamObject => $ParamObject, Mandatory => $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, ); if ( !IsHashRefWithData($ValidationResult) ) { my $Output = $LayoutObject->CustomerHeader( Title => Translatable('Error'), ); $Output .= $LayoutObject->CustomerError( Message => $LayoutObject->{LanguageObject} ->Translate( 'Could not perform validation on field %s!', $DynamicFieldConfig->{Label} ), Comment => Translatable('Please contact the administrator.'), ); $Output .= $LayoutObject->CustomerFooter(); return $Output; } # propagate validation error to the Error variable to be detected by the frontend if ( $ValidationResult->{ServerError} ) { $Error{ $DynamicFieldConfig->{Name} } = ' ServerError'; } } # get field html $DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = $BackendObject->EditFieldRender( DynamicFieldConfig => $DynamicFieldConfig, PossibleValuesFilter => $PossibleValuesFilter, Mandatory => $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, ServerError => $ValidationResult->{ServerError} || '', ErrorMessage => $ValidationResult->{ErrorMessage} || '', LayoutObject => $LayoutObject, ParamObject => $ParamObject, AJAXUpdate => 1, UpdatableFields => $Self->_GetFieldsToUpdate(), ); } # rewrap body if no rich text is used if ( $GetParam{Body} && !$LayoutObject->{BrowserRichText} ) { $GetParam{Body} = $LayoutObject->WrapPlainText( MaxCharacters => $ConfigObject->Get('Ticket::Frontend::TextAreaNote'), PlainText => $GetParam{Body}, ); } # if there is FromChatID, get related messages and prepend them to body if ( $GetParam{FromChatID} ) { my @ChatMessages = $Kernel::OM->Get('Kernel::System::Chat')->ChatMessageList( ChatID => $GetParam{FromChatID}, ); for my $Message (@ChatMessages) { $Message->{MessageText} = $LayoutObject->Ascii2Html( Text => $Message->{MessageText}, LinkFeature => 1, ); } } # check queue if ( !$NewQueueID && !$GetParam{Expand} ) { $Error{QueueInvalid} = 'ServerError'; } # prevent tamper with (Queue/Dest), see bug#9408 if ($NewQueueID) { # get the original list of queues to display my $Tos = $Self->_GetTos( %GetParam, %ACLCompatGetParam, QueueID => $NewQueueID, ); # check if current selected QueueID exists in the list of queues,\ # otherwise rise an error if ( !$Tos->{$NewQueueID} ) { $Error{QueueInvalid} = 'ServerError'; } # set the correct queue name in $To if it was altered if ( $To ne $Tos->{$NewQueueID} ) { $To = $Tos->{$NewQueueID}; } } # check subject if ( !$GetParam{Subject} ) { $Error{SubjectInvalid} = 'ServerError'; } # check body if ( !$GetParam{Body} ) { $Error{BodyInvalid} = 'ServerError'; } if ( $GetParam{Expand} ) { %Error = (); $Error{Expand} = 1; } # check mandatory service if ( $ConfigObject->Get('Ticket::Service') && $Config->{Service} && $Config->{ServiceMandatory} && !$GetParam{ServiceID} ) { $Error{'ServiceIDInvalid'} = 'ServerError'; } # check mandatory sla if ( $ConfigObject->Get('Ticket::Service') && $Config->{SLA} && $Config->{SLAMandatory} && !$GetParam{SLAID} ) { $Error{'SLAIDInvalid'} = 'ServerError'; } # check type if ( $ConfigObject->Get('Ticket::Type') && !$GetParam{TypeID} && !$GetParam{Expand} ) { $Error{TypeIDInvalid} = 'ServerError'; } if (%Error) { # html output my $Output = $LayoutObject->CustomerHeader(); $Output .= $LayoutObject->CustomerNavigationBar(); $Output .= $Self->_MaskNew( Attachments => \@Attachments, %GetParam, ToSelected => $Dest, QueueID => $NewQueueID, DynamicFieldHTML => \%DynamicFieldHTML, Errors => \%Error, ); $Output .= $LayoutObject->CustomerFooter(); return $Output; } # challenge token check for write action $LayoutObject->ChallengeTokenCheck( Type => 'Customer' ); # if customer is not allowed to set priority, set it to default if ( !$Config->{Priority} ) { $GetParam{PriorityID} = ''; $GetParam{Priority} = $Config->{PriorityDefault}; } # create new ticket, do db insert my $TicketID = $TicketObject->TicketCreate( QueueID => $NewQueueID, TypeID => $GetParam{TypeID}, ServiceID => $GetParam{ServiceID}, SLAID => $GetParam{SLAID}, Title => $GetParam{Subject}, PriorityID => $GetParam{PriorityID}, Priority => $GetParam{Priority}, Lock => 'unlock', State => $Config->{StateDefault}, CustomerID => $Self->{UserCustomerID}, CustomerUser => $Self->{UserLogin}, OwnerID => $ConfigObject->Get('CustomerPanelUserID'), UserID => $ConfigObject->Get('CustomerPanelUserID'), ); # set ticket dynamic fields # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Ticket'; # set the value my $Success = $BackendObject->ValueSet( DynamicFieldConfig => $DynamicFieldConfig, ObjectID => $TicketID, Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, UserID => $ConfigObject->Get('CustomerPanelUserID'), ); } my $MimeType = 'text/plain'; if ( $LayoutObject->{BrowserRichText} ) { $MimeType = 'text/html'; # verify html document $GetParam{Body} = $LayoutObject->RichTextDocumentComplete( String => $GetParam{Body}, ); } my $PlainBody = $GetParam{Body}; if ( $LayoutObject->{BrowserRichText} ) { $PlainBody = $LayoutObject->RichText2Ascii( String => $GetParam{Body} ); } # create article my $FullName = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerName( UserLogin => $Self->{UserLogin}, ); my $From = "\"$FullName\" <$Self->{UserEmail}>"; my $ArticleID = $ArticleBackendObject->ArticleCreate( TicketID => $TicketID, IsVisibleForCustomer => 1, SenderType => $Config->{SenderType}, From => $From, To => $To, Subject => $GetParam{Subject}, Body => $GetParam{Body}, MimeType => $MimeType, Charset => $LayoutObject->{UserCharset}, UserID => $ConfigObject->Get('CustomerPanelUserID'), HistoryType => $Config->{HistoryType}, HistoryComment => $Config->{HistoryComment} || '%%', AutoResponseType => ( $ConfigObject->Get('AutoResponseForWebTickets') ) ? 'auto reply' : '', OrigHeader => { From => $From, To => $Self->{UserLogin}, Subject => $GetParam{Subject}, Body => $PlainBody, }, Queue => $QueueObject->QueueLookup( QueueID => $NewQueueID ), ); if ( !$ArticleID ) { my $Output = $LayoutObject->CustomerHeader( Title => Translatable('Error'), ); $Output .= $LayoutObject->CustomerError(); $Output .= $LayoutObject->CustomerFooter(); return $Output; } # set article dynamic fields # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Article'; # set the value my $Success = $BackendObject->ValueSet( DynamicFieldConfig => $DynamicFieldConfig, ObjectID => $ArticleID, Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, UserID => $ConfigObject->Get('CustomerPanelUserID'), ); } # Permissions check were done earlier if ( $GetParam{FromChatID} ) { my $ChatObject = $Kernel::OM->Get('Kernel::System::Chat'); my %Chat = $ChatObject->ChatGet( ChatID => $GetParam{FromChatID}, ); my @ChatMessageList = $ChatObject->ChatMessageList( ChatID => $GetParam{FromChatID}, ); my $ChatArticleID; if (@ChatMessageList) { for my $Message (@ChatMessageList) { $Message->{MessageText} = $LayoutObject->Ascii2Html( Text => $Message->{MessageText}, LinkFeature => 1, ); } my $ArticleChatBackend = $ArticleObject->BackendForChannel( ChannelName => 'Chat' ); $ChatArticleID = $ArticleChatBackend->ArticleCreate( TicketID => $TicketID, SenderType => $Config->{SenderType}, ChatMessageList => \@ChatMessageList, IsVisibleForCustomer => 1, UserID => $ConfigObject->Get('CustomerPanelUserID'), HistoryType => $Config->{HistoryType}, HistoryComment => $Config->{HistoryComment} || '%%', ); } if ($ChatArticleID) { $ChatObject->ChatDelete( ChatID => $GetParam{FromChatID}, ); } } # get pre loaded attachment my @AttachmentData = $UploadCacheObject->FormIDGetAllFilesData( FormID => $Self->{FormID}, ); # get submitted attachment my %UploadStuff = $ParamObject->GetUploadAll( Param => 'file_upload', ); if (%UploadStuff) { push @AttachmentData, \%UploadStuff; } # write attachments ATTACHMENT: for my $Attachment (@AttachmentData) { # skip, deleted not used inline images my $ContentID = $Attachment->{ContentID}; if ( $ContentID && ( $Attachment->{ContentType} =~ /image/i ) && ( $Attachment->{Disposition} eq 'inline' ) ) { my $ContentIDHTMLQuote = $LayoutObject->Ascii2Html( Text => $ContentID, ); # workaround for link encode of rich text editor, see bug#5053 my $ContentIDLinkEncode = $LayoutObject->LinkEncode($ContentID); $GetParam{Body} =~ s/(ContentID=)$ContentIDLinkEncode/$1$ContentID/g; # ignore attachment if not linked in body next ATTACHMENT if $GetParam{Body} !~ /(\Q$ContentIDHTMLQuote\E|\Q$ContentID\E)/i; } # write existing file to backend $ArticleBackendObject->ArticleWriteAttachment( %{$Attachment}, ArticleID => $ArticleID, UserID => $ConfigObject->Get('CustomerPanelUserID'), ); } # remove pre submitted attachments $UploadCacheObject->FormIDRemove( FormID => $Self->{FormID} ); # redirect return $LayoutObject->Redirect( OP => "Action=$NextScreen;TicketID=$TicketID", ); } elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) { my $Dest = $ParamObject->GetParam( Param => 'Dest' ) || ''; my $CustomerUser = $Self->{UserID}; my $QueueID = ''; if ( $Dest =~ /^(\d{1,100})\|\|.+?$/ ) { $QueueID = $1; } # get list type my $TreeView = 0; if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { $TreeView = 1; } my $Tos = $Self->_GetTos( %GetParam, %ACLCompatGetParam, QueueID => $QueueID, ); my $NewTos; if ($Tos) { TOs: for my $KeyTo ( sort keys %{$Tos} ) { next TOs if ( $Tos->{$KeyTo} eq '-' ); $NewTos->{"$KeyTo||$Tos->{$KeyTo}"} = $Tos->{$KeyTo}; } } my $Priorities = $Self->_GetPriorities( %GetParam, %ACLCompatGetParam, CustomerUserID => $CustomerUser || '', QueueID => $QueueID || 1, ); my $Services = $Self->_GetServices( %GetParam, %ACLCompatGetParam, CustomerUserID => $CustomerUser || '', QueueID => $QueueID || 1, ); my $SLAs = $Self->_GetSLAs( %GetParam, %ACLCompatGetParam, CustomerUserID => $CustomerUser || '', QueueID => $QueueID || 1, Services => $Services, ); my $Types = $Self->_GetTypes( %GetParam, %ACLCompatGetParam, CustomerUserID => $CustomerUser || '', QueueID => $QueueID || 1, ); # update Dynamic Fields Possible Values via AJAX my @DynamicFieldAJAX; # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsACLReducible = $BackendObject->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); next DYNAMICFIELD 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; # set possible values filter from ACLs my $ACL = $TicketObject->TicketAcl( %GetParam, %ACLCompatGetParam, Action => $Self->{Action}, QueueID => $QueueID || 0, 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 %{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter; } my $DataValues = $BackendObject->BuildSelectionDataGet( DynamicFieldConfig => $DynamicFieldConfig, PossibleValues => $PossibleValues, Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, ) || $PossibleValues; # add dynamic field to the list of fields to update push( @DynamicFieldAJAX, { Name => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => $DataValues, SelectedID => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, Translation => $DynamicFieldConfig->{Config}->{TranslatableValues} || 0, Max => 100, } ); } my $JSON = $LayoutObject->BuildSelectionJSON( [ { Name => 'Dest', Data => $NewTos, SelectedID => $Dest, Translation => 0, PossibleNone => 1, TreeView => $TreeView, Max => 100, }, { Name => 'PriorityID', Data => $Priorities, SelectedID => $GetParam{PriorityID}, Translation => 1, Max => 100, }, { Name => 'ServiceID', Data => $Services, SelectedID => $GetParam{ServiceID}, PossibleNone => 1, Translation => 0, TreeView => $TreeView, Max => 100, }, { Name => 'SLAID', Data => $SLAs, SelectedID => $GetParam{SLAID}, PossibleNone => 1, Translation => 0, Max => 100, }, { Name => 'TypeID', Data => $Types, SelectedID => $GetParam{TypeID}, PossibleNone => 1, Translation => 0, Max => 100, }, @DynamicFieldAJAX, ], ); return $LayoutObject->Attachment( ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, Content => $JSON, Type => 'inline', NoCache => 1, ); } else { return $LayoutObject->ErrorScreen( Message => Translatable('No Subaction!'), Comment => Translatable('Please contact the administrator.'), ); } } sub _GetPriorities { my ( $Self, %Param ) = @_; # get priority my %Priorities; if ( $Param{QueueID} || $Param{TicketID} ) { %Priorities = $Kernel::OM->Get('Kernel::System::Ticket')->TicketPriorityList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } return \%Priorities; } sub _GetTypes { my ( $Self, %Param ) = @_; # get type my %Type; if ( $Param{QueueID} || $Param{TicketID} ) { %Type = $Kernel::OM->Get('Kernel::System::Ticket')->TicketTypeList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } return \%Type; } sub _GetServices { my ( $Self, %Param ) = @_; # get service my %Service; # check needed return \%Service if !$Param{QueueID} && !$Param{TicketID}; # get options for default services for unknown customers my $DefaultServiceUnknownCustomer = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service::Default::UnknownCustomer'); # get service list if ( $Param{CustomerUserID} || $DefaultServiceUnknownCustomer ) { %Service = $Kernel::OM->Get('Kernel::System::Ticket')->TicketServiceList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } return \%Service; } sub _GetSLAs { my ( $Self, %Param ) = @_; # get sla my %SLA; if ( $Param{ServiceID} && $Param{Services} && %{ $Param{Services} } ) { if ( $Param{Services}->{ $Param{ServiceID} } ) { %SLA = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSLAList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } } return \%SLA; } sub _GetTos { my ( $Self, %Param ) = @_; # check own selection my %NewTos = ( '', '-' ); my $Module = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanel::NewTicketQueueSelectionModule') || 'Kernel::Output::HTML::CustomerNewTicket::QueueSelectionGeneric'; if ( $Kernel::OM->Get('Kernel::System::Main')->Require($Module) ) { my $Object = $Module->new( %{$Self}, SystemAddress => $Kernel::OM->Get('Kernel::System::SystemAddress'), Debug => $Self->{Debug}, ); # log loaded module if ( $Self->{Debug} && $Self->{Debug} > 1 ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => "Module: $Module loaded!", ); } %NewTos = ( $Object->Run( Env => $Self, ACLParams => \%Param ), ( '', => '-' ) ); } else { return $Kernel::OM->Get('Kernel::Output::HTML::Layout')->FatalDie( Message => "Could not load $Module!", ); } return \%NewTos; } sub _MaskNew { my ( $Self, %Param ) = @_; $Param{FormID} = $Self->{FormID}; $Param{Errors}->{QueueInvalid} = $Param{Errors}->{QueueInvalid} || ''; my $DynamicFieldNames = $Self->_GetFieldsToUpdate( OnlyDynamicFields => 1, ); my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); # get list type my $TreeView = 0; if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { $TreeView = 1; } my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}"); if ( $Config->{Queue} ) { # check own selection my %NewTos = ( '', '-' ); my $Module = $ConfigObject->Get('CustomerPanel::NewTicketQueueSelectionModule') || 'Kernel::Output::HTML::CustomerNewTicket::QueueSelectionGeneric'; if ( $Kernel::OM->Get('Kernel::System::Main')->Require($Module) ) { my $Object = $Module->new( %{$Self}, SystemAddress => $Kernel::OM->Get('Kernel::System::SystemAddress'), Debug => $Self->{Debug}, ); # log loaded module if ( $Self->{Debug} && $Self->{Debug} > 1 ) { $Kernel::OM->Get('Kernel::System::Log')->Log( Priority => 'debug', Message => "Module: $Module loaded!", ); } %NewTos = ( $Object->Run( Env => $Self, ACLParams => \%Param ), ( '', => '-' ) ); } else { return $LayoutObject->FatalError(); } # build to string if (%NewTos) { for ( sort keys %NewTos ) { $NewTos{"$_||$NewTos{$_}"} = $NewTos{$_}; delete $NewTos{$_}; } } $Param{ToStrg} = $LayoutObject->AgentQueueListOption( Data => \%NewTos, Multiple => 0, Size => 0, Name => 'Dest', Class => "Validate_Required Modernize " . $Param{Errors}->{QueueInvalid}, SelectedID => $Param{ToSelected} || $Param{QueueID}, TreeView => $TreeView, ); $LayoutObject->Block( Name => 'Queue', Data => { %Param, QueueInvalid => $Param{Errors}->{QueueInvalid}, }, ); } my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); # get priority if ( $Config->{Priority} ) { my %Priorities = $TicketObject->TicketPriorityList( %Param, CustomerUserID => $Self->{UserID}, Action => $Self->{Action}, ); # build priority string my %PrioritySelected; if ( $Param{PriorityID} ) { $PrioritySelected{SelectedID} = $Param{PriorityID}; } else { $PrioritySelected{SelectedValue} = $Config->{PriorityDefault} || '3 normal'; } $Param{PriorityStrg} = $LayoutObject->BuildSelection( Data => \%Priorities, Name => 'PriorityID', Class => 'Modernize', %PrioritySelected, ); $LayoutObject->Block( Name => 'Priority', Data => \%Param, ); } # types if ( $ConfigObject->Get('Ticket::Type') && $Config->{'TicketType'} ) { my %Type = $TicketObject->TicketTypeList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); if ( $Config->{'TicketTypeDefault'} && !$Param{TypeID} ) { my %ReverseType = reverse %Type; $Param{TypeID} = $ReverseType{ $Config->{'TicketTypeDefault'} }; } $Param{TypeStrg} = $LayoutObject->BuildSelection( Data => \%Type, Name => 'TypeID', SelectedID => $Param{TypeID}, PossibleNone => 1, Sort => 'AlphanumericValue', Translation => 0, Class => "Validate_Required Modernize " . ( $Param{Errors}->{TypeIDInvalid} || '' ), ); $LayoutObject->Block( Name => 'TicketType', Data => { %Param, TypeIDInvalid => $Param{Errors}->{TypeIDInvalid}, } ); } # services if ( $ConfigObject->Get('Ticket::Service') && $Config->{Service} ) { my %Services; if ( $Param{QueueID} || $Param{TicketID} ) { %Services = $TicketObject->TicketServiceList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } $Param{ServiceStrg} = $LayoutObject->BuildSelection( Data => \%Services, Name => 'ServiceID', SelectedID => $Param{ServiceID}, Class => 'Modernize ' . ( $Config->{ServiceMandatory} ? 'Validate_Required ' : '' ) . ( $Param{Errors}->{ServiceIDInvalid} || '' ), PossibleNone => 1, TreeView => $TreeView, Sort => 'TreeView', Translation => 0, Max => 200, ); $LayoutObject->Block( Name => 'TicketService', Data => { ServiceMandatory => $Config->{ServiceMandatory} || 0, %Param, }, ); # reset previous ServiceID to reset SLA-List if no service is selected if ( !$Services{ $Param{ServiceID} || '' } ) { $Param{ServiceID} = ''; } my %SLA; if ( $Config->{SLA} ) { if ( $Param{ServiceID} ) { %SLA = $TicketObject->TicketSLAList( %Param, Action => $Self->{Action}, CustomerUserID => $Self->{UserID}, ); } $Param{SLAStrg} = $LayoutObject->BuildSelection( Data => \%SLA, Name => 'SLAID', SelectedID => $Param{SLAID}, Class => 'Modernize ' . ( $Config->{SLAMandatory} ? 'Validate_Required ' : '' ) . ( $Param{Errors}->{SLAInvalid} || '' ), PossibleNone => 1, Sort => 'AlphanumericValue', Translation => 0, Max => 200, ); $LayoutObject->Block( Name => 'TicketSLA', Data => { SLAMandatory => $Config->{SLAMandatory} || 0, %Param, } ); } } # prepare errors if ( $Param{Errors} ) { for ( sort keys %{ $Param{Errors} } ) { $Param{$_} = $Param{Errors}->{$_}; } } # get the dynamic fields for this screen my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => [ 'Ticket', 'Article' ], FieldFilter => $Config->{DynamicField} || {}, ); # reduce the dynamic fields to only the ones that are designed for customer interface my @CustomerDynamicFields; DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsCustomerInterfaceCapable = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsCustomerInterfaceCapable', ); next DYNAMICFIELD if !$IsCustomerInterfaceCapable; push @CustomerDynamicFields, $DynamicFieldConfig; } $DynamicField = \@CustomerDynamicFields; # Dynamic fields # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); # skip fields that HTML could not be retrieved next DYNAMICFIELD if !IsHashRefWithData( $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} } ); # get the html strings form $Param my $DynamicFieldHTML = $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} }; $LayoutObject->Block( Name => 'DynamicField', Data => { Name => $DynamicFieldConfig->{Name}, Label => $DynamicFieldHTML->{Label}, Field => $DynamicFieldHTML->{Field}, }, ); # example of dynamic fields order customization $LayoutObject->Block( Name => 'DynamicField_' . $DynamicFieldConfig->{Name}, Data => { Name => $DynamicFieldConfig->{Name}, Label => $DynamicFieldHTML->{Label}, Field => $DynamicFieldHTML->{Field}, }, ); } # show attachments ATTACHMENT: for my $Attachment ( @{ $Param{Attachments} } ) { if ( $Attachment->{ContentID} && $LayoutObject->{BrowserRichText} && ( $Attachment->{ContentType} =~ /image/i ) && ( $Attachment->{Disposition} eq 'inline' ) ) { next ATTACHMENT; } push @{ $Param{AttachmentList} }, $Attachment; } # add rich text editor if ( $LayoutObject->{BrowserRichText} ) { # use height/width defined for this screen $Param{RichTextHeight} = $Config->{RichTextHeight} || 0; $Param{RichTextWidth} = $Config->{RichTextWidth} || 0; # set up customer rich text editor $LayoutObject->CustomerSetRichTextParameters( Data => \%Param, ); } # Permissions have been checked before in Run() if ( $Param{FromChatID} ) { my @ChatMessages = $Kernel::OM->Get('Kernel::System::Chat')->ChatMessageList( ChatID => $Param{FromChatID}, ); for my $Message (@ChatMessages) { $Message->{MessageText} = $LayoutObject->Ascii2Html( Text => $Message->{MessageText}, LinkFeature => 1, ); } $LayoutObject->Block( Name => 'ChatArticlePreview', Data => { ChatMessages => \@ChatMessages, }, ); } # send data to JS $LayoutObject->AddJSData( Key => 'DynamicFieldNames', Value => $DynamicFieldNames, ); # get output back return $LayoutObject->Output( TemplateFile => 'CustomerTicketMessage', Data => \%Param, ); } sub _GetFieldsToUpdate { my ( $Self, %Param ) = @_; my @UpdatableFields; # set the fields that can be updatable via AJAXUpdate if ( !$Param{OnlyDynamicFields} ) { @UpdatableFields = qw( Dest ServiceID SLAID PriorityID ); } my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}"); # get the dynamic fields for this screen my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( Valid => 1, ObjectType => [ 'Ticket', 'Article' ], FieldFilter => $Config->{DynamicField} || {}, ); # cycle trough the activated Dynamic Fields for this screen DYNAMICFIELD: for my $DynamicFieldConfig ( @{$DynamicField} ) { next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); my $IsACLReducible = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior( DynamicFieldConfig => $DynamicFieldConfig, Behavior => 'IsACLReducible', ); next DYNAMICFIELD if !$IsACLReducible; push @UpdatableFields, 'DynamicField_' . $DynamicFieldConfig->{Name}; } return \@UpdatableFields; } 1;