Files
scripts/Perl OTRS/Kernel/Modules/CustomerTicketMessage.pm
2024-10-14 00:08:40 +02:00

1377 lines
48 KiB
Perl

# --
# 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;