2370 lines
79 KiB
Perl
2370 lines
79 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::GenericInterface::Operation::Ticket::TicketUpdate;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::System::VariableCheck qw( :all );
|
|
|
|
use parent qw(
|
|
Kernel::GenericInterface::Operation::Common
|
|
Kernel::GenericInterface::Operation::Ticket::Common
|
|
);
|
|
|
|
our $ObjectManagerDisabled = 1;
|
|
|
|
=head1 NAME
|
|
|
|
Kernel::GenericInterface::Operation::Ticket::TicketUpdate - GenericInterface Ticket TicketUpdate Operation backend
|
|
|
|
=head1 PUBLIC INTERFACE
|
|
|
|
=head2 new()
|
|
|
|
usually, you want to create an instance of this
|
|
by using Kernel::GenericInterface::Operation->new();
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
my $Self = {};
|
|
bless( $Self, $Type );
|
|
|
|
# check needed objects
|
|
for my $Needed (qw( DebuggerObject WebserviceID )) {
|
|
if ( !$Param{$Needed} ) {
|
|
return {
|
|
Success => 0,
|
|
ErrorMessage => "Got no $Needed!",
|
|
};
|
|
}
|
|
|
|
$Self->{$Needed} = $Param{$Needed};
|
|
}
|
|
|
|
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::TicketUpdate');
|
|
$Self->{Operation} = $Param{Operation};
|
|
|
|
return $Self;
|
|
}
|
|
|
|
=head2 Run()
|
|
|
|
perform TicketUpdate Operation. This will return the updated TicketID and
|
|
if applicable the created ArticleID.
|
|
|
|
my $Result = $OperationObject->Run(
|
|
Data => {
|
|
UserLogin => 'some agent login', # UserLogin or CustomerUserLogin or SessionID is
|
|
# required
|
|
CustomerUserLogin => 'some customer login',
|
|
SessionID => 123,
|
|
|
|
Password => 'some password', # if UserLogin or customerUserLogin is sent then
|
|
# Password is required
|
|
|
|
TicketID => 123, # TicketID or TicketNumber is required
|
|
TicketNumber => '2004040510440485',
|
|
|
|
Ticket { # optional
|
|
Title => 'some ticket title',
|
|
|
|
QueueID => 123, # Optional
|
|
Queue => 'some queue name', # Optional
|
|
LockID => 123, # optional
|
|
Lock => 'some lock name', # optional
|
|
TypeID => 123, # optional
|
|
Type => 'some type name', # optional
|
|
ServiceID => 123, # optional
|
|
Service => 'some service name', # optional
|
|
SLAID => 123, # optional
|
|
SLA => 'some SLA name', # optional
|
|
StateID => 123, # optional
|
|
State => 'some state name', # optional
|
|
PriorityID => 123, # optional
|
|
Priority => 'some priority name', # optional
|
|
OwnerID => 123, # optional
|
|
Owner => 'some user login', # optional
|
|
ResponsibleID => 123, # optional
|
|
Responsible => 'some user login', # optional
|
|
CustomerUser => 'some customer user login',
|
|
|
|
PendingTime { # optional
|
|
Year => 2011,
|
|
Month => 12
|
|
Day => 03,
|
|
Hour => 23,
|
|
Minute => 05,
|
|
},
|
|
# or
|
|
# PendingTime {
|
|
# Diff => 10080, # Pending time in minutes
|
|
#},
|
|
},
|
|
Article => { # optional
|
|
CommunicationChannel => 'Email', # CommunicationChannel or CommunicationChannelID must be provided.
|
|
CommunicationChannelID => 1,
|
|
IsVisibleForCustomer => 1, # optional
|
|
SenderTypeID => 123, # optional
|
|
SenderType => 'some sender type name', # optional
|
|
AutoResponseType => 'some auto response type', # optional
|
|
From => 'some from string', # optional
|
|
Subject => 'some subject',
|
|
Body => 'some body',
|
|
|
|
ContentType => 'some content type', # ContentType or MimeType and Charset is required
|
|
MimeType => 'some mime type',
|
|
Charset => 'some charset',
|
|
|
|
HistoryType => 'some history type', # optional
|
|
HistoryComment => 'Some history comment', # optional
|
|
TimeUnit => 123, # optional
|
|
NoAgentNotify => 1, # optional
|
|
ForceNotificationToUserID => [1, 2, 3] # optional
|
|
ExcludeNotificationToUserID => [1, 2, 3] # optional
|
|
ExcludeMuteNotificationToUserID => [1, 2, 3] # optional
|
|
},
|
|
|
|
DynamicField => [ # optional
|
|
{
|
|
Name => 'some name',
|
|
Value => $Value, # value type depends on the dynamic field
|
|
},
|
|
# ...
|
|
],
|
|
# or
|
|
# DynamicField {
|
|
# Name => 'some name',
|
|
# Value => $Value,
|
|
#},
|
|
|
|
Attachment [
|
|
{
|
|
Content => 'content' # base64 encoded
|
|
ContentType => 'some content type'
|
|
Filename => 'some fine name'
|
|
},
|
|
# ...
|
|
],
|
|
#or
|
|
#Attachment {
|
|
# Content => 'content'
|
|
# ContentType => 'some content type'
|
|
# Filename => 'some fine name'
|
|
#},
|
|
},
|
|
);
|
|
|
|
$Result = {
|
|
Success => 1, # 0 or 1
|
|
ErrorMessage => '', # in case of error
|
|
Data => { # result data payload after Operation
|
|
TicketID => 123, # Ticket ID number in OTRS (help desk system)
|
|
ArticleID => 43, # Article ID number in OTRS (help desk system)
|
|
Error => { # should not return errors
|
|
ErrorCode => 'TicketUpdate.ErrorCode'
|
|
ErrorMessage => 'Error Description'
|
|
},
|
|
|
|
# If IncludeTicketData is enabled
|
|
Ticket => [
|
|
{
|
|
TicketNumber => '20101027000001',
|
|
Title => 'some title',
|
|
TicketID => 123,
|
|
State => 'some state',
|
|
StateID => 123,
|
|
StateType => 'some state type',
|
|
Priority => 'some priority',
|
|
PriorityID => 123,
|
|
Lock => 'lock',
|
|
LockID => 123,
|
|
Queue => 'some queue',
|
|
QueueID => 123,
|
|
CustomerID => 'customer_id_123',
|
|
CustomerUserID => 'customer_user_id_123',
|
|
Owner => 'some_owner_login',
|
|
OwnerID => 123,
|
|
Type => 'some ticket type',
|
|
TypeID => 123,
|
|
SLA => 'some sla',
|
|
SLAID => 123,
|
|
Service => 'some service',
|
|
ServiceID => 123,
|
|
Responsible => 'some_responsible_login',
|
|
ResponsibleID => 123,
|
|
Age => 3456,
|
|
Created => '2010-10-27 20:15:00'
|
|
CreateBy => 123,
|
|
Changed => '2010-10-27 20:15:15',
|
|
ChangeBy => 123,
|
|
ArchiveFlag => 'y',
|
|
|
|
DynamicField => [
|
|
{
|
|
Name => 'some name',
|
|
Value => 'some value',
|
|
},
|
|
],
|
|
|
|
# (time stamps of expected escalations)
|
|
EscalationResponseTime (unix time stamp of response time escalation)
|
|
EscalationUpdateTime (unix time stamp of update time escalation)
|
|
EscalationSolutionTime (unix time stamp of solution time escalation)
|
|
|
|
# (general escalation info of nearest escalation type)
|
|
EscalationDestinationIn (escalation in e. g. 1h 4m)
|
|
EscalationDestinationTime (date of escalation in unix time, e. g. 72193292)
|
|
EscalationDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
|
|
EscalationTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
|
|
EscalationTime (seconds total till escalation of nearest escalation time type - response, update or solution time, e. g. "3600")
|
|
|
|
# (detailed escalation info about first response, update and solution time)
|
|
FirstResponseTimeEscalation (if true, ticket is escalated)
|
|
FirstResponseTimeNotification (if true, notify - x% of escalation has reached)
|
|
FirstResponseTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
|
|
FirstResponseTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
|
|
FirstResponseTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
|
|
FirstResponseTime (seconds total till escalation, e. g. "3600")
|
|
|
|
UpdateTimeEscalation (if true, ticket is escalated)
|
|
UpdateTimeNotification (if true, notify - x% of escalation has reached)
|
|
UpdateTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
|
|
UpdateTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
|
|
UpdateTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
|
|
UpdateTime (seconds total till escalation, e. g. "3600")
|
|
|
|
SolutionTimeEscalation (if true, ticket is escalated)
|
|
SolutionTimeNotification (if true, notify - x% of escalation has reached)
|
|
SolutionTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
|
|
SolutionTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
|
|
SolutionTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800")
|
|
SolutionTime (seconds total till escalation, e. g. "3600")
|
|
|
|
Article => [
|
|
{
|
|
ArticleID
|
|
From
|
|
To
|
|
Cc
|
|
Subject
|
|
Body
|
|
ReplyTo
|
|
MessageID
|
|
InReplyTo
|
|
References
|
|
SenderType
|
|
SenderTypeID
|
|
CommunicationChannelID
|
|
IsVisibleForCustomer
|
|
ContentType
|
|
Charset
|
|
MimeType
|
|
IncomingTime
|
|
|
|
DynamicField => [
|
|
{
|
|
Name => 'some name',
|
|
Value => 'some value',
|
|
},
|
|
],
|
|
|
|
Attachment => [
|
|
{
|
|
Content => "xxxx", # actual attachment contents, base64 enconded
|
|
ContentAlternative => "",
|
|
ContentID => "",
|
|
ContentType => "application/pdf",
|
|
Filename => "StdAttachment-Test1.pdf",
|
|
Filesize => "4.6 KBytes",
|
|
FilesizeRaw => 4722,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
=cut
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $Result = $Self->Init(
|
|
WebserviceID => $Self->{WebserviceID},
|
|
);
|
|
|
|
if ( !$Result->{Success} ) {
|
|
$Self->ReturnError(
|
|
ErrorCode => 'Webservice.InvalidConfiguration',
|
|
ErrorMessage => $Result->{ErrorMessage},
|
|
);
|
|
}
|
|
|
|
# check needed stuff
|
|
if ( !IsHashRefWithData( $Param{Data} ) ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.EmptyRequest',
|
|
ErrorMessage => "TicketUpdate: The request data is invalid!",
|
|
);
|
|
}
|
|
|
|
if ( !$Param{Data}->{TicketID} && !$Param{Data}->{TicketNumber} ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: TicketID or TicketNumber is required!",
|
|
);
|
|
}
|
|
|
|
if (
|
|
!$Param{Data}->{UserLogin}
|
|
&& !$Param{Data}->{CustomerUserLogin}
|
|
&& !$Param{Data}->{SessionID}
|
|
)
|
|
{
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: UserLogin, CustomerUserLogin or SessionID is required!",
|
|
);
|
|
}
|
|
|
|
if ( $Param{Data}->{UserLogin} || $Param{Data}->{CustomerUserLogin} ) {
|
|
|
|
if ( !$Param{Data}->{Password} )
|
|
{
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Password or SessionID is required!",
|
|
);
|
|
}
|
|
}
|
|
|
|
# authenticate user
|
|
my ( $UserID, $UserType ) = $Self->Auth(%Param);
|
|
|
|
if ( !$UserID ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.AuthFail',
|
|
ErrorMessage => "TicketUpdate: User could not be authenticated!",
|
|
);
|
|
}
|
|
|
|
my $PermissionUserID = $UserID;
|
|
|
|
if ( $UserType eq 'Customer' ) {
|
|
$UserID = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanelUserID');
|
|
}
|
|
|
|
# get ticket object
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# check TicketID
|
|
my $TicketID;
|
|
if ( $Param{Data}->{TicketNumber} ) {
|
|
$TicketID = $TicketObject->TicketIDLookup(
|
|
TicketNumber => $Param{Data}->{TicketNumber},
|
|
UserID => $UserID,
|
|
);
|
|
|
|
}
|
|
else {
|
|
$TicketID = $Param{Data}->{TicketID};
|
|
}
|
|
|
|
if ( !($TicketID) ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: User does not have access to the ticket!",
|
|
);
|
|
}
|
|
|
|
my %TicketData = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
UserID => $UserID,
|
|
);
|
|
|
|
if ( !IsHashRefWithData( \%TicketData ) ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: User does not have access to the ticket!",
|
|
);
|
|
}
|
|
|
|
# check basic needed permissions
|
|
my $Access = $Self->CheckAccessPermissions(
|
|
TicketID => $TicketID,
|
|
UserID => $PermissionUserID,
|
|
UserType => $UserType,
|
|
);
|
|
|
|
if ( !$Access ) {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: User does not have access to the ticket!",
|
|
);
|
|
}
|
|
|
|
# check optional hashes
|
|
for my $Optional (qw(Ticket Article)) {
|
|
if (
|
|
defined $Param{Data}->{$Optional}
|
|
&& !IsHashRefWithData( $Param{Data}->{$Optional} )
|
|
)
|
|
{
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: $Optional parameter is not valid!",
|
|
);
|
|
}
|
|
}
|
|
|
|
# check optional array/hashes
|
|
for my $Optional (qw(DynamicField Attachment)) {
|
|
if (
|
|
defined $Param{Data}->{$Optional}
|
|
&& !IsHashRefWithData( $Param{Data}->{$Optional} )
|
|
&& !IsArrayRefWithData( $Param{Data}->{$Optional} )
|
|
)
|
|
{
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: $Optional parameter is missing or not valid!",
|
|
);
|
|
}
|
|
}
|
|
|
|
my $Ticket;
|
|
if ( defined $Param{Data}->{Ticket} ) {
|
|
|
|
# isolate ticket parameter
|
|
$Ticket = $Param{Data}->{Ticket};
|
|
|
|
$Ticket->{UserID} = $UserID;
|
|
|
|
# remove leading and trailing spaces
|
|
for my $Attribute ( sort keys %{$Ticket} ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$Ticket->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$Ticket->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
if ( IsHashRefWithData( $Ticket->{PendingTime} ) ) {
|
|
for my $Attribute ( sort keys %{ $Ticket->{PendingTime} } ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$Ticket->{PendingTime}->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$Ticket->{PendingTime}->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
}
|
|
|
|
# check Ticket attribute values
|
|
my $TicketCheck = $Self->_CheckTicket(
|
|
Ticket => $Ticket,
|
|
OldTicket => \%TicketData,
|
|
);
|
|
|
|
if ( !$TicketCheck->{Success} ) {
|
|
return $Self->ReturnError( %{$TicketCheck} );
|
|
}
|
|
}
|
|
|
|
my $Article;
|
|
if ( defined $Param{Data}->{Article} ) {
|
|
|
|
$Article = $Param{Data}->{Article};
|
|
$Article->{UserType} = $UserType;
|
|
|
|
# remove leading and trailing spaces
|
|
for my $Attribute ( sort keys %{$Article} ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$Article->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$Article->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
if ( IsHashRefWithData( $Article->{OrigHeader} ) ) {
|
|
for my $Attribute ( sort keys %{ $Article->{OrigHeader} } ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$Article->{OrigHeader}->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$Article->{OrigHeader}->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check attributes that can be set by sysconfig.
|
|
if ( !$Article->{AutoResponseType} ) {
|
|
$Article->{AutoResponseType} = $Self->{Config}->{AutoResponseType} || '';
|
|
}
|
|
|
|
# TODO: GenericInterface::Operation::TicketUpdate###CommunicationChannel
|
|
if ( !$Article->{CommunicationChannelID} && !$Article->{CommunicationChannel} ) {
|
|
$Article->{CommunicationChannel} = 'Internal';
|
|
}
|
|
if ( !defined $Article->{IsVisibleForCustomer} ) {
|
|
$Article->{IsVisibleForCustomer} = $Self->{Config}->{IsVisibleForCustomer} // 1;
|
|
}
|
|
if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) {
|
|
$Article->{SenderType} = $UserType eq 'User' ? 'agent' : 'customer';
|
|
}
|
|
if ( !$Article->{HistoryType} ) {
|
|
$Article->{HistoryType} = $Self->{Config}->{HistoryType} || '';
|
|
}
|
|
if ( !$Article->{HistoryComment} ) {
|
|
$Article->{HistoryComment} = $Self->{Config}->{HistoryComment} || '';
|
|
}
|
|
|
|
# check Article attribute values
|
|
my $ArticleCheck = $Self->_CheckArticle( Article => $Article );
|
|
|
|
if ( !$ArticleCheck->{Success} ) {
|
|
if ( !$ArticleCheck->{ErrorCode} ) {
|
|
return {
|
|
Success => 0,
|
|
%{$ArticleCheck},
|
|
};
|
|
}
|
|
return $Self->ReturnError( %{$ArticleCheck} );
|
|
}
|
|
}
|
|
|
|
my $DynamicField;
|
|
my @DynamicFieldList;
|
|
if ( defined $Param{Data}->{DynamicField} ) {
|
|
|
|
# isolate DynamicField parameter
|
|
$DynamicField = $Param{Data}->{DynamicField};
|
|
|
|
# homogenate input to array
|
|
if ( ref $DynamicField eq 'HASH' ) {
|
|
push @DynamicFieldList, $DynamicField;
|
|
}
|
|
else {
|
|
@DynamicFieldList = @{$DynamicField};
|
|
}
|
|
|
|
# check DynamicField internal structure
|
|
for my $DynamicFieldItem (@DynamicFieldList) {
|
|
if ( !IsHashRefWithData($DynamicFieldItem) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->DynamicField parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# remove leading and trailing spaces
|
|
for my $Attribute ( sort keys %{$DynamicFieldItem} ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$DynamicFieldItem->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$DynamicFieldItem->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
|
|
# check DynamicField attribute values
|
|
my $DynamicFieldCheck = $Self->_CheckDynamicField(
|
|
DynamicField => $DynamicFieldItem,
|
|
Article => $Article,
|
|
);
|
|
|
|
if ( !$DynamicFieldCheck->{Success} ) {
|
|
return $Self->ReturnError( %{$DynamicFieldCheck} );
|
|
}
|
|
}
|
|
}
|
|
|
|
my $Attachment;
|
|
my @AttachmentList;
|
|
if ( defined $Param{Data}->{Attachment} ) {
|
|
|
|
# isolate Attachment parameter
|
|
$Attachment = $Param{Data}->{Attachment};
|
|
|
|
# homogenate input to array
|
|
if ( ref $Attachment eq 'HASH' ) {
|
|
push @AttachmentList, $Attachment;
|
|
}
|
|
else {
|
|
@AttachmentList = @{$Attachment};
|
|
}
|
|
|
|
# check Attachment internal structure
|
|
for my $AttachmentItem (@AttachmentList) {
|
|
if ( !IsHashRefWithData($AttachmentItem) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->Attachment parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# remove leading and trailing spaces
|
|
for my $Attribute ( sort keys %{$AttachmentItem} ) {
|
|
if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
|
|
|
|
#remove leading spaces
|
|
$AttachmentItem->{$Attribute} =~ s{\A\s+}{};
|
|
|
|
#remove trailing spaces
|
|
$AttachmentItem->{$Attribute} =~ s{\s+\z}{};
|
|
}
|
|
}
|
|
|
|
# check Attachment attribute values
|
|
my $AttachmentCheck = $Self->_CheckAttachment(
|
|
Attachment => $AttachmentItem,
|
|
Article => $Article,
|
|
);
|
|
|
|
if ( !$AttachmentCheck->{Success} ) {
|
|
return $Self->ReturnError( %{$AttachmentCheck} );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $Self->_TicketUpdate(
|
|
TicketID => $TicketID,
|
|
Ticket => $Ticket,
|
|
Article => $Article,
|
|
DynamicFieldList => \@DynamicFieldList,
|
|
AttachmentList => \@AttachmentList,
|
|
UserID => $UserID,
|
|
UserType => $UserType,
|
|
);
|
|
}
|
|
|
|
=begin Internal:
|
|
|
|
=head2 _CheckTicket()
|
|
|
|
checks if the given ticket parameters are valid.
|
|
|
|
my $TicketCheck = $OperationObject->_CheckTicket(
|
|
Ticket => $Ticket, # all ticket parameters
|
|
);
|
|
|
|
returns:
|
|
|
|
$TicketCheck = {
|
|
Success => 1, # if everything is OK
|
|
}
|
|
|
|
$TicketCheck = {
|
|
ErrorCode => 'Function.Error', # if error
|
|
ErrorMessage => 'Error description',
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _CheckTicket {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $Ticket = $Param{Ticket};
|
|
my $OldTicket = $Param{OldTicket};
|
|
|
|
# check Ticket->CustomerUser
|
|
if (
|
|
$Ticket->{CustomerUser}
|
|
&& !$Self->ValidateCustomer( %{$Ticket} )
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->CustomerUser parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# check Ticket->Queue
|
|
if ( $Ticket->{QueueID} || $Ticket->{Queue} ) {
|
|
if ( !$Self->ValidateQueue( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->QueueID or Ticket->Queue parameter is"
|
|
. " invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Lock
|
|
if ( $Ticket->{LockID} || $Ticket->{Lock} ) {
|
|
if ( !$Self->ValidateLock( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->LockID or Ticket->Lock parameter is"
|
|
. " invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Type
|
|
if ( $Ticket->{TypeID} || $Ticket->{Type} ) {
|
|
if ( !$Self->ValidateType( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->TypeID or Ticket->Type parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Service
|
|
if ( $Ticket->{ServiceID} || $Ticket->{Service} ) {
|
|
|
|
# set customer user from old ticket if no new customer user is to be set
|
|
my $CustomerUser = $Ticket->{CustomerUser} || '';
|
|
if ( !$CustomerUser ) {
|
|
$CustomerUser = $OldTicket->{CustomerUserID};
|
|
}
|
|
if (
|
|
!$Self->ValidateService(
|
|
%{$Ticket},
|
|
CustomerUser => $CustomerUser,
|
|
)
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->ServiceID or Ticket->Service parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->SLA
|
|
if ( $Ticket->{SLAID} || $Ticket->{SLA} ) {
|
|
|
|
# set ServiceID from old ticket if no new ServiceID or Service is to be set
|
|
my $Service = $Ticket->{Service} || '';
|
|
my $ServiceID = $Ticket->{ServiceID} || '';
|
|
if ( !$ServiceID && !$Service ) {
|
|
$ServiceID = $OldTicket->{ServiceID};
|
|
}
|
|
|
|
if (
|
|
!$Self->ValidateSLA(
|
|
%{$Ticket},
|
|
Service => $Service,
|
|
ServiceID => $ServiceID,
|
|
)
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->SLAID or Ticket->SLA parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->State
|
|
if ( $Ticket->{StateID} || $Ticket->{State} ) {
|
|
if ( !$Self->ValidateState( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->StateID or Ticket->State parameter is"
|
|
. " invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Priority
|
|
if ( $Ticket->{PriorityID} || $Ticket->{Priority} ) {
|
|
if ( !$Self->ValidatePriority( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->PriorityID or Ticket->Priority parameter is"
|
|
. " invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Owner
|
|
if ( $Ticket->{OwnerID} || $Ticket->{Owner} ) {
|
|
if ( !$Self->ValidateOwner( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: Ticket->OwnerID or Ticket->Owner parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->Responsible
|
|
if ( $Ticket->{ResponsibleID} || $Ticket->{Responsible} ) {
|
|
if ( !$Self->ValidateResponsible( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->ResponsibleID or Ticket->Responsible"
|
|
. " parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Ticket->PendingTime
|
|
if ( $Ticket->{PendingTime} ) {
|
|
if ( !$Self->ValidatePendingTime( %{$Ticket} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Ticket->PendingTime parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# if everything is OK then return Success
|
|
return {
|
|
Success => 1,
|
|
};
|
|
}
|
|
|
|
=head2 _CheckArticle()
|
|
|
|
checks if the given article parameter is valid.
|
|
|
|
my $ArticleCheck = $OperationObject->_CheckArticle(
|
|
Article => $Article, # all article parameters
|
|
);
|
|
|
|
returns:
|
|
|
|
$ArticleCheck = {
|
|
Success => 1, # if everything is OK
|
|
}
|
|
|
|
$ArticleCheck = {
|
|
ErrorCode => 'Function.Error', # if error
|
|
ErrorMessage => 'Error description',
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _CheckArticle {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $Article = $Param{Article};
|
|
|
|
# check ticket internally
|
|
for my $Needed (qw(Subject Body AutoResponseType)) {
|
|
if ( !$Article->{$Needed} ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Article->$Needed parameter is missing!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->AutoResponseType
|
|
if ( !$Article->{AutoResponseType} ) {
|
|
|
|
# return internal server error
|
|
return {
|
|
ErrorMessage => "TicketUpdate: Article->AutoResponseType parameter is required and",
|
|
};
|
|
}
|
|
|
|
if ( !$Self->ValidateAutoResponseType( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->AutoResponseType parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# check Article->CommunicationChannel
|
|
if ( !$Article->{CommunicationChannel} && !$Article->{CommunicationChannelID} ) {
|
|
|
|
# return internal server error
|
|
return {
|
|
ErrorMessage => "TicketUpdate: Article->CommunicationChannelID or Article->CommunicationChannel parameter"
|
|
. " is required and Sysconfig CommunicationChannelID setting could not be read!"
|
|
};
|
|
}
|
|
if ( !$Self->ValidateArticleCommunicationChannel( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->CommunicationChannel or Article->CommunicationChannelID parameter"
|
|
. " is invalid or not supported!",
|
|
};
|
|
}
|
|
|
|
# check Article->SenderType
|
|
if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) {
|
|
|
|
# return internal server error
|
|
return {
|
|
ErrorMessage => "TicketUpdate: Article->SenderTypeID or Article->SenderType parameter"
|
|
. " is required and Sysconfig SenderTypeID setting could not be read!"
|
|
};
|
|
}
|
|
if ( !$Self->ValidateSenderType( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->SenderTypeID or Ticket->SenderType parameter"
|
|
. " is invalid!",
|
|
};
|
|
}
|
|
|
|
# check Article->From
|
|
if ( $Article->{From} ) {
|
|
if ( !$Self->ValidateFrom( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->From parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->ContentType vs Article->MimeType and Article->Charset
|
|
if ( !$Article->{ContentType} && !$Article->{MimeType} && !$Article->{Charset} ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Article->ContentType or Ticket->MimeType and"
|
|
. " Article->Charset parameters are required!",
|
|
};
|
|
}
|
|
|
|
if ( $Article->{MimeType} && !$Article->{Charset} ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Article->Charset is required!",
|
|
};
|
|
}
|
|
|
|
if ( $Article->{Charset} && !$Article->{MimeType} ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Article->MimeType is required!",
|
|
};
|
|
}
|
|
|
|
# check Article->MimeType
|
|
if ( $Article->{MimeType} ) {
|
|
|
|
$Article->{MimeType} = lc $Article->{MimeType};
|
|
|
|
if ( !$Self->ValidateMimeType( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->MimeType is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->MimeType
|
|
if ( $Article->{Charset} ) {
|
|
|
|
$Article->{Charset} = lc $Article->{Charset};
|
|
|
|
if ( !$Self->ValidateCharset( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->Charset is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->ContentType
|
|
if ( $Article->{ContentType} ) {
|
|
|
|
$Article->{ContentType} = lc $Article->{ContentType};
|
|
|
|
# check Charset part
|
|
my $Charset = '';
|
|
if ( $Article->{ContentType} =~ /charset=/i ) {
|
|
$Charset = $Article->{ContentType};
|
|
$Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
|
|
$Charset =~ s/"|'//g;
|
|
$Charset =~ s/(.+?);.*/$1/g;
|
|
}
|
|
|
|
if ( !$Self->ValidateCharset( Charset => $Charset ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->ContentType is invalid!",
|
|
};
|
|
}
|
|
|
|
# check MimeType part
|
|
my $MimeType = '';
|
|
if ( $Article->{ContentType} =~ /^(\w+\/\w+)/i ) {
|
|
$MimeType = $1;
|
|
$MimeType =~ s/"|'//g;
|
|
}
|
|
|
|
if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->ContentType is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->HistoryType
|
|
if ( !$Article->{HistoryType} ) {
|
|
|
|
# return internal server error
|
|
return {
|
|
ErrorMessage => "TicketUpdate: Article-> HistoryType is required and Sysconfig"
|
|
. " HistoryType setting could not be read!"
|
|
};
|
|
}
|
|
if ( !$Self->ValidateHistoryType( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->HistoryType parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# check Article->HistoryComment
|
|
if ( !$Article->{HistoryComment} ) {
|
|
|
|
# return internal server error
|
|
return {
|
|
ErrorMessage => "TicketUpdate: Article->HistoryComment is required and Sysconfig"
|
|
. " HistoryComment setting could not be read!"
|
|
};
|
|
}
|
|
|
|
# get config object
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# check Article->TimeUnit
|
|
# TimeUnit could be required or not depending on sysconfig option
|
|
if (
|
|
( !defined $Article->{TimeUnit} || !IsStringWithData( $Article->{TimeUnit} ) )
|
|
&& $ConfigObject->{'Ticket::Frontend::AccountTime'}
|
|
&& $ConfigObject->{'Ticket::Frontend::NeedAccountedTime'}
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Article->TimeUnit is required by sysconfig option!",
|
|
};
|
|
}
|
|
if ( $Article->{TimeUnit} ) {
|
|
if ( !$Self->ValidateTimeUnit( %{$Article} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->TimeUnit parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check Article->NoAgentNotify
|
|
if ( $Article->{NoAgentNotify} && $Article->{NoAgentNotify} ne '1' ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->NoAgent parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# check Article array parameters
|
|
for my $Attribute (
|
|
qw( ForceNotificationToUserID ExcludeNotificationToUserID ExcludeMuteNotificationToUserID )
|
|
)
|
|
{
|
|
if ( defined $Article->{$Attribute} ) {
|
|
|
|
# check structure
|
|
if ( IsHashRefWithData( $Article->{$Attribute} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->$Attribute parameter is invalid!",
|
|
};
|
|
}
|
|
else {
|
|
if ( !IsArrayRefWithData( $Article->{$Attribute} ) ) {
|
|
$Article->{$Attribute} = [ $Article->{$Attribute} ];
|
|
}
|
|
for my $UserID ( @{ $Article->{$Attribute} } ) {
|
|
if ( !$Self->ValidateUserID( UserID => $UserID ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Article->$Attribute UserID=$UserID"
|
|
. " parameter is invalid!",
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# if everything is OK then return Success
|
|
return {
|
|
Success => 1,
|
|
};
|
|
}
|
|
|
|
=head2 _CheckDynamicField()
|
|
|
|
checks if the given dynamic field parameter is valid.
|
|
|
|
my $DynamicFieldCheck = $OperationObject->_CheckDynamicField(
|
|
DynamicField => $DynamicField, # all dynamic field parameters
|
|
);
|
|
|
|
returns:
|
|
|
|
$DynamicFieldCheck = {
|
|
Success => 1, # if everything is OK
|
|
}
|
|
|
|
$DynamicFieldCheck = {
|
|
ErrorCode => 'Function.Error', # if error
|
|
ErrorMessage => 'Error description',
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _CheckDynamicField {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $DynamicField = $Param{DynamicField};
|
|
my $ArticleData = $Param{Article};
|
|
|
|
my $Article;
|
|
if ( IsHashRefWithData($ArticleData) ) {
|
|
$Article = 1;
|
|
}
|
|
|
|
# check DynamicField item internally
|
|
for my $Needed (qw(Name Value)) {
|
|
if (
|
|
!defined $DynamicField->{$Needed}
|
|
|| ( !IsString( $DynamicField->{$Needed} ) && ref $DynamicField->{$Needed} ne 'ARRAY' )
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: DynamicField->$Needed parameter is missing!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check DynamicField->Name
|
|
if ( !$Self->ValidateDynamicFieldName( %{$DynamicField} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: DynamicField->Name parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# check objectType for dynamic field
|
|
if (
|
|
!$Self->ValidateDynamicFieldObjectType(
|
|
%{$DynamicField},
|
|
Article => $Article,
|
|
)
|
|
)
|
|
{
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage =>
|
|
"TicketUpdate: To create an article DynamicField an article is required!",
|
|
};
|
|
}
|
|
|
|
# check DynamicField->Value
|
|
if ( !$Self->ValidateDynamicFieldValue( %{$DynamicField} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: DynamicField->Value parameter is invalid!",
|
|
};
|
|
}
|
|
|
|
# if everything is OK then return Success
|
|
return {
|
|
Success => 1,
|
|
};
|
|
}
|
|
|
|
=head2 _CheckAttachment()
|
|
|
|
checks if the given attachment parameter is valid.
|
|
|
|
my $AttachmentCheck = $OperationObject->_CheckAttachment(
|
|
Attachment => $Attachment, # all attachment parameters
|
|
);
|
|
|
|
returns:
|
|
|
|
$AttachmentCheck = {
|
|
Success => 1, # if everything is OK
|
|
}
|
|
|
|
$AttachmentCheck = {
|
|
ErrorCode => 'Function.Error', # if error
|
|
ErrorMessage => 'Error description',
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _CheckAttachment {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $Attachment = $Param{Attachment};
|
|
my $Article = $Param{Article};
|
|
|
|
# check if article is going to be created
|
|
if ( !IsHashRefWithData($Article) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: To create an attachment an article is needed!",
|
|
};
|
|
}
|
|
|
|
# check attachment item internally
|
|
for my $Needed (qw(Content ContentType)) {
|
|
if ( !$Attachment->{$Needed} ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => "TicketUpdate: Attachment->$Needed parameter is missing!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# Check Filename separately because it can be '0'. See bug#14761.
|
|
if ( !IsStringWithData( $Attachment->{Filename} ) ) {
|
|
return {
|
|
ErrorCode => 'TicketCreate.MissingParameter',
|
|
ErrorMessage => "TicketCreate: Attachment->Filename parameter is missing!",
|
|
};
|
|
}
|
|
|
|
# check Article->ContentType
|
|
if ( $Attachment->{ContentType} ) {
|
|
|
|
$Attachment->{ContentType} = lc $Attachment->{ContentType};
|
|
|
|
# check Charset part
|
|
my $Charset = '';
|
|
if ( $Attachment->{ContentType} =~ /charset=/i ) {
|
|
$Charset = $Attachment->{ContentType};
|
|
$Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
|
|
$Charset =~ s/"|'//g;
|
|
$Charset =~ s/(.+?);.*/$1/g;
|
|
}
|
|
|
|
if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Attachment->ContentType is invalid!",
|
|
};
|
|
}
|
|
|
|
# check MimeType part
|
|
my $MimeType = '';
|
|
if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) {
|
|
$MimeType = $1;
|
|
$MimeType =~ s/"|'//g;
|
|
}
|
|
|
|
if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.InvalidParameter',
|
|
ErrorMessage => "TicketUpdate: Attachment->ContentType is invalid!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# if everything is OK then return Success
|
|
return {
|
|
Success => 1,
|
|
};
|
|
}
|
|
|
|
=head2 _CheckUpdatePermissions()
|
|
|
|
check if user has permissions to update ticket attributes.
|
|
|
|
my $Response = $OperationObject->_CheckUpdatePermissions(
|
|
TicketID => 123
|
|
Ticket => $Ticket, # all ticket parameters
|
|
Article => $Ticket, # all attachment parameters
|
|
DynamicField => $Ticket, # all dynamic field parameters
|
|
Attachment => $Ticket, # all attachment parameters
|
|
UserID => 123,
|
|
);
|
|
|
|
returns:
|
|
|
|
$Response = {
|
|
Success => 1, # if everything is OK
|
|
}
|
|
|
|
$Response = {
|
|
Success => 0,
|
|
ErrorCode => "function.error", # if error
|
|
ErrorMessage => "Error description"
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _CheckUpdatePermissions {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $TicketID = $Param{TicketID};
|
|
my $Ticket = $Param{Ticket};
|
|
my $Article = $Param{Article};
|
|
my $DynamicFieldList = $Param{DynamicFieldList};
|
|
my $AttachmentList = $Param{AttachmentList};
|
|
|
|
# get ticket object
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# check Article permissions
|
|
if ( IsHashRefWithData($Article) ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'note',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to create new articles!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check dynamic field permissions
|
|
if ( IsArrayRefWithData($DynamicFieldList) ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'rw',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update dynamic fields!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check queue permissions
|
|
if ( $Ticket->{Queue} || $Ticket->{QueueID} ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'move',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update queue!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check owner permissions
|
|
if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'owner',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update owner!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check responsible permissions
|
|
if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'responsible',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update responsibe!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check priority permissions
|
|
if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) {
|
|
my $Access = $TicketObject->TicketPermission(
|
|
Type => 'priority',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update priority!",
|
|
};
|
|
}
|
|
}
|
|
|
|
# check state permissions
|
|
if ( $Ticket->{State} || $Ticket->{StateID} ) {
|
|
|
|
# get State Data
|
|
my %StateData;
|
|
my $StateID;
|
|
|
|
# get state object
|
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
|
|
|
if ( $Ticket->{StateID} ) {
|
|
$StateID = $Ticket->{StateID};
|
|
}
|
|
else {
|
|
$StateID = $StateObject->StateLookup(
|
|
State => $Ticket->{State},
|
|
);
|
|
}
|
|
|
|
%StateData = $StateObject->StateGet(
|
|
ID => $StateID,
|
|
);
|
|
|
|
my $Access = 1;
|
|
|
|
if ( $StateData{TypeName} =~ /^close/i ) {
|
|
$Access = $TicketObject->TicketPermission(
|
|
Type => 'close',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# set pending time
|
|
elsif ( $StateData{TypeName} =~ /^pending/i ) {
|
|
$Access = $TicketObject->TicketPermission(
|
|
Type => 'close',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
if ( !$Access ) {
|
|
return {
|
|
ErrorCode => 'TicketUpdate.AccessDenied',
|
|
ErrorMessage => "TicketUpdate: Does not have permissions to update state!",
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
Success => 1,
|
|
};
|
|
}
|
|
|
|
=head2 _TicketUpdate()
|
|
|
|
updates a ticket and creates an article and sets dynamic fields and attachments if specified.
|
|
|
|
my $Response = $OperationObject->_TicketUpdate(
|
|
TicketID => 123
|
|
Ticket => $Ticket, # all ticket parameters
|
|
Article => $Article, # all attachment parameters
|
|
DynamicField => $DynamicField, # all dynamic field parameters
|
|
Attachment => $Attachment, # all attachment parameters
|
|
UserID => 123,
|
|
UserType => 'Agent' # || 'Customer
|
|
);
|
|
|
|
returns:
|
|
|
|
$Response = {
|
|
Success => 1, # if everything is OK
|
|
Data => {
|
|
TicketID => 123,
|
|
TicketNumber => 'TN3422332',
|
|
ArticleID => 123, # if new article was created
|
|
}
|
|
}
|
|
|
|
$Response = {
|
|
Success => 0, # if unexpected error
|
|
ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
|
|
}
|
|
|
|
=cut
|
|
|
|
sub _TicketUpdate {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my $TicketID = $Param{TicketID};
|
|
my $Ticket = $Param{Ticket};
|
|
my $Article = $Param{Article};
|
|
my $DynamicFieldList = $Param{DynamicFieldList};
|
|
my $AttachmentList = $Param{AttachmentList};
|
|
|
|
my $Access = $Self->_CheckUpdatePermissions(%Param);
|
|
|
|
# if no permissions return error
|
|
if ( !$Access->{Success} ) {
|
|
return $Self->ReturnError( %{$Access} );
|
|
}
|
|
|
|
my %CustomerUserData;
|
|
|
|
# get customer information
|
|
if ( $Ticket->{CustomerUser} ) {
|
|
%CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
|
|
User => $Ticket->{CustomerUser},
|
|
);
|
|
}
|
|
|
|
# get ticket object
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
# get current ticket data
|
|
my %TicketData = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 0,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# update ticket parameters
|
|
# update Ticket->Title
|
|
if (
|
|
defined $Ticket->{Title}
|
|
&& $Ticket->{Title} ne ''
|
|
&& $Ticket->{Title} ne $TicketData{Title}
|
|
)
|
|
{
|
|
my $Success = $TicketObject->TicketTitleUpdate(
|
|
Title => $Ticket->{Title},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket title could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Queue
|
|
if ( $Ticket->{Queue} || $Ticket->{QueueID} ) {
|
|
my $Success;
|
|
if ( defined $Ticket->{Queue} && $Ticket->{Queue} ne $TicketData{Queue} ) {
|
|
$Success = $TicketObject->TicketQueueSet(
|
|
Queue => $Ticket->{Queue},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{QueueID} && $Ticket->{QueueID} ne $TicketData{QueueID} ) {
|
|
$Success = $TicketObject->TicketQueueSet(
|
|
QueueID => $Ticket->{QueueID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
ErrorMessage =>
|
|
'Ticket queue could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Lock
|
|
if ( $Ticket->{Lock} || $Ticket->{LockID} ) {
|
|
my $Success;
|
|
if ( defined $Ticket->{Lock} && $Ticket->{Lock} ne $TicketData{Lock} ) {
|
|
$Success = $TicketObject->TicketLockSet(
|
|
Lock => $Ticket->{Lock},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{LockID} && $Ticket->{LockID} ne $TicketData{LockID} ) {
|
|
$Success = $TicketObject->TicketLockSet(
|
|
LockID => $Ticket->{LockID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket lock could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Type
|
|
if ( $Ticket->{Type} || $Ticket->{TypeID} ) {
|
|
my $Success;
|
|
if ( defined $Ticket->{Type} && $Ticket->{Type} ne $TicketData{Type} ) {
|
|
$Success = $TicketObject->TicketTypeSet(
|
|
Type => $Ticket->{Type},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{TypeID} && $Ticket->{TypeID} ne $TicketData{TypeID} )
|
|
{
|
|
$Success = $TicketObject->TicketTypeSet(
|
|
TypeID => $Ticket->{TypeID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket type could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket>State
|
|
# depending on the state, might require to unlock ticket or enables pending time set
|
|
if ( $Ticket->{State} || $Ticket->{StateID} ) {
|
|
|
|
# get State Data
|
|
my %StateData;
|
|
my $StateID;
|
|
|
|
# get state object
|
|
my $StateObject = $Kernel::OM->Get('Kernel::System::State');
|
|
|
|
if ( $Ticket->{StateID} ) {
|
|
$StateID = $Ticket->{StateID};
|
|
}
|
|
else {
|
|
$StateID = $StateObject->StateLookup(
|
|
State => $Ticket->{State},
|
|
);
|
|
}
|
|
|
|
%StateData = $StateObject->StateGet(
|
|
ID => $StateID,
|
|
);
|
|
|
|
# force unlock if state type is close
|
|
if ( $StateData{TypeName} =~ /^close/i ) {
|
|
|
|
# set lock
|
|
$TicketObject->TicketLockSet(
|
|
TicketID => $TicketID,
|
|
Lock => 'unlock',
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
|
|
# set pending time
|
|
elsif ( $StateData{TypeName} =~ /^pending/i ) {
|
|
|
|
# set pending time
|
|
if ( defined $Ticket->{PendingTime} ) {
|
|
my $Success = $TicketObject->TicketPendingTimeSet(
|
|
UserID => $Param{UserID},
|
|
TicketID => $TicketID,
|
|
%{ $Ticket->{PendingTime} },
|
|
);
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket pendig time could not be updated, please contact system'
|
|
. ' administrator!',
|
|
};
|
|
}
|
|
}
|
|
else {
|
|
return $Self->ReturnError(
|
|
ErrorCode => 'TicketUpdate.MissingParameter',
|
|
ErrorMessage => 'Can\'t set a ticket on a pending state without pendig time!'
|
|
);
|
|
}
|
|
}
|
|
|
|
my $Success;
|
|
if ( defined $Ticket->{State} && $Ticket->{State} ne $TicketData{State} ) {
|
|
$Success = $TicketObject->TicketStateSet(
|
|
State => $Ticket->{State},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{StateID} && $Ticket->{StateID} ne $TicketData{StateID} )
|
|
{
|
|
$Success = $TicketObject->TicketStateSet(
|
|
StateID => $Ticket->{StateID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket state could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Service
|
|
# this might reset SLA if current SLA is not available for the new service
|
|
if ( $Ticket->{Service} || $Ticket->{ServiceID} ) {
|
|
|
|
# check if ticket has a SLA assigned
|
|
if ( $TicketData{SLAID} ) {
|
|
|
|
# check if old SLA is still valid
|
|
if (
|
|
!$Self->ValidateSLA(
|
|
SLAID => $TicketData{SLAID},
|
|
Service => $Ticket->{Service} || '',
|
|
ServiceID => $Ticket->{ServiceID} || '',
|
|
)
|
|
)
|
|
{
|
|
|
|
# remove current SLA if is not compatible with new service
|
|
my $Success = $TicketObject->TicketSLASet(
|
|
SLAID => '',
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
my $Success;
|
|
|
|
# prevent comparison errors on undefined values
|
|
if ( !defined $TicketData{Service} ) {
|
|
$TicketData{Service} = '';
|
|
}
|
|
if ( !defined $TicketData{ServiceID} ) {
|
|
$TicketData{ServiceID} = '';
|
|
}
|
|
|
|
if ( defined $Ticket->{Service} && $Ticket->{Service} ne $TicketData{Service} ) {
|
|
$Success = $TicketObject->TicketServiceSet(
|
|
Service => $Ticket->{Service},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{ServiceID} && $Ticket->{ServiceID} ne $TicketData{ServiceID} )
|
|
{
|
|
$Success = $TicketObject->TicketServiceSet(
|
|
ServiceID => $Ticket->{ServiceID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket service could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->SLA
|
|
if ( $Ticket->{SLA} || $Ticket->{SLAID} ) {
|
|
my $Success;
|
|
|
|
# prevent comparison errors on undefined values
|
|
if ( !defined $TicketData{SLA} ) {
|
|
$TicketData{SLA} = '';
|
|
}
|
|
if ( !defined $TicketData{SLAID} ) {
|
|
$TicketData{SLAID} = '';
|
|
}
|
|
|
|
if ( defined $Ticket->{SLA} && $Ticket->{SLA} ne $TicketData{SLA} ) {
|
|
$Success = $TicketObject->TicketSLASet(
|
|
SLA => $Ticket->{SLA},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{SLAID} && $Ticket->{SLAID} ne $TicketData{SLAID} )
|
|
{
|
|
$Success = $TicketObject->TicketSLASet(
|
|
SLAID => $Ticket->{SLAID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket SLA could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->CustomerUser && Ticket->CustomerID
|
|
if ( $Ticket->{CustomerUser} || $Ticket->{CustomerID} ) {
|
|
|
|
# set values to empty if they are not defined
|
|
$TicketData{CustomerUserID} = $TicketData{CustomerUserID} || '';
|
|
$TicketData{CustomerID} = $TicketData{CustomerID} || '';
|
|
$Ticket->{CustomerUser} = $Ticket->{CustomerUser} || '';
|
|
$Ticket->{CustomerID} = $Ticket->{CustomerID} || '';
|
|
|
|
my $Success;
|
|
if (
|
|
$Ticket->{CustomerUser} ne $TicketData{CustomerUserID}
|
|
|| $Ticket->{CustomerID} ne $TicketData{CustomerID}
|
|
)
|
|
{
|
|
my $CustomerID = $CustomerUserData{UserCustomerID} || '';
|
|
|
|
# use user defined CustomerID if defined
|
|
if ( defined $Ticket->{CustomerID} && $Ticket->{CustomerID} ne '' ) {
|
|
$CustomerID = $Ticket->{CustomerID};
|
|
}
|
|
|
|
$Success = $TicketObject->TicketCustomerSet(
|
|
No => $CustomerID,
|
|
User => $Ticket->{CustomerUser},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket customer user could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Priority
|
|
if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) {
|
|
my $Success;
|
|
if ( defined $Ticket->{Priority} && $Ticket->{Priority} ne $TicketData{Priority} ) {
|
|
$Success = $TicketObject->TicketPrioritySet(
|
|
Priority => $Ticket->{Priority},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif ( defined $Ticket->{PriorityID} && $Ticket->{PriorityID} ne $TicketData{PriorityID} )
|
|
{
|
|
$Success = $TicketObject->TicketPrioritySet(
|
|
PriorityID => $Ticket->{PriorityID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket priority could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
my $UnlockOnAway = 1;
|
|
|
|
# update Ticket->Owner
|
|
if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) {
|
|
my $Success;
|
|
if ( defined $Ticket->{Owner} && $Ticket->{Owner} ne $TicketData{Owner} ) {
|
|
$Success = $TicketObject->TicketOwnerSet(
|
|
NewUser => $Ticket->{Owner},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
$UnlockOnAway = 0;
|
|
}
|
|
elsif ( defined $Ticket->{OwnerID} && $Ticket->{OwnerID} ne $TicketData{OwnerID} )
|
|
{
|
|
$Success = $TicketObject->TicketOwnerSet(
|
|
NewUserID => $Ticket->{OwnerID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
$UnlockOnAway = 0;
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket owner could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
# update Ticket->Responsible
|
|
if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) {
|
|
my $Success;
|
|
if (
|
|
defined $Ticket->{Responsible}
|
|
&& $Ticket->{Responsible} ne $TicketData{Responsible}
|
|
)
|
|
{
|
|
$Success = $TicketObject->TicketResponsibleSet(
|
|
NewUser => $Ticket->{Responsible},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
elsif (
|
|
defined $Ticket->{ResponsibleID}
|
|
&& $Ticket->{ResponsibleID} ne $TicketData{ResponsibleID}
|
|
)
|
|
{
|
|
$Success = $TicketObject->TicketResponsibleSet(
|
|
NewUserID => $Ticket->{ResponsibleID},
|
|
TicketID => $TicketID,
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# data is the same as in ticket nothing to do
|
|
$Success = 1;
|
|
}
|
|
|
|
if ( !$Success ) {
|
|
return {
|
|
Success => 0,
|
|
Errormessage =>
|
|
'Ticket responsible could not be updated, please contact system administrator!',
|
|
};
|
|
}
|
|
}
|
|
|
|
my $ArticleID;
|
|
if ( IsHashRefWithData($Article) ) {
|
|
|
|
# set Article From
|
|
my $From;
|
|
if ( $Article->{From} ) {
|
|
$From = $Article->{From};
|
|
}
|
|
elsif ( $Param{UserType} eq 'Customer' ) {
|
|
|
|
# use data from customer user (if customer user is in database)
|
|
if ( IsHashRefWithData( \%CustomerUserData ) ) {
|
|
$From = '"'
|
|
. $CustomerUserData{UserFullname} . '"'
|
|
. ' <' . $CustomerUserData{UserEmail} . '>';
|
|
}
|
|
|
|
# otherwise use customer user as sent from the request (it should be an email)
|
|
else {
|
|
$From = $Ticket->{CustomerUser};
|
|
}
|
|
}
|
|
else {
|
|
my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
|
|
UserID => $Param{UserID},
|
|
);
|
|
$From = $UserData{UserFullname};
|
|
}
|
|
|
|
# Set Article To, Cc, Bcc.
|
|
my ( $To, $Cc, $Bcc );
|
|
if ( $Article->{To} ) {
|
|
$To = $Article->{To};
|
|
}
|
|
if ( $Article->{Cc} ) {
|
|
$Cc = $Article->{Cc};
|
|
}
|
|
if ( $Article->{Bcc} ) {
|
|
$Bcc = $Article->{Bcc};
|
|
}
|
|
|
|
# Fallback for To
|
|
if ( !$To && $Article->{CommunicationChannel} eq 'Email' ) {
|
|
|
|
# Use data from customer user (if customer user is in database).
|
|
if ( IsHashRefWithData( \%CustomerUserData ) ) {
|
|
$To = '"' . $CustomerUserData{UserFullname} . '"'
|
|
. ' <' . $CustomerUserData{UserEmail} . '>';
|
|
}
|
|
|
|
# Otherwise use customer user as sent from the request (it should be an email).
|
|
else {
|
|
$To = $Ticket->{CustomerUser} // $TicketData{CustomerUserID};
|
|
}
|
|
}
|
|
|
|
if ( !$Article->{CommunicationChannel} ) {
|
|
|
|
my %CommunicationChannel = $Kernel::OM->Get('Kernel::System::CommunicationChannel')->ChannelGet(
|
|
ChannelID => $Article->{CommunicationChannelID},
|
|
);
|
|
$Article->{CommunicationChannel} = $CommunicationChannel{ChannelName};
|
|
}
|
|
|
|
my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel(
|
|
ChannelName => $Article->{CommunicationChannel},
|
|
);
|
|
|
|
my $PlainBody = $Article->{Body};
|
|
|
|
# Convert article body to plain text, if HTML content was supplied. This is necessary since auto response code
|
|
# expects plain text content. Please see bug#13397 for more information.
|
|
if ( $Article->{ContentType} =~ /text\/html/i || $Article->{MimeType} =~ /text\/html/i ) {
|
|
$PlainBody = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
|
|
String => $Article->{Body},
|
|
);
|
|
}
|
|
|
|
# Create article.
|
|
$ArticleID = $ArticleBackendObject->ArticleCreate(
|
|
NoAgentNotify => $Article->{NoAgentNotify} || 0,
|
|
TicketID => $TicketID,
|
|
SenderTypeID => $Article->{SenderTypeID} || '',
|
|
SenderType => $Article->{SenderType} || '',
|
|
IsVisibleForCustomer => $Article->{IsVisibleForCustomer},
|
|
From => $From,
|
|
To => $To,
|
|
Cc => $Cc,
|
|
Bcc => $Bcc,
|
|
Subject => $Article->{Subject},
|
|
Body => $Article->{Body},
|
|
MimeType => $Article->{MimeType} || '',
|
|
Charset => $Article->{Charset} || '',
|
|
ContentType => $Article->{ContentType} || '',
|
|
UserID => $Param{UserID},
|
|
HistoryType => $Article->{HistoryType},
|
|
HistoryComment => $Article->{HistoryComment} || '%%',
|
|
AutoResponseType => $Article->{AutoResponseType},
|
|
UnlockOnAway => $UnlockOnAway,
|
|
OrigHeader => {
|
|
From => $From,
|
|
To => $To,
|
|
Subject => $Article->{Subject},
|
|
Body => $PlainBody,
|
|
},
|
|
);
|
|
|
|
if ( !$ArticleID ) {
|
|
return {
|
|
Success => 0,
|
|
ErrorMessage =>
|
|
'Article could not be created, please contact the system administrator'
|
|
};
|
|
}
|
|
|
|
# time accounting
|
|
if ( $Article->{TimeUnit} ) {
|
|
$TicketObject->TicketAccountTime(
|
|
TicketID => $TicketID,
|
|
ArticleID => $ArticleID,
|
|
TimeUnit => $Article->{TimeUnit},
|
|
UserID => $Param{UserID},
|
|
);
|
|
}
|
|
}
|
|
|
|
# set dynamic fields
|
|
for my $DynamicField ( @{$DynamicFieldList} ) {
|
|
my $Result = $Self->SetDynamicFieldValue(
|
|
%{$DynamicField},
|
|
TicketID => $TicketID,
|
|
ArticleID => $ArticleID || '',
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
if ( !$Result->{Success} ) {
|
|
my $ErrorMessage =
|
|
$Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be set,"
|
|
. " please contact the system administrator";
|
|
|
|
return {
|
|
Success => 0,
|
|
ErrorMessage => $ErrorMessage,
|
|
};
|
|
}
|
|
}
|
|
|
|
# set attachments
|
|
|
|
for my $Attachment ( @{$AttachmentList} ) {
|
|
my $Result = $Self->CreateAttachment(
|
|
Attachment => $Attachment,
|
|
TicketID => $TicketID,
|
|
ArticleID => $ArticleID || '',
|
|
UserID => $Param{UserID}
|
|
);
|
|
|
|
if ( !$Result->{Success} ) {
|
|
my $ErrorMessage =
|
|
$Result->{ErrorMessage} || "Attachment could not be created, please contact the "
|
|
. " system administrator";
|
|
|
|
return {
|
|
Success => 0,
|
|
ErrorMessage => $ErrorMessage,
|
|
};
|
|
}
|
|
}
|
|
|
|
# get web service configuration
|
|
my $Webservice = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet(
|
|
ID => $Self->{WebserviceID},
|
|
);
|
|
|
|
my $IncludeTicketData;
|
|
|
|
# Get operation config, if operation name was supplied.
|
|
if ( $Self->{Operation} ) {
|
|
my $OperationConfig = $Webservice->{Config}->{Provider}->{Operation}->{ $Self->{Operation} };
|
|
$IncludeTicketData = $OperationConfig->{IncludeTicketData};
|
|
}
|
|
|
|
if ( !$IncludeTicketData ) {
|
|
if ($ArticleID) {
|
|
return {
|
|
Success => 1,
|
|
Data => {
|
|
TicketID => $TicketID,
|
|
TicketNumber => $TicketData{TicketNumber},
|
|
ArticleID => $ArticleID,
|
|
},
|
|
};
|
|
}
|
|
return {
|
|
Success => 1,
|
|
Data => {
|
|
TicketID => $TicketID,
|
|
TicketNumber => $TicketData{TicketNumber},
|
|
},
|
|
};
|
|
}
|
|
|
|
# get updated TicketData
|
|
%TicketData = ();
|
|
%TicketData = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
DynamicFields => 1,
|
|
UserID => $Param{UserID},
|
|
);
|
|
|
|
# extract all dynamic fields from main ticket hash.
|
|
my %TicketDynamicFields;
|
|
TICKETATTRIBUTE:
|
|
for my $TicketAttribute ( sort keys %TicketData ) {
|
|
if ( $TicketAttribute =~ m{\A DynamicField_(.*) \z}msx ) {
|
|
$TicketDynamicFields{$1} = {
|
|
Name => $1,
|
|
Value => $TicketData{$TicketAttribute},
|
|
};
|
|
delete $TicketData{$TicketAttribute};
|
|
}
|
|
}
|
|
|
|
# add dynamic fields as array into 'DynamicField' hash key if any
|
|
if (%TicketDynamicFields) {
|
|
$TicketData{DynamicField} = [ sort { $a->{Name} cmp $b->{Name} } values %TicketDynamicFields ];
|
|
}
|
|
|
|
# get last ArticleID
|
|
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
|
|
my $ArticleBackendObject = $ArticleObject->BackendForArticle(
|
|
ArticleID => $ArticleID,
|
|
TicketID => $TicketID
|
|
);
|
|
|
|
my @Articles = $ArticleObject->ArticleList(
|
|
TicketID => $TicketID,
|
|
OnlyLast => 1,
|
|
);
|
|
|
|
my $LastArticleID = $Articles[0]->{ArticleID};
|
|
|
|
# return ticket data if we have no article data
|
|
if ( !$ArticleID && !$LastArticleID ) {
|
|
return {
|
|
Success => 1,
|
|
Data => {
|
|
TicketID => $TicketID,
|
|
TicketNumber => $TicketData{TicketNumber},
|
|
Ticket => \%TicketData,
|
|
},
|
|
};
|
|
}
|
|
|
|
# get Article and ArticleAttachement
|
|
my %ArticleData = $ArticleBackendObject->ArticleGet(
|
|
ArticleID => $ArticleID || $LastArticleID,
|
|
DynamicFields => 1,
|
|
TicketID => $TicketID,
|
|
);
|
|
|
|
# prepare Article DynamicFields
|
|
my @ArticleDynamicFields;
|
|
|
|
# remove all dynamic fields from main ticket hash and set them into an array.
|
|
ARTICLEATTRIBUTE:
|
|
for my $ArticleAttribute ( sort keys %ArticleData ) {
|
|
if ( $ArticleAttribute =~ m{\A DynamicField_(.*) \z}msx ) {
|
|
if ( !exists $TicketDynamicFields{$1} ) {
|
|
push @ArticleDynamicFields, {
|
|
Name => $1,
|
|
Value => $ArticleData{$ArticleAttribute},
|
|
};
|
|
}
|
|
|
|
delete $ArticleData{$ArticleAttribute};
|
|
}
|
|
}
|
|
|
|
# add dynamic fields array into 'DynamicField' hash key if any
|
|
if (@ArticleDynamicFields) {
|
|
$ArticleData{DynamicField} = \@ArticleDynamicFields;
|
|
}
|
|
|
|
# add attachment if the request includes attachments
|
|
if ( IsArrayRefWithData($AttachmentList) ) {
|
|
my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex(
|
|
ArticleID => $ArticleData{ArticleID},
|
|
);
|
|
|
|
my @Attachments;
|
|
$Kernel::OM->Get('Kernel::System::Main')->Require('MIME::Base64');
|
|
ATTACHMENT:
|
|
for my $FileID ( sort keys %AttachmentIndex ) {
|
|
next ATTACHMENT if !$FileID;
|
|
my %Attachment = $ArticleBackendObject->ArticleAttachment(
|
|
ArticleID => $ArticleData{ArticleID},
|
|
FileID => $FileID,
|
|
);
|
|
|
|
next ATTACHMENT if !IsHashRefWithData( \%Attachment );
|
|
|
|
# convert content to base64, but prevent 76 chars brake, see bug#14500.
|
|
$Attachment{Content} = MIME::Base64::encode_base64( $Attachment{Content}, '' );
|
|
push @Attachments, {%Attachment};
|
|
}
|
|
|
|
# set Attachments data
|
|
if (@Attachments) {
|
|
$ArticleData{Attachment} = \@Attachments;
|
|
}
|
|
}
|
|
|
|
$TicketData{Article} = \%ArticleData;
|
|
|
|
# return ticket data and article data
|
|
return {
|
|
Success => 1,
|
|
Data => {
|
|
TicketID => $TicketID,
|
|
TicketNumber => $TicketData{TicketNumber},
|
|
ArticleID => $ArticleData{ArticleID},
|
|
Ticket => \%TicketData,
|
|
},
|
|
};
|
|
}
|
|
|
|
1;
|
|
|
|
=end Internal:
|
|
|
|
=head1 TERMS AND CONDITIONS
|
|
|
|
This software is part of the OTRS project (L<https://otrs.org/>).
|
|
|
|
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 L<https://www.gnu.org/licenses/gpl-3.0.txt>.
|
|
|
|
=cut
|