1# -- 2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/ 3# -- 4# This software comes with ABSOLUTELY NO WARRANTY. For details, see 5# the enclosed file COPYING for license information (GPL). If you 6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt. 7# -- 8 9package Kernel::GenericInterface::Operation::Ticket::TicketUpdate; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw( :all ); 15 16use parent qw( 17 Kernel::GenericInterface::Operation::Common 18 Kernel::GenericInterface::Operation::Ticket::Common 19); 20 21our $ObjectManagerDisabled = 1; 22 23=head1 NAME 24 25Kernel::GenericInterface::Operation::Ticket::TicketUpdate - GenericInterface Ticket TicketUpdate Operation backend 26 27=head1 PUBLIC INTERFACE 28 29=head2 new() 30 31usually, you want to create an instance of this 32by using Kernel::GenericInterface::Operation->new(); 33 34=cut 35 36sub new { 37 my ( $Type, %Param ) = @_; 38 39 my $Self = {}; 40 bless( $Self, $Type ); 41 42 # check needed objects 43 for my $Needed (qw( DebuggerObject WebserviceID )) { 44 if ( !$Param{$Needed} ) { 45 return { 46 Success => 0, 47 ErrorMessage => "Got no $Needed!", 48 }; 49 } 50 51 $Self->{$Needed} = $Param{$Needed}; 52 } 53 54 $Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::TicketUpdate'); 55 $Self->{Operation} = $Param{Operation}; 56 57 return $Self; 58} 59 60=head2 Run() 61 62perform TicketUpdate Operation. This will return the updated TicketID and 63if applicable the created ArticleID. 64 65 my $Result = $OperationObject->Run( 66 Data => { 67 UserLogin => 'some agent login', # UserLogin or CustomerUserLogin or SessionID is 68 # required 69 CustomerUserLogin => 'some customer login', 70 SessionID => 123, 71 72 Password => 'some password', # if UserLogin or customerUserLogin is sent then 73 # Password is required 74 75 TicketID => 123, # TicketID or TicketNumber is required 76 TicketNumber => '2004040510440485', 77 78 Ticket { # optional 79 Title => 'some ticket title', 80 81 QueueID => 123, # Optional 82 Queue => 'some queue name', # Optional 83 LockID => 123, # optional 84 Lock => 'some lock name', # optional 85 TypeID => 123, # optional 86 Type => 'some type name', # optional 87 ServiceID => 123, # optional 88 Service => 'some service name', # optional 89 SLAID => 123, # optional 90 SLA => 'some SLA name', # optional 91 StateID => 123, # optional 92 State => 'some state name', # optional 93 PriorityID => 123, # optional 94 Priority => 'some priority name', # optional 95 OwnerID => 123, # optional 96 Owner => 'some user login', # optional 97 ResponsibleID => 123, # optional 98 Responsible => 'some user login', # optional 99 CustomerUser => 'some customer user login', 100 101 PendingTime { # optional 102 Year => 2011, 103 Month => 12 104 Day => 03, 105 Hour => 23, 106 Minute => 05, 107 }, 108 # or 109 # PendingTime { 110 # Diff => 10080, # Pending time in minutes 111 #}, 112 }, 113 Article => { # optional 114 CommunicationChannel => 'Email', # CommunicationChannel or CommunicationChannelID must be provided. 115 CommunicationChannelID => 1, 116 IsVisibleForCustomer => 1, # optional 117 SenderTypeID => 123, # optional 118 SenderType => 'some sender type name', # optional 119 AutoResponseType => 'some auto response type', # optional 120 From => 'some from string', # optional 121 Subject => 'some subject', 122 Body => 'some body', 123 124 ContentType => 'some content type', # ContentType or MimeType and Charset is required 125 MimeType => 'some mime type', 126 Charset => 'some charset', 127 128 HistoryType => 'some history type', # optional 129 HistoryComment => 'Some history comment', # optional 130 TimeUnit => 123, # optional 131 NoAgentNotify => 1, # optional 132 ForceNotificationToUserID => [1, 2, 3] # optional 133 ExcludeNotificationToUserID => [1, 2, 3] # optional 134 ExcludeMuteNotificationToUserID => [1, 2, 3] # optional 135 }, 136 137 DynamicField => [ # optional 138 { 139 Name => 'some name', 140 Value => $Value, # value type depends on the dynamic field 141 }, 142 # ... 143 ], 144 # or 145 # DynamicField { 146 # Name => 'some name', 147 # Value => $Value, 148 #}, 149 150 Attachment [ 151 { 152 Content => 'content' # base64 encoded 153 ContentType => 'some content type' 154 Filename => 'some fine name' 155 }, 156 # ... 157 ], 158 #or 159 #Attachment { 160 # Content => 'content' 161 # ContentType => 'some content type' 162 # Filename => 'some fine name' 163 #}, 164 }, 165 ); 166 167 $Result = { 168 Success => 1, # 0 or 1 169 ErrorMessage => '', # in case of error 170 Data => { # result data payload after Operation 171 TicketID => 123, # Ticket ID number in OTRS (help desk system) 172 ArticleID => 43, # Article ID number in OTRS (help desk system) 173 Error => { # should not return errors 174 ErrorCode => 'TicketUpdate.ErrorCode' 175 ErrorMessage => 'Error Description' 176 }, 177 178 # If IncludeTicketData is enabled 179 Ticket => [ 180 { 181 TicketNumber => '20101027000001', 182 Title => 'some title', 183 TicketID => 123, 184 State => 'some state', 185 StateID => 123, 186 StateType => 'some state type', 187 Priority => 'some priority', 188 PriorityID => 123, 189 Lock => 'lock', 190 LockID => 123, 191 Queue => 'some queue', 192 QueueID => 123, 193 CustomerID => 'customer_id_123', 194 CustomerUserID => 'customer_user_id_123', 195 Owner => 'some_owner_login', 196 OwnerID => 123, 197 Type => 'some ticket type', 198 TypeID => 123, 199 SLA => 'some sla', 200 SLAID => 123, 201 Service => 'some service', 202 ServiceID => 123, 203 Responsible => 'some_responsible_login', 204 ResponsibleID => 123, 205 Age => 3456, 206 Created => '2010-10-27 20:15:00' 207 CreateBy => 123, 208 Changed => '2010-10-27 20:15:15', 209 ChangeBy => 123, 210 ArchiveFlag => 'y', 211 212 DynamicField => [ 213 { 214 Name => 'some name', 215 Value => 'some value', 216 }, 217 ], 218 219 # (time stamps of expected escalations) 220 EscalationResponseTime (unix time stamp of response time escalation) 221 EscalationUpdateTime (unix time stamp of update time escalation) 222 EscalationSolutionTime (unix time stamp of solution time escalation) 223 224 # (general escalation info of nearest escalation type) 225 EscalationDestinationIn (escalation in e. g. 1h 4m) 226 EscalationDestinationTime (date of escalation in unix time, e. g. 72193292) 227 EscalationDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 228 EscalationTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 229 EscalationTime (seconds total till escalation of nearest escalation time type - response, update or solution time, e. g. "3600") 230 231 # (detailed escalation info about first response, update and solution time) 232 FirstResponseTimeEscalation (if true, ticket is escalated) 233 FirstResponseTimeNotification (if true, notify - x% of escalation has reached) 234 FirstResponseTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 235 FirstResponseTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 236 FirstResponseTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 237 FirstResponseTime (seconds total till escalation, e. g. "3600") 238 239 UpdateTimeEscalation (if true, ticket is escalated) 240 UpdateTimeNotification (if true, notify - x% of escalation has reached) 241 UpdateTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 242 UpdateTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 243 UpdateTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 244 UpdateTime (seconds total till escalation, e. g. "3600") 245 246 SolutionTimeEscalation (if true, ticket is escalated) 247 SolutionTimeNotification (if true, notify - x% of escalation has reached) 248 SolutionTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 249 SolutionTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 250 SolutionTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 251 SolutionTime (seconds total till escalation, e. g. "3600") 252 253 Article => [ 254 { 255 ArticleID 256 From 257 To 258 Cc 259 Subject 260 Body 261 ReplyTo 262 MessageID 263 InReplyTo 264 References 265 SenderType 266 SenderTypeID 267 CommunicationChannelID 268 IsVisibleForCustomer 269 ContentType 270 Charset 271 MimeType 272 IncomingTime 273 274 DynamicField => [ 275 { 276 Name => 'some name', 277 Value => 'some value', 278 }, 279 ], 280 281 Attachment => [ 282 { 283 Content => "xxxx", # actual attachment contents, base64 enconded 284 ContentAlternative => "", 285 ContentID => "", 286 ContentType => "application/pdf", 287 Filename => "StdAttachment-Test1.pdf", 288 Filesize => "4.6 KBytes", 289 FilesizeRaw => 4722, 290 }, 291 ], 292 }, 293 ], 294 }, 295 ], 296 }, 297 }; 298 299=cut 300 301sub Run { 302 my ( $Self, %Param ) = @_; 303 304 my $Result = $Self->Init( 305 WebserviceID => $Self->{WebserviceID}, 306 ); 307 308 if ( !$Result->{Success} ) { 309 $Self->ReturnError( 310 ErrorCode => 'Webservice.InvalidConfiguration', 311 ErrorMessage => $Result->{ErrorMessage}, 312 ); 313 } 314 315 # check needed stuff 316 if ( !IsHashRefWithData( $Param{Data} ) ) { 317 return $Self->ReturnError( 318 ErrorCode => 'TicketUpdate.EmptyRequest', 319 ErrorMessage => "TicketUpdate: The request data is invalid!", 320 ); 321 } 322 323 if ( !$Param{Data}->{TicketID} && !$Param{Data}->{TicketNumber} ) { 324 return $Self->ReturnError( 325 ErrorCode => 'TicketUpdate.MissingParameter', 326 ErrorMessage => "TicketUpdate: TicketID or TicketNumber is required!", 327 ); 328 } 329 330 if ( 331 !$Param{Data}->{UserLogin} 332 && !$Param{Data}->{CustomerUserLogin} 333 && !$Param{Data}->{SessionID} 334 ) 335 { 336 return $Self->ReturnError( 337 ErrorCode => 'TicketUpdate.MissingParameter', 338 ErrorMessage => "TicketUpdate: UserLogin, CustomerUserLogin or SessionID is required!", 339 ); 340 } 341 342 if ( $Param{Data}->{UserLogin} || $Param{Data}->{CustomerUserLogin} ) { 343 344 if ( !$Param{Data}->{Password} ) 345 { 346 return $Self->ReturnError( 347 ErrorCode => 'TicketUpdate.MissingParameter', 348 ErrorMessage => "TicketUpdate: Password or SessionID is required!", 349 ); 350 } 351 } 352 353 # authenticate user 354 my ( $UserID, $UserType ) = $Self->Auth(%Param); 355 356 if ( !$UserID ) { 357 return $Self->ReturnError( 358 ErrorCode => 'TicketUpdate.AuthFail', 359 ErrorMessage => "TicketUpdate: User could not be authenticated!", 360 ); 361 } 362 363 my $PermissionUserID = $UserID; 364 365 if ( $UserType eq 'Customer' ) { 366 $UserID = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanelUserID'); 367 } 368 369 # get ticket object 370 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 371 372 # check TicketID 373 my $TicketID; 374 if ( $Param{Data}->{TicketNumber} ) { 375 $TicketID = $TicketObject->TicketIDLookup( 376 TicketNumber => $Param{Data}->{TicketNumber}, 377 UserID => $UserID, 378 ); 379 380 } 381 else { 382 $TicketID = $Param{Data}->{TicketID}; 383 } 384 385 if ( !($TicketID) ) { 386 return $Self->ReturnError( 387 ErrorCode => 'TicketUpdate.AccessDenied', 388 ErrorMessage => "TicketUpdate: User does not have access to the ticket!", 389 ); 390 } 391 392 my %TicketData = $TicketObject->TicketGet( 393 TicketID => $TicketID, 394 DynamicFields => 0, 395 UserID => $UserID, 396 ); 397 398 if ( !IsHashRefWithData( \%TicketData ) ) { 399 return $Self->ReturnError( 400 ErrorCode => 'TicketUpdate.AccessDenied', 401 ErrorMessage => "TicketUpdate: User does not have access to the ticket!", 402 ); 403 } 404 405 # check basic needed permissions 406 my $Access = $Self->CheckAccessPermissions( 407 TicketID => $TicketID, 408 UserID => $PermissionUserID, 409 UserType => $UserType, 410 ); 411 412 if ( !$Access ) { 413 return $Self->ReturnError( 414 ErrorCode => 'TicketUpdate.AccessDenied', 415 ErrorMessage => "TicketUpdate: User does not have access to the ticket!", 416 ); 417 } 418 419 # check optional hashes 420 for my $Optional (qw(Ticket Article)) { 421 if ( 422 defined $Param{Data}->{$Optional} 423 && !IsHashRefWithData( $Param{Data}->{$Optional} ) 424 ) 425 { 426 return $Self->ReturnError( 427 ErrorCode => 'TicketUpdate.InvalidParameter', 428 ErrorMessage => "TicketUpdate: $Optional parameter is not valid!", 429 ); 430 } 431 } 432 433 # check optional array/hashes 434 for my $Optional (qw(DynamicField Attachment)) { 435 if ( 436 defined $Param{Data}->{$Optional} 437 && !IsHashRefWithData( $Param{Data}->{$Optional} ) 438 && !IsArrayRefWithData( $Param{Data}->{$Optional} ) 439 ) 440 { 441 return $Self->ReturnError( 442 ErrorCode => 'TicketUpdate.MissingParameter', 443 ErrorMessage => "TicketUpdate: $Optional parameter is missing or not valid!", 444 ); 445 } 446 } 447 448 my $Ticket; 449 if ( defined $Param{Data}->{Ticket} ) { 450 451 # isolate ticket parameter 452 $Ticket = $Param{Data}->{Ticket}; 453 454 $Ticket->{UserID} = $UserID; 455 456 # remove leading and trailing spaces 457 for my $Attribute ( sort keys %{$Ticket} ) { 458 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 459 460 #remove leading spaces 461 $Ticket->{$Attribute} =~ s{\A\s+}{}; 462 463 #remove trailing spaces 464 $Ticket->{$Attribute} =~ s{\s+\z}{}; 465 } 466 } 467 if ( IsHashRefWithData( $Ticket->{PendingTime} ) ) { 468 for my $Attribute ( sort keys %{ $Ticket->{PendingTime} } ) { 469 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 470 471 #remove leading spaces 472 $Ticket->{PendingTime}->{$Attribute} =~ s{\A\s+}{}; 473 474 #remove trailing spaces 475 $Ticket->{PendingTime}->{$Attribute} =~ s{\s+\z}{}; 476 } 477 } 478 } 479 480 # check Ticket attribute values 481 my $TicketCheck = $Self->_CheckTicket( 482 Ticket => $Ticket, 483 OldTicket => \%TicketData, 484 ); 485 486 if ( !$TicketCheck->{Success} ) { 487 return $Self->ReturnError( %{$TicketCheck} ); 488 } 489 } 490 491 my $Article; 492 if ( defined $Param{Data}->{Article} ) { 493 494 $Article = $Param{Data}->{Article}; 495 $Article->{UserType} = $UserType; 496 497 # remove leading and trailing spaces 498 for my $Attribute ( sort keys %{$Article} ) { 499 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 500 501 #remove leading spaces 502 $Article->{$Attribute} =~ s{\A\s+}{}; 503 504 #remove trailing spaces 505 $Article->{$Attribute} =~ s{\s+\z}{}; 506 } 507 } 508 if ( IsHashRefWithData( $Article->{OrigHeader} ) ) { 509 for my $Attribute ( sort keys %{ $Article->{OrigHeader} } ) { 510 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 511 512 #remove leading spaces 513 $Article->{OrigHeader}->{$Attribute} =~ s{\A\s+}{}; 514 515 #remove trailing spaces 516 $Article->{OrigHeader}->{$Attribute} =~ s{\s+\z}{}; 517 } 518 } 519 } 520 521 # Check attributes that can be set by sysconfig. 522 if ( !$Article->{AutoResponseType} ) { 523 $Article->{AutoResponseType} = $Self->{Config}->{AutoResponseType} || ''; 524 } 525 526 # TODO: GenericInterface::Operation::TicketUpdate###CommunicationChannel 527 if ( !$Article->{CommunicationChannelID} && !$Article->{CommunicationChannel} ) { 528 $Article->{CommunicationChannel} = 'Internal'; 529 } 530 if ( !defined $Article->{IsVisibleForCustomer} ) { 531 $Article->{IsVisibleForCustomer} = $Self->{Config}->{IsVisibleForCustomer} // 1; 532 } 533 if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) { 534 $Article->{SenderType} = $UserType eq 'User' ? 'agent' : 'customer'; 535 } 536 if ( !$Article->{HistoryType} ) { 537 $Article->{HistoryType} = $Self->{Config}->{HistoryType} || ''; 538 } 539 if ( !$Article->{HistoryComment} ) { 540 $Article->{HistoryComment} = $Self->{Config}->{HistoryComment} || ''; 541 } 542 543 # check Article attribute values 544 my $ArticleCheck = $Self->_CheckArticle( Article => $Article ); 545 546 if ( !$ArticleCheck->{Success} ) { 547 if ( !$ArticleCheck->{ErrorCode} ) { 548 return { 549 Success => 0, 550 %{$ArticleCheck}, 551 }; 552 } 553 return $Self->ReturnError( %{$ArticleCheck} ); 554 } 555 } 556 557 my $DynamicField; 558 my @DynamicFieldList; 559 if ( defined $Param{Data}->{DynamicField} ) { 560 561 # isolate DynamicField parameter 562 $DynamicField = $Param{Data}->{DynamicField}; 563 564 # homogenate input to array 565 if ( ref $DynamicField eq 'HASH' ) { 566 push @DynamicFieldList, $DynamicField; 567 } 568 else { 569 @DynamicFieldList = @{$DynamicField}; 570 } 571 572 # check DynamicField internal structure 573 for my $DynamicFieldItem (@DynamicFieldList) { 574 if ( !IsHashRefWithData($DynamicFieldItem) ) { 575 return { 576 ErrorCode => 'TicketUpdate.InvalidParameter', 577 ErrorMessage => 578 "TicketUpdate: Ticket->DynamicField parameter is invalid!", 579 }; 580 } 581 582 # remove leading and trailing spaces 583 for my $Attribute ( sort keys %{$DynamicFieldItem} ) { 584 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 585 586 #remove leading spaces 587 $DynamicFieldItem->{$Attribute} =~ s{\A\s+}{}; 588 589 #remove trailing spaces 590 $DynamicFieldItem->{$Attribute} =~ s{\s+\z}{}; 591 } 592 } 593 594 # check DynamicField attribute values 595 my $DynamicFieldCheck = $Self->_CheckDynamicField( 596 DynamicField => $DynamicFieldItem, 597 Article => $Article, 598 ); 599 600 if ( !$DynamicFieldCheck->{Success} ) { 601 return $Self->ReturnError( %{$DynamicFieldCheck} ); 602 } 603 } 604 } 605 606 my $Attachment; 607 my @AttachmentList; 608 if ( defined $Param{Data}->{Attachment} ) { 609 610 # isolate Attachment parameter 611 $Attachment = $Param{Data}->{Attachment}; 612 613 # homogenate input to array 614 if ( ref $Attachment eq 'HASH' ) { 615 push @AttachmentList, $Attachment; 616 } 617 else { 618 @AttachmentList = @{$Attachment}; 619 } 620 621 # check Attachment internal structure 622 for my $AttachmentItem (@AttachmentList) { 623 if ( !IsHashRefWithData($AttachmentItem) ) { 624 return { 625 ErrorCode => 'TicketUpdate.InvalidParameter', 626 ErrorMessage => 627 "TicketUpdate: Ticket->Attachment parameter is invalid!", 628 }; 629 } 630 631 # remove leading and trailing spaces 632 for my $Attribute ( sort keys %{$AttachmentItem} ) { 633 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 634 635 #remove leading spaces 636 $AttachmentItem->{$Attribute} =~ s{\A\s+}{}; 637 638 #remove trailing spaces 639 $AttachmentItem->{$Attribute} =~ s{\s+\z}{}; 640 } 641 } 642 643 # check Attachment attribute values 644 my $AttachmentCheck = $Self->_CheckAttachment( 645 Attachment => $AttachmentItem, 646 Article => $Article, 647 ); 648 649 if ( !$AttachmentCheck->{Success} ) { 650 return $Self->ReturnError( %{$AttachmentCheck} ); 651 } 652 } 653 } 654 655 return $Self->_TicketUpdate( 656 TicketID => $TicketID, 657 Ticket => $Ticket, 658 Article => $Article, 659 DynamicFieldList => \@DynamicFieldList, 660 AttachmentList => \@AttachmentList, 661 UserID => $UserID, 662 UserType => $UserType, 663 ); 664} 665 666=begin Internal: 667 668=head2 _CheckTicket() 669 670checks if the given ticket parameters are valid. 671 672 my $TicketCheck = $OperationObject->_CheckTicket( 673 Ticket => $Ticket, # all ticket parameters 674 ); 675 676 returns: 677 678 $TicketCheck = { 679 Success => 1, # if everything is OK 680 } 681 682 $TicketCheck = { 683 ErrorCode => 'Function.Error', # if error 684 ErrorMessage => 'Error description', 685 } 686 687=cut 688 689sub _CheckTicket { 690 my ( $Self, %Param ) = @_; 691 692 my $Ticket = $Param{Ticket}; 693 my $OldTicket = $Param{OldTicket}; 694 695 # check Ticket->CustomerUser 696 if ( 697 $Ticket->{CustomerUser} 698 && !$Self->ValidateCustomer( %{$Ticket} ) 699 ) 700 { 701 return { 702 ErrorCode => 'TicketUpdate.InvalidParameter', 703 ErrorMessage => 704 "TicketUpdate: Ticket->CustomerUser parameter is invalid!", 705 }; 706 } 707 708 # check Ticket->Queue 709 if ( $Ticket->{QueueID} || $Ticket->{Queue} ) { 710 if ( !$Self->ValidateQueue( %{$Ticket} ) ) { 711 return { 712 ErrorCode => 'TicketUpdate.InvalidParameter', 713 ErrorMessage => "TicketUpdate: Ticket->QueueID or Ticket->Queue parameter is" 714 . " invalid!", 715 }; 716 } 717 } 718 719 # check Ticket->Lock 720 if ( $Ticket->{LockID} || $Ticket->{Lock} ) { 721 if ( !$Self->ValidateLock( %{$Ticket} ) ) { 722 return { 723 ErrorCode => 'TicketUpdate.InvalidParameter', 724 ErrorMessage => "TicketUpdate: Ticket->LockID or Ticket->Lock parameter is" 725 . " invalid!", 726 }; 727 } 728 } 729 730 # check Ticket->Type 731 if ( $Ticket->{TypeID} || $Ticket->{Type} ) { 732 if ( !$Self->ValidateType( %{$Ticket} ) ) { 733 return { 734 ErrorCode => 'TicketUpdate.InvalidParameter', 735 ErrorMessage => 736 "TicketUpdate: Ticket->TypeID or Ticket->Type parameter is invalid!", 737 }; 738 } 739 } 740 741 # check Ticket->Service 742 if ( $Ticket->{ServiceID} || $Ticket->{Service} ) { 743 744 # set customer user from old ticket if no new customer user is to be set 745 my $CustomerUser = $Ticket->{CustomerUser} || ''; 746 if ( !$CustomerUser ) { 747 $CustomerUser = $OldTicket->{CustomerUserID}; 748 } 749 if ( 750 !$Self->ValidateService( 751 %{$Ticket}, 752 CustomerUser => $CustomerUser, 753 ) 754 ) 755 { 756 return { 757 ErrorCode => 'TicketUpdate.InvalidParameter', 758 ErrorMessage => 759 "TicketUpdate: Ticket->ServiceID or Ticket->Service parameter is invalid!", 760 }; 761 } 762 } 763 764 # check Ticket->SLA 765 if ( $Ticket->{SLAID} || $Ticket->{SLA} ) { 766 767 # set ServiceID from old ticket if no new ServiceID or Service is to be set 768 my $Service = $Ticket->{Service} || ''; 769 my $ServiceID = $Ticket->{ServiceID} || ''; 770 if ( !$ServiceID && !$Service ) { 771 $ServiceID = $OldTicket->{ServiceID}; 772 } 773 774 if ( 775 !$Self->ValidateSLA( 776 %{$Ticket}, 777 Service => $Service, 778 ServiceID => $ServiceID, 779 ) 780 ) 781 { 782 return { 783 ErrorCode => 'TicketUpdate.InvalidParameter', 784 ErrorMessage => 785 "TicketUpdate: Ticket->SLAID or Ticket->SLA parameter is invalid!", 786 }; 787 } 788 } 789 790 # check Ticket->State 791 if ( $Ticket->{StateID} || $Ticket->{State} ) { 792 if ( !$Self->ValidateState( %{$Ticket} ) ) { 793 return { 794 ErrorCode => 'TicketUpdate.InvalidParameter', 795 ErrorMessage => "TicketUpdate: Ticket->StateID or Ticket->State parameter is" 796 . " invalid!", 797 }; 798 } 799 } 800 801 # check Ticket->Priority 802 if ( $Ticket->{PriorityID} || $Ticket->{Priority} ) { 803 if ( !$Self->ValidatePriority( %{$Ticket} ) ) { 804 return { 805 ErrorCode => 'TicketUpdate.InvalidParameter', 806 ErrorMessage => "TicketUpdate: Ticket->PriorityID or Ticket->Priority parameter is" 807 . " invalid!", 808 }; 809 } 810 } 811 812 # check Ticket->Owner 813 if ( $Ticket->{OwnerID} || $Ticket->{Owner} ) { 814 if ( !$Self->ValidateOwner( %{$Ticket} ) ) { 815 return { 816 ErrorCode => 'TicketUpdate.InvalidParameter', 817 ErrorMessage => 818 "TicketUpdate: Ticket->OwnerID or Ticket->Owner parameter is invalid!", 819 }; 820 } 821 } 822 823 # check Ticket->Responsible 824 if ( $Ticket->{ResponsibleID} || $Ticket->{Responsible} ) { 825 if ( !$Self->ValidateResponsible( %{$Ticket} ) ) { 826 return { 827 ErrorCode => 'TicketUpdate.InvalidParameter', 828 ErrorMessage => "TicketUpdate: Ticket->ResponsibleID or Ticket->Responsible" 829 . " parameter is invalid!", 830 }; 831 } 832 } 833 834 # check Ticket->PendingTime 835 if ( $Ticket->{PendingTime} ) { 836 if ( !$Self->ValidatePendingTime( %{$Ticket} ) ) { 837 return { 838 ErrorCode => 'TicketUpdate.InvalidParameter', 839 ErrorMessage => "TicketUpdate: Ticket->PendingTime parameter is invalid!", 840 }; 841 } 842 } 843 844 # if everything is OK then return Success 845 return { 846 Success => 1, 847 }; 848} 849 850=head2 _CheckArticle() 851 852checks if the given article parameter is valid. 853 854 my $ArticleCheck = $OperationObject->_CheckArticle( 855 Article => $Article, # all article parameters 856 ); 857 858 returns: 859 860 $ArticleCheck = { 861 Success => 1, # if everything is OK 862 } 863 864 $ArticleCheck = { 865 ErrorCode => 'Function.Error', # if error 866 ErrorMessage => 'Error description', 867 } 868 869=cut 870 871sub _CheckArticle { 872 my ( $Self, %Param ) = @_; 873 874 my $Article = $Param{Article}; 875 876 # check ticket internally 877 for my $Needed (qw(Subject Body AutoResponseType)) { 878 if ( !$Article->{$Needed} ) { 879 return { 880 ErrorCode => 'TicketUpdate.MissingParameter', 881 ErrorMessage => "TicketUpdate: Article->$Needed parameter is missing!", 882 }; 883 } 884 } 885 886 # check Article->AutoResponseType 887 if ( !$Article->{AutoResponseType} ) { 888 889 # return internal server error 890 return { 891 ErrorMessage => "TicketUpdate: Article->AutoResponseType parameter is required and", 892 }; 893 } 894 895 if ( !$Self->ValidateAutoResponseType( %{$Article} ) ) { 896 return { 897 ErrorCode => 'TicketUpdate.InvalidParameter', 898 ErrorMessage => "TicketUpdate: Article->AutoResponseType parameter is invalid!", 899 }; 900 } 901 902 # check Article->CommunicationChannel 903 if ( !$Article->{CommunicationChannel} && !$Article->{CommunicationChannelID} ) { 904 905 # return internal server error 906 return { 907 ErrorMessage => "TicketUpdate: Article->CommunicationChannelID or Article->CommunicationChannel parameter" 908 . " is required and Sysconfig CommunicationChannelID setting could not be read!" 909 }; 910 } 911 if ( !$Self->ValidateArticleCommunicationChannel( %{$Article} ) ) { 912 return { 913 ErrorCode => 'TicketUpdate.InvalidParameter', 914 ErrorMessage => "TicketUpdate: Article->CommunicationChannel or Article->CommunicationChannelID parameter" 915 . " is invalid or not supported!", 916 }; 917 } 918 919 # check Article->SenderType 920 if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) { 921 922 # return internal server error 923 return { 924 ErrorMessage => "TicketUpdate: Article->SenderTypeID or Article->SenderType parameter" 925 . " is required and Sysconfig SenderTypeID setting could not be read!" 926 }; 927 } 928 if ( !$Self->ValidateSenderType( %{$Article} ) ) { 929 return { 930 ErrorCode => 'TicketUpdate.InvalidParameter', 931 ErrorMessage => "TicketUpdate: Article->SenderTypeID or Ticket->SenderType parameter" 932 . " is invalid!", 933 }; 934 } 935 936 # check Article->From 937 if ( $Article->{From} ) { 938 if ( !$Self->ValidateFrom( %{$Article} ) ) { 939 return { 940 ErrorCode => 'TicketUpdate.InvalidParameter', 941 ErrorMessage => "TicketUpdate: Article->From parameter is invalid!", 942 }; 943 } 944 } 945 946 # check Article->ContentType vs Article->MimeType and Article->Charset 947 if ( !$Article->{ContentType} && !$Article->{MimeType} && !$Article->{Charset} ) { 948 return { 949 ErrorCode => 'TicketUpdate.MissingParameter', 950 ErrorMessage => "TicketUpdate: Article->ContentType or Ticket->MimeType and" 951 . " Article->Charset parameters are required!", 952 }; 953 } 954 955 if ( $Article->{MimeType} && !$Article->{Charset} ) { 956 return { 957 ErrorCode => 'TicketUpdate.MissingParameter', 958 ErrorMessage => "TicketUpdate: Article->Charset is required!", 959 }; 960 } 961 962 if ( $Article->{Charset} && !$Article->{MimeType} ) { 963 return { 964 ErrorCode => 'TicketUpdate.MissingParameter', 965 ErrorMessage => "TicketUpdate: Article->MimeType is required!", 966 }; 967 } 968 969 # check Article->MimeType 970 if ( $Article->{MimeType} ) { 971 972 $Article->{MimeType} = lc $Article->{MimeType}; 973 974 if ( !$Self->ValidateMimeType( %{$Article} ) ) { 975 return { 976 ErrorCode => 'TicketUpdate.InvalidParameter', 977 ErrorMessage => "TicketUpdate: Article->MimeType is invalid!", 978 }; 979 } 980 } 981 982 # check Article->MimeType 983 if ( $Article->{Charset} ) { 984 985 $Article->{Charset} = lc $Article->{Charset}; 986 987 if ( !$Self->ValidateCharset( %{$Article} ) ) { 988 return { 989 ErrorCode => 'TicketUpdate.InvalidParameter', 990 ErrorMessage => "TicketUpdate: Article->Charset is invalid!", 991 }; 992 } 993 } 994 995 # check Article->ContentType 996 if ( $Article->{ContentType} ) { 997 998 $Article->{ContentType} = lc $Article->{ContentType}; 999 1000 # check Charset part 1001 my $Charset = ''; 1002 if ( $Article->{ContentType} =~ /charset=/i ) { 1003 $Charset = $Article->{ContentType}; 1004 $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi; 1005 $Charset =~ s/"|'//g; 1006 $Charset =~ s/(.+?);.*/$1/g; 1007 } 1008 1009 if ( !$Self->ValidateCharset( Charset => $Charset ) ) { 1010 return { 1011 ErrorCode => 'TicketUpdate.InvalidParameter', 1012 ErrorMessage => "TicketUpdate: Article->ContentType is invalid!", 1013 }; 1014 } 1015 1016 # check MimeType part 1017 my $MimeType = ''; 1018 if ( $Article->{ContentType} =~ /^(\w+\/\w+)/i ) { 1019 $MimeType = $1; 1020 $MimeType =~ s/"|'//g; 1021 } 1022 1023 if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) { 1024 return { 1025 ErrorCode => 'TicketUpdate.InvalidParameter', 1026 ErrorMessage => "TicketUpdate: Article->ContentType is invalid!", 1027 }; 1028 } 1029 } 1030 1031 # check Article->HistoryType 1032 if ( !$Article->{HistoryType} ) { 1033 1034 # return internal server error 1035 return { 1036 ErrorMessage => "TicketUpdate: Article-> HistoryType is required and Sysconfig" 1037 . " HistoryType setting could not be read!" 1038 }; 1039 } 1040 if ( !$Self->ValidateHistoryType( %{$Article} ) ) { 1041 return { 1042 ErrorCode => 'TicketUpdate.InvalidParameter', 1043 ErrorMessage => "TicketUpdate: Article->HistoryType parameter is invalid!", 1044 }; 1045 } 1046 1047 # check Article->HistoryComment 1048 if ( !$Article->{HistoryComment} ) { 1049 1050 # return internal server error 1051 return { 1052 ErrorMessage => "TicketUpdate: Article->HistoryComment is required and Sysconfig" 1053 . " HistoryComment setting could not be read!" 1054 }; 1055 } 1056 1057 # get config object 1058 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 1059 1060 # check Article->TimeUnit 1061 # TimeUnit could be required or not depending on sysconfig option 1062 if ( 1063 ( !defined $Article->{TimeUnit} || !IsStringWithData( $Article->{TimeUnit} ) ) 1064 && $ConfigObject->{'Ticket::Frontend::AccountTime'} 1065 && $ConfigObject->{'Ticket::Frontend::NeedAccountedTime'} 1066 ) 1067 { 1068 return { 1069 ErrorCode => 'TicketUpdate.MissingParameter', 1070 ErrorMessage => "TicketUpdate: Article->TimeUnit is required by sysconfig option!", 1071 }; 1072 } 1073 if ( $Article->{TimeUnit} ) { 1074 if ( !$Self->ValidateTimeUnit( %{$Article} ) ) { 1075 return { 1076 ErrorCode => 'TicketUpdate.InvalidParameter', 1077 ErrorMessage => "TicketUpdate: Article->TimeUnit parameter is invalid!", 1078 }; 1079 } 1080 } 1081 1082 # check Article->NoAgentNotify 1083 if ( $Article->{NoAgentNotify} && $Article->{NoAgentNotify} ne '1' ) { 1084 return { 1085 ErrorCode => 'TicketUpdate.InvalidParameter', 1086 ErrorMessage => "TicketUpdate: Article->NoAgent parameter is invalid!", 1087 }; 1088 } 1089 1090 # check Article array parameters 1091 for my $Attribute ( 1092 qw( ForceNotificationToUserID ExcludeNotificationToUserID ExcludeMuteNotificationToUserID ) 1093 ) 1094 { 1095 if ( defined $Article->{$Attribute} ) { 1096 1097 # check structure 1098 if ( IsHashRefWithData( $Article->{$Attribute} ) ) { 1099 return { 1100 ErrorCode => 'TicketUpdate.InvalidParameter', 1101 ErrorMessage => "TicketUpdate: Article->$Attribute parameter is invalid!", 1102 }; 1103 } 1104 else { 1105 if ( !IsArrayRefWithData( $Article->{$Attribute} ) ) { 1106 $Article->{$Attribute} = [ $Article->{$Attribute} ]; 1107 } 1108 for my $UserID ( @{ $Article->{$Attribute} } ) { 1109 if ( !$Self->ValidateUserID( UserID => $UserID ) ) { 1110 return { 1111 ErrorCode => 'TicketUpdate.InvalidParameter', 1112 ErrorMessage => "TicketUpdate: Article->$Attribute UserID=$UserID" 1113 . " parameter is invalid!", 1114 }; 1115 } 1116 } 1117 } 1118 } 1119 } 1120 1121 # if everything is OK then return Success 1122 return { 1123 Success => 1, 1124 }; 1125} 1126 1127=head2 _CheckDynamicField() 1128 1129checks if the given dynamic field parameter is valid. 1130 1131 my $DynamicFieldCheck = $OperationObject->_CheckDynamicField( 1132 DynamicField => $DynamicField, # all dynamic field parameters 1133 ); 1134 1135 returns: 1136 1137 $DynamicFieldCheck = { 1138 Success => 1, # if everything is OK 1139 } 1140 1141 $DynamicFieldCheck = { 1142 ErrorCode => 'Function.Error', # if error 1143 ErrorMessage => 'Error description', 1144 } 1145 1146=cut 1147 1148sub _CheckDynamicField { 1149 my ( $Self, %Param ) = @_; 1150 1151 my $DynamicField = $Param{DynamicField}; 1152 my $ArticleData = $Param{Article}; 1153 1154 my $Article; 1155 if ( IsHashRefWithData($ArticleData) ) { 1156 $Article = 1; 1157 } 1158 1159 # check DynamicField item internally 1160 for my $Needed (qw(Name Value)) { 1161 if ( 1162 !defined $DynamicField->{$Needed} 1163 || ( !IsString( $DynamicField->{$Needed} ) && ref $DynamicField->{$Needed} ne 'ARRAY' ) 1164 ) 1165 { 1166 return { 1167 ErrorCode => 'TicketUpdate.MissingParameter', 1168 ErrorMessage => "TicketUpdate: DynamicField->$Needed parameter is missing!", 1169 }; 1170 } 1171 } 1172 1173 # check DynamicField->Name 1174 if ( !$Self->ValidateDynamicFieldName( %{$DynamicField} ) ) { 1175 return { 1176 ErrorCode => 'TicketUpdate.InvalidParameter', 1177 ErrorMessage => "TicketUpdate: DynamicField->Name parameter is invalid!", 1178 }; 1179 } 1180 1181 # check objectType for dynamic field 1182 if ( 1183 !$Self->ValidateDynamicFieldObjectType( 1184 %{$DynamicField}, 1185 Article => $Article, 1186 ) 1187 ) 1188 { 1189 return { 1190 ErrorCode => 'TicketUpdate.MissingParameter', 1191 ErrorMessage => 1192 "TicketUpdate: To create an article DynamicField an article is required!", 1193 }; 1194 } 1195 1196 # check DynamicField->Value 1197 if ( !$Self->ValidateDynamicFieldValue( %{$DynamicField} ) ) { 1198 return { 1199 ErrorCode => 'TicketUpdate.InvalidParameter', 1200 ErrorMessage => "TicketUpdate: DynamicField->Value parameter is invalid!", 1201 }; 1202 } 1203 1204 # if everything is OK then return Success 1205 return { 1206 Success => 1, 1207 }; 1208} 1209 1210=head2 _CheckAttachment() 1211 1212checks if the given attachment parameter is valid. 1213 1214 my $AttachmentCheck = $OperationObject->_CheckAttachment( 1215 Attachment => $Attachment, # all attachment parameters 1216 ); 1217 1218 returns: 1219 1220 $AttachmentCheck = { 1221 Success => 1, # if everything is OK 1222 } 1223 1224 $AttachmentCheck = { 1225 ErrorCode => 'Function.Error', # if error 1226 ErrorMessage => 'Error description', 1227 } 1228 1229=cut 1230 1231sub _CheckAttachment { 1232 my ( $Self, %Param ) = @_; 1233 1234 my $Attachment = $Param{Attachment}; 1235 my $Article = $Param{Article}; 1236 1237 # check if article is going to be created 1238 if ( !IsHashRefWithData($Article) ) { 1239 return { 1240 ErrorCode => 'TicketUpdate.MissingParameter', 1241 ErrorMessage => "TicketUpdate: To create an attachment an article is needed!", 1242 }; 1243 } 1244 1245 # check attachment item internally 1246 for my $Needed (qw(Content ContentType Filename)) { 1247 if ( !IsStringWithData( $Attachment->{$Needed} ) ) { 1248 return { 1249 ErrorCode => 'TicketUpdate.MissingParameter', 1250 ErrorMessage => "TicketUpdate: Attachment->$Needed parameter is missing!", 1251 }; 1252 } 1253 } 1254 1255 # check Article->ContentType 1256 if ( $Attachment->{ContentType} ) { 1257 1258 $Attachment->{ContentType} = lc $Attachment->{ContentType}; 1259 1260 # check Charset part 1261 my $Charset = ''; 1262 if ( $Attachment->{ContentType} =~ /charset=/i ) { 1263 $Charset = $Attachment->{ContentType}; 1264 $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi; 1265 $Charset =~ s/"|'//g; 1266 $Charset =~ s/(.+?);.*/$1/g; 1267 } 1268 1269 if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) ) { 1270 return { 1271 ErrorCode => 'TicketUpdate.InvalidParameter', 1272 ErrorMessage => "TicketUpdate: Attachment->ContentType is invalid!", 1273 }; 1274 } 1275 1276 # check MimeType part 1277 my $MimeType = ''; 1278 if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) { 1279 $MimeType = $1; 1280 $MimeType =~ s/"|'//g; 1281 } 1282 1283 if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) { 1284 return { 1285 ErrorCode => 'TicketUpdate.InvalidParameter', 1286 ErrorMessage => "TicketUpdate: Attachment->ContentType is invalid!", 1287 }; 1288 } 1289 } 1290 1291 # if everything is OK then return Success 1292 return { 1293 Success => 1, 1294 }; 1295} 1296 1297=head2 _CheckUpdatePermissions() 1298 1299check if user has permissions to update ticket attributes. 1300 1301 my $Response = $OperationObject->_CheckUpdatePermissions( 1302 TicketID => 123 1303 Ticket => $Ticket, # all ticket parameters 1304 Article => $Ticket, # all attachment parameters 1305 DynamicField => $Ticket, # all dynamic field parameters 1306 Attachment => $Ticket, # all attachment parameters 1307 UserID => 123, 1308 ); 1309 1310 returns: 1311 1312 $Response = { 1313 Success => 1, # if everything is OK 1314 } 1315 1316 $Response = { 1317 Success => 0, 1318 ErrorCode => "function.error", # if error 1319 ErrorMessage => "Error description" 1320 } 1321 1322=cut 1323 1324sub _CheckUpdatePermissions { 1325 my ( $Self, %Param ) = @_; 1326 1327 my $TicketID = $Param{TicketID}; 1328 my $Ticket = $Param{Ticket}; 1329 my $Article = $Param{Article}; 1330 my $DynamicFieldList = $Param{DynamicFieldList}; 1331 my $AttachmentList = $Param{AttachmentList}; 1332 1333 # get ticket object 1334 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 1335 1336 # check Article permissions 1337 if ( IsHashRefWithData($Article) ) { 1338 my $Access = $TicketObject->TicketPermission( 1339 Type => 'note', 1340 TicketID => $TicketID, 1341 UserID => $Param{UserID}, 1342 ); 1343 if ( !$Access ) { 1344 return { 1345 ErrorCode => 'TicketUpdate.AccessDenied', 1346 ErrorMessage => "TicketUpdate: Does not have permissions to create new articles!", 1347 }; 1348 } 1349 } 1350 1351 # check dynamic field permissions 1352 if ( IsArrayRefWithData($DynamicFieldList) ) { 1353 my $Access = $TicketObject->TicketPermission( 1354 Type => 'rw', 1355 TicketID => $TicketID, 1356 UserID => $Param{UserID}, 1357 ); 1358 if ( !$Access ) { 1359 return { 1360 ErrorCode => 'TicketUpdate.AccessDenied', 1361 ErrorMessage => "TicketUpdate: Does not have permissions to update dynamic fields!", 1362 }; 1363 } 1364 } 1365 1366 # check queue permissions 1367 if ( $Ticket->{Queue} || $Ticket->{QueueID} ) { 1368 my $Access = $TicketObject->TicketPermission( 1369 Type => 'move', 1370 TicketID => $TicketID, 1371 UserID => $Param{UserID}, 1372 ); 1373 if ( !$Access ) { 1374 return { 1375 ErrorCode => 'TicketUpdate.AccessDenied', 1376 ErrorMessage => "TicketUpdate: Does not have permissions to update queue!", 1377 }; 1378 } 1379 } 1380 1381 # check owner permissions 1382 if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) { 1383 my $Access = $TicketObject->TicketPermission( 1384 Type => 'owner', 1385 TicketID => $TicketID, 1386 UserID => $Param{UserID}, 1387 ); 1388 if ( !$Access ) { 1389 return { 1390 ErrorCode => 'TicketUpdate.AccessDenied', 1391 ErrorMessage => "TicketUpdate: Does not have permissions to update owner!", 1392 }; 1393 } 1394 } 1395 1396 # check responsible permissions 1397 if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) { 1398 my $Access = $TicketObject->TicketPermission( 1399 Type => 'responsible', 1400 TicketID => $TicketID, 1401 UserID => $Param{UserID}, 1402 ); 1403 if ( !$Access ) { 1404 return { 1405 ErrorCode => 'TicketUpdate.AccessDenied', 1406 ErrorMessage => "TicketUpdate: Does not have permissions to update responsibe!", 1407 }; 1408 } 1409 } 1410 1411 # check priority permissions 1412 if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) { 1413 my $Access = $TicketObject->TicketPermission( 1414 Type => 'priority', 1415 TicketID => $TicketID, 1416 UserID => $Param{UserID}, 1417 ); 1418 if ( !$Access ) { 1419 return { 1420 ErrorCode => 'TicketUpdate.AccessDenied', 1421 ErrorMessage => "TicketUpdate: Does not have permissions to update priority!", 1422 }; 1423 } 1424 } 1425 1426 # check state permissions 1427 if ( $Ticket->{State} || $Ticket->{StateID} ) { 1428 1429 # get State Data 1430 my %StateData; 1431 my $StateID; 1432 1433 # get state object 1434 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 1435 1436 if ( $Ticket->{StateID} ) { 1437 $StateID = $Ticket->{StateID}; 1438 } 1439 else { 1440 $StateID = $StateObject->StateLookup( 1441 State => $Ticket->{State}, 1442 ); 1443 } 1444 1445 %StateData = $StateObject->StateGet( 1446 ID => $StateID, 1447 ); 1448 1449 my $Access = 1; 1450 1451 if ( $StateData{TypeName} =~ /^close/i ) { 1452 $Access = $TicketObject->TicketPermission( 1453 Type => 'close', 1454 TicketID => $TicketID, 1455 UserID => $Param{UserID}, 1456 ); 1457 } 1458 1459 # set pending time 1460 elsif ( $StateData{TypeName} =~ /^pending/i ) { 1461 $Access = $TicketObject->TicketPermission( 1462 Type => 'close', 1463 TicketID => $TicketID, 1464 UserID => $Param{UserID}, 1465 ); 1466 } 1467 if ( !$Access ) { 1468 return { 1469 ErrorCode => 'TicketUpdate.AccessDenied', 1470 ErrorMessage => "TicketUpdate: Does not have permissions to update state!", 1471 }; 1472 } 1473 } 1474 1475 return { 1476 Success => 1, 1477 }; 1478} 1479 1480=head2 _TicketUpdate() 1481 1482updates a ticket and creates an article and sets dynamic fields and attachments if specified. 1483 1484 my $Response = $OperationObject->_TicketUpdate( 1485 TicketID => 123 1486 Ticket => $Ticket, # all ticket parameters 1487 Article => $Article, # all attachment parameters 1488 DynamicField => $DynamicField, # all dynamic field parameters 1489 Attachment => $Attachment, # all attachment parameters 1490 UserID => 123, 1491 UserType => 'Agent' # || 'Customer 1492 ); 1493 1494 returns: 1495 1496 $Response = { 1497 Success => 1, # if everything is OK 1498 Data => { 1499 TicketID => 123, 1500 TicketNumber => 'TN3422332', 1501 ArticleID => 123, # if new article was created 1502 } 1503 } 1504 1505 $Response = { 1506 Success => 0, # if unexpected error 1507 ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}", 1508 } 1509 1510=cut 1511 1512sub _TicketUpdate { 1513 my ( $Self, %Param ) = @_; 1514 1515 my $TicketID = $Param{TicketID}; 1516 my $Ticket = $Param{Ticket}; 1517 my $Article = $Param{Article}; 1518 my $DynamicFieldList = $Param{DynamicFieldList}; 1519 my $AttachmentList = $Param{AttachmentList}; 1520 1521 my $Access = $Self->_CheckUpdatePermissions(%Param); 1522 1523 # if no permissions return error 1524 if ( !$Access->{Success} ) { 1525 return $Self->ReturnError( %{$Access} ); 1526 } 1527 1528 my %CustomerUserData; 1529 1530 # get customer information 1531 if ( $Ticket->{CustomerUser} ) { 1532 %CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet( 1533 User => $Ticket->{CustomerUser}, 1534 ); 1535 } 1536 1537 # get ticket object 1538 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 1539 1540 # get current ticket data 1541 my %TicketData = $TicketObject->TicketGet( 1542 TicketID => $TicketID, 1543 DynamicFields => 0, 1544 UserID => $Param{UserID}, 1545 ); 1546 1547 # update ticket parameters 1548 # update Ticket->Title 1549 if ( 1550 defined $Ticket->{Title} 1551 && $Ticket->{Title} ne '' 1552 && $Ticket->{Title} ne $TicketData{Title} 1553 ) 1554 { 1555 my $Success = $TicketObject->TicketTitleUpdate( 1556 Title => $Ticket->{Title}, 1557 TicketID => $TicketID, 1558 UserID => $Param{UserID}, 1559 ); 1560 if ( !$Success ) { 1561 return { 1562 Success => 0, 1563 Errormessage => 1564 'Ticket title could not be updated, please contact system administrator!', 1565 }; 1566 } 1567 } 1568 1569 # update Ticket->Queue 1570 if ( $Ticket->{Queue} || $Ticket->{QueueID} ) { 1571 my $Success; 1572 if ( defined $Ticket->{Queue} && $Ticket->{Queue} ne $TicketData{Queue} ) { 1573 $Success = $TicketObject->TicketQueueSet( 1574 Queue => $Ticket->{Queue}, 1575 TicketID => $TicketID, 1576 UserID => $Param{UserID}, 1577 ); 1578 } 1579 elsif ( defined $Ticket->{QueueID} && $Ticket->{QueueID} ne $TicketData{QueueID} ) { 1580 $Success = $TicketObject->TicketQueueSet( 1581 QueueID => $Ticket->{QueueID}, 1582 TicketID => $TicketID, 1583 UserID => $Param{UserID}, 1584 ); 1585 } 1586 else { 1587 1588 # data is the same as in ticket nothing to do 1589 $Success = 1; 1590 } 1591 1592 if ( !$Success ) { 1593 return { 1594 Success => 0, 1595 ErrorMessage => 1596 'Ticket queue could not be updated, please contact system administrator!', 1597 }; 1598 } 1599 } 1600 1601 # update Ticket->Lock 1602 if ( $Ticket->{Lock} || $Ticket->{LockID} ) { 1603 my $Success; 1604 if ( defined $Ticket->{Lock} && $Ticket->{Lock} ne $TicketData{Lock} ) { 1605 $Success = $TicketObject->TicketLockSet( 1606 Lock => $Ticket->{Lock}, 1607 TicketID => $TicketID, 1608 UserID => $Param{UserID}, 1609 ); 1610 } 1611 elsif ( defined $Ticket->{LockID} && $Ticket->{LockID} ne $TicketData{LockID} ) { 1612 $Success = $TicketObject->TicketLockSet( 1613 LockID => $Ticket->{LockID}, 1614 TicketID => $TicketID, 1615 UserID => $Param{UserID}, 1616 ); 1617 } 1618 else { 1619 1620 # data is the same as in ticket nothing to do 1621 $Success = 1; 1622 } 1623 1624 if ( !$Success ) { 1625 return { 1626 Success => 0, 1627 Errormessage => 1628 'Ticket lock could not be updated, please contact system administrator!', 1629 }; 1630 } 1631 } 1632 1633 # update Ticket->Type 1634 if ( $Ticket->{Type} || $Ticket->{TypeID} ) { 1635 my $Success; 1636 if ( defined $Ticket->{Type} && $Ticket->{Type} ne $TicketData{Type} ) { 1637 $Success = $TicketObject->TicketTypeSet( 1638 Type => $Ticket->{Type}, 1639 TicketID => $TicketID, 1640 UserID => $Param{UserID}, 1641 ); 1642 } 1643 elsif ( defined $Ticket->{TypeID} && $Ticket->{TypeID} ne $TicketData{TypeID} ) 1644 { 1645 $Success = $TicketObject->TicketTypeSet( 1646 TypeID => $Ticket->{TypeID}, 1647 TicketID => $TicketID, 1648 UserID => $Param{UserID}, 1649 ); 1650 } 1651 else { 1652 1653 # data is the same as in ticket nothing to do 1654 $Success = 1; 1655 } 1656 1657 if ( !$Success ) { 1658 return { 1659 Success => 0, 1660 Errormessage => 1661 'Ticket type could not be updated, please contact system administrator!', 1662 }; 1663 } 1664 } 1665 1666 # update Ticket>State 1667 # depending on the state, might require to unlock ticket or enables pending time set 1668 if ( $Ticket->{State} || $Ticket->{StateID} ) { 1669 1670 # get State Data 1671 my %StateData; 1672 my $StateID; 1673 1674 # get state object 1675 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 1676 1677 if ( $Ticket->{StateID} ) { 1678 $StateID = $Ticket->{StateID}; 1679 } 1680 else { 1681 $StateID = $StateObject->StateLookup( 1682 State => $Ticket->{State}, 1683 ); 1684 } 1685 1686 %StateData = $StateObject->StateGet( 1687 ID => $StateID, 1688 ); 1689 1690 # force unlock if state type is close 1691 if ( $StateData{TypeName} =~ /^close/i ) { 1692 1693 # set lock 1694 $TicketObject->TicketLockSet( 1695 TicketID => $TicketID, 1696 Lock => 'unlock', 1697 UserID => $Param{UserID}, 1698 ); 1699 } 1700 1701 # set pending time 1702 elsif ( $StateData{TypeName} =~ /^pending/i ) { 1703 1704 # set pending time 1705 if ( defined $Ticket->{PendingTime} ) { 1706 my $Success = $TicketObject->TicketPendingTimeSet( 1707 UserID => $Param{UserID}, 1708 TicketID => $TicketID, 1709 %{ $Ticket->{PendingTime} }, 1710 ); 1711 1712 if ( !$Success ) { 1713 return { 1714 Success => 0, 1715 Errormessage => 1716 'Ticket pendig time could not be updated, please contact system' 1717 . ' administrator!', 1718 }; 1719 } 1720 } 1721 else { 1722 return $Self->ReturnError( 1723 ErrorCode => 'TicketUpdate.MissingParameter', 1724 ErrorMessage => 'Can\'t set a ticket on a pending state without pendig time!' 1725 ); 1726 } 1727 } 1728 1729 my $Success; 1730 if ( defined $Ticket->{State} && $Ticket->{State} ne $TicketData{State} ) { 1731 $Success = $TicketObject->TicketStateSet( 1732 State => $Ticket->{State}, 1733 TicketID => $TicketID, 1734 UserID => $Param{UserID}, 1735 ); 1736 } 1737 elsif ( defined $Ticket->{StateID} && $Ticket->{StateID} ne $TicketData{StateID} ) 1738 { 1739 $Success = $TicketObject->TicketStateSet( 1740 StateID => $Ticket->{StateID}, 1741 TicketID => $TicketID, 1742 UserID => $Param{UserID}, 1743 ); 1744 } 1745 else { 1746 1747 # data is the same as in ticket nothing to do 1748 $Success = 1; 1749 } 1750 1751 if ( !$Success ) { 1752 return { 1753 Success => 0, 1754 Errormessage => 1755 'Ticket state could not be updated, please contact system administrator!', 1756 }; 1757 } 1758 } 1759 1760 # update Ticket->Service 1761 # this might reset SLA if current SLA is not available for the new service 1762 if ( $Ticket->{Service} || $Ticket->{ServiceID} ) { 1763 1764 # check if ticket has a SLA assigned 1765 if ( $TicketData{SLAID} ) { 1766 1767 # check if old SLA is still valid 1768 if ( 1769 !$Self->ValidateSLA( 1770 SLAID => $TicketData{SLAID}, 1771 Service => $Ticket->{Service} || '', 1772 ServiceID => $Ticket->{ServiceID} || '', 1773 ) 1774 ) 1775 { 1776 1777 # remove current SLA if is not compatible with new service 1778 my $Success = $TicketObject->TicketSLASet( 1779 SLAID => '', 1780 TicketID => $TicketID, 1781 UserID => $Param{UserID}, 1782 ); 1783 } 1784 } 1785 1786 my $Success; 1787 1788 # prevent comparison errors on undefined values 1789 if ( !defined $TicketData{Service} ) { 1790 $TicketData{Service} = ''; 1791 } 1792 if ( !defined $TicketData{ServiceID} ) { 1793 $TicketData{ServiceID} = ''; 1794 } 1795 1796 if ( defined $Ticket->{Service} && $Ticket->{Service} ne $TicketData{Service} ) { 1797 $Success = $TicketObject->TicketServiceSet( 1798 Service => $Ticket->{Service}, 1799 TicketID => $TicketID, 1800 UserID => $Param{UserID}, 1801 ); 1802 } 1803 elsif ( defined $Ticket->{ServiceID} && $Ticket->{ServiceID} ne $TicketData{ServiceID} ) 1804 { 1805 $Success = $TicketObject->TicketServiceSet( 1806 ServiceID => $Ticket->{ServiceID}, 1807 TicketID => $TicketID, 1808 UserID => $Param{UserID}, 1809 ); 1810 } 1811 else { 1812 1813 # data is the same as in ticket nothing to do 1814 $Success = 1; 1815 } 1816 1817 if ( !$Success ) { 1818 return { 1819 Success => 0, 1820 Errormessage => 1821 'Ticket service could not be updated, please contact system administrator!', 1822 }; 1823 } 1824 } 1825 1826 # update Ticket->SLA 1827 if ( $Ticket->{SLA} || $Ticket->{SLAID} ) { 1828 my $Success; 1829 1830 # prevent comparison errors on undefined values 1831 if ( !defined $TicketData{SLA} ) { 1832 $TicketData{SLA} = ''; 1833 } 1834 if ( !defined $TicketData{SLAID} ) { 1835 $TicketData{SLAID} = ''; 1836 } 1837 1838 if ( defined $Ticket->{SLA} && $Ticket->{SLA} ne $TicketData{SLA} ) { 1839 $Success = $TicketObject->TicketSLASet( 1840 SLA => $Ticket->{SLA}, 1841 TicketID => $TicketID, 1842 UserID => $Param{UserID}, 1843 ); 1844 } 1845 elsif ( defined $Ticket->{SLAID} && $Ticket->{SLAID} ne $TicketData{SLAID} ) 1846 { 1847 $Success = $TicketObject->TicketSLASet( 1848 SLAID => $Ticket->{SLAID}, 1849 TicketID => $TicketID, 1850 UserID => $Param{UserID}, 1851 ); 1852 } 1853 else { 1854 1855 # data is the same as in ticket nothing to do 1856 $Success = 1; 1857 } 1858 1859 if ( !$Success ) { 1860 return { 1861 Success => 0, 1862 Errormessage => 1863 'Ticket SLA could not be updated, please contact system administrator!', 1864 }; 1865 } 1866 } 1867 1868 # update Ticket->CustomerUser && Ticket->CustomerID 1869 if ( $Ticket->{CustomerUser} || $Ticket->{CustomerID} ) { 1870 1871 # set values to empty if they are not defined 1872 $TicketData{CustomerUserID} = $TicketData{CustomerUserID} || ''; 1873 $TicketData{CustomerID} = $TicketData{CustomerID} || ''; 1874 $Ticket->{CustomerUser} = $Ticket->{CustomerUser} || ''; 1875 $Ticket->{CustomerID} = $Ticket->{CustomerID} || ''; 1876 1877 my $Success; 1878 if ( 1879 $Ticket->{CustomerUser} ne $TicketData{CustomerUserID} 1880 || $Ticket->{CustomerID} ne $TicketData{CustomerID} 1881 ) 1882 { 1883 my $CustomerID = $CustomerUserData{UserCustomerID} || ''; 1884 1885 # use user defined CustomerID if defined 1886 if ( defined $Ticket->{CustomerID} && $Ticket->{CustomerID} ne '' ) { 1887 $CustomerID = $Ticket->{CustomerID}; 1888 } 1889 1890 $Success = $TicketObject->TicketCustomerSet( 1891 No => $CustomerID, 1892 User => $Ticket->{CustomerUser}, 1893 TicketID => $TicketID, 1894 UserID => $Param{UserID}, 1895 ); 1896 } 1897 else { 1898 1899 # data is the same as in ticket nothing to do 1900 $Success = 1; 1901 } 1902 1903 if ( !$Success ) { 1904 return { 1905 Success => 0, 1906 Errormessage => 1907 'Ticket customer user could not be updated, please contact system administrator!', 1908 }; 1909 } 1910 } 1911 1912 # update Ticket->Priority 1913 if ( $Ticket->{Priority} || $Ticket->{PriorityID} ) { 1914 my $Success; 1915 if ( defined $Ticket->{Priority} && $Ticket->{Priority} ne $TicketData{Priority} ) { 1916 $Success = $TicketObject->TicketPrioritySet( 1917 Priority => $Ticket->{Priority}, 1918 TicketID => $TicketID, 1919 UserID => $Param{UserID}, 1920 ); 1921 } 1922 elsif ( defined $Ticket->{PriorityID} && $Ticket->{PriorityID} ne $TicketData{PriorityID} ) 1923 { 1924 $Success = $TicketObject->TicketPrioritySet( 1925 PriorityID => $Ticket->{PriorityID}, 1926 TicketID => $TicketID, 1927 UserID => $Param{UserID}, 1928 ); 1929 } 1930 else { 1931 1932 # data is the same as in ticket nothing to do 1933 $Success = 1; 1934 } 1935 1936 if ( !$Success ) { 1937 return { 1938 Success => 0, 1939 Errormessage => 1940 'Ticket priority could not be updated, please contact system administrator!', 1941 }; 1942 } 1943 } 1944 1945 my $UnlockOnAway = 1; 1946 1947 # update Ticket->Owner 1948 if ( $Ticket->{Owner} || $Ticket->{OwnerID} ) { 1949 my $Success; 1950 if ( defined $Ticket->{Owner} && $Ticket->{Owner} ne $TicketData{Owner} ) { 1951 $Success = $TicketObject->TicketOwnerSet( 1952 NewUser => $Ticket->{Owner}, 1953 TicketID => $TicketID, 1954 UserID => $Param{UserID}, 1955 ); 1956 $UnlockOnAway = 0; 1957 } 1958 elsif ( defined $Ticket->{OwnerID} && $Ticket->{OwnerID} ne $TicketData{OwnerID} ) 1959 { 1960 $Success = $TicketObject->TicketOwnerSet( 1961 NewUserID => $Ticket->{OwnerID}, 1962 TicketID => $TicketID, 1963 UserID => $Param{UserID}, 1964 ); 1965 $UnlockOnAway = 0; 1966 } 1967 else { 1968 1969 # data is the same as in ticket nothing to do 1970 $Success = 1; 1971 } 1972 1973 if ( !$Success ) { 1974 return { 1975 Success => 0, 1976 Errormessage => 1977 'Ticket owner could not be updated, please contact system administrator!', 1978 }; 1979 } 1980 } 1981 1982 # update Ticket->Responsible 1983 if ( $Ticket->{Responsible} || $Ticket->{ResponsibleID} ) { 1984 my $Success; 1985 if ( 1986 defined $Ticket->{Responsible} 1987 && $Ticket->{Responsible} ne $TicketData{Responsible} 1988 ) 1989 { 1990 $Success = $TicketObject->TicketResponsibleSet( 1991 NewUser => $Ticket->{Responsible}, 1992 TicketID => $TicketID, 1993 UserID => $Param{UserID}, 1994 ); 1995 } 1996 elsif ( 1997 defined $Ticket->{ResponsibleID} 1998 && $Ticket->{ResponsibleID} ne $TicketData{ResponsibleID} 1999 ) 2000 { 2001 $Success = $TicketObject->TicketResponsibleSet( 2002 NewUserID => $Ticket->{ResponsibleID}, 2003 TicketID => $TicketID, 2004 UserID => $Param{UserID}, 2005 ); 2006 } 2007 else { 2008 2009 # data is the same as in ticket nothing to do 2010 $Success = 1; 2011 } 2012 2013 if ( !$Success ) { 2014 return { 2015 Success => 0, 2016 Errormessage => 2017 'Ticket responsible could not be updated, please contact system administrator!', 2018 }; 2019 } 2020 } 2021 2022 my $ArticleID; 2023 if ( IsHashRefWithData($Article) ) { 2024 2025 # set Article From 2026 my $From; 2027 if ( $Article->{From} ) { 2028 $From = $Article->{From}; 2029 } 2030 elsif ( $Param{UserType} eq 'Customer' ) { 2031 2032 # use data from customer user (if customer user is in database) 2033 if ( IsHashRefWithData( \%CustomerUserData ) ) { 2034 $From = '"' 2035 . $CustomerUserData{UserFullname} . '"' 2036 . ' <' . $CustomerUserData{UserEmail} . '>'; 2037 } 2038 2039 # otherwise use customer user as sent from the request (it should be an email) 2040 else { 2041 $From = $Ticket->{CustomerUser}; 2042 } 2043 } 2044 else { 2045 my %UserData = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 2046 UserID => $Param{UserID}, 2047 ); 2048 $From = $UserData{UserFullname}; 2049 } 2050 2051 # Set Article To, Cc, Bcc. 2052 my ( $To, $Cc, $Bcc ); 2053 if ( $Article->{To} ) { 2054 $To = $Article->{To}; 2055 } 2056 if ( $Article->{Cc} ) { 2057 $Cc = $Article->{Cc}; 2058 } 2059 if ( $Article->{Bcc} ) { 2060 $Bcc = $Article->{Bcc}; 2061 } 2062 2063 # Fallback for To 2064 if ( !$To && $Article->{CommunicationChannel} eq 'Email' ) { 2065 2066 # Use data from customer user (if customer user is in database). 2067 if ( IsHashRefWithData( \%CustomerUserData ) ) { 2068 $To = '"' . $CustomerUserData{UserFullname} . '"' 2069 . ' <' . $CustomerUserData{UserEmail} . '>'; 2070 } 2071 2072 # Otherwise use customer user as sent from the request (it should be an email). 2073 else { 2074 $To = $Ticket->{CustomerUser} // $TicketData{CustomerUserID}; 2075 } 2076 } 2077 2078 if ( !$Article->{CommunicationChannel} ) { 2079 2080 my %CommunicationChannel = $Kernel::OM->Get('Kernel::System::CommunicationChannel')->ChannelGet( 2081 ChannelID => $Article->{CommunicationChannelID}, 2082 ); 2083 $Article->{CommunicationChannel} = $CommunicationChannel{ChannelName}; 2084 } 2085 2086 my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel( 2087 ChannelName => $Article->{CommunicationChannel}, 2088 ); 2089 2090 my $PlainBody = $Article->{Body}; 2091 2092 # Convert article body to plain text, if HTML content was supplied. This is necessary since auto response code 2093 # expects plain text content. Please see bug#13397 for more information. 2094 if ( $Article->{ContentType} =~ /text\/html/i || $Article->{MimeType} =~ /text\/html/i ) { 2095 $PlainBody = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii( 2096 String => $Article->{Body}, 2097 ); 2098 } 2099 2100 # Create article. 2101 $ArticleID = $ArticleBackendObject->ArticleCreate( 2102 NoAgentNotify => $Article->{NoAgentNotify} || 0, 2103 TicketID => $TicketID, 2104 SenderTypeID => $Article->{SenderTypeID} || '', 2105 SenderType => $Article->{SenderType} || '', 2106 IsVisibleForCustomer => $Article->{IsVisibleForCustomer}, 2107 From => $From, 2108 To => $To, 2109 Cc => $Cc, 2110 Bcc => $Bcc, 2111 Subject => $Article->{Subject}, 2112 Body => $Article->{Body}, 2113 MimeType => $Article->{MimeType} || '', 2114 Charset => $Article->{Charset} || '', 2115 ContentType => $Article->{ContentType} || '', 2116 UserID => $Param{UserID}, 2117 HistoryType => $Article->{HistoryType}, 2118 HistoryComment => $Article->{HistoryComment} || '%%', 2119 AutoResponseType => $Article->{AutoResponseType}, 2120 UnlockOnAway => $UnlockOnAway, 2121 OrigHeader => { 2122 From => $From, 2123 To => $To, 2124 Subject => $Article->{Subject}, 2125 Body => $PlainBody, 2126 }, 2127 ); 2128 2129 if ( !$ArticleID ) { 2130 return { 2131 Success => 0, 2132 ErrorMessage => 2133 'Article could not be created, please contact the system administrator' 2134 }; 2135 } 2136 2137 # time accounting 2138 if ( $Article->{TimeUnit} ) { 2139 $TicketObject->TicketAccountTime( 2140 TicketID => $TicketID, 2141 ArticleID => $ArticleID, 2142 TimeUnit => $Article->{TimeUnit}, 2143 UserID => $Param{UserID}, 2144 ); 2145 } 2146 } 2147 2148 # set dynamic fields 2149 for my $DynamicField ( @{$DynamicFieldList} ) { 2150 my $Result = $Self->SetDynamicFieldValue( 2151 %{$DynamicField}, 2152 TicketID => $TicketID, 2153 ArticleID => $ArticleID || '', 2154 UserID => $Param{UserID}, 2155 ); 2156 2157 if ( !$Result->{Success} ) { 2158 my $ErrorMessage = 2159 $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be set," 2160 . " please contact the system administrator"; 2161 2162 return { 2163 Success => 0, 2164 ErrorMessage => $ErrorMessage, 2165 }; 2166 } 2167 } 2168 2169 # set attachments 2170 2171 for my $Attachment ( @{$AttachmentList} ) { 2172 my $Result = $Self->CreateAttachment( 2173 Attachment => $Attachment, 2174 TicketID => $TicketID, 2175 ArticleID => $ArticleID || '', 2176 UserID => $Param{UserID} 2177 ); 2178 2179 if ( !$Result->{Success} ) { 2180 my $ErrorMessage = 2181 $Result->{ErrorMessage} || "Attachment could not be created, please contact the " 2182 . " system administrator"; 2183 2184 return { 2185 Success => 0, 2186 ErrorMessage => $ErrorMessage, 2187 }; 2188 } 2189 } 2190 2191 # get web service configuration 2192 my $Webservice = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet( 2193 ID => $Self->{WebserviceID}, 2194 ); 2195 2196 my $IncludeTicketData; 2197 2198 # Get operation config, if operation name was supplied. 2199 if ( $Self->{Operation} ) { 2200 my $OperationConfig = $Webservice->{Config}->{Provider}->{Operation}->{ $Self->{Operation} }; 2201 $IncludeTicketData = $OperationConfig->{IncludeTicketData}; 2202 } 2203 2204 if ( !$IncludeTicketData ) { 2205 if ($ArticleID) { 2206 return { 2207 Success => 1, 2208 Data => { 2209 TicketID => $TicketID, 2210 TicketNumber => $TicketData{TicketNumber}, 2211 ArticleID => $ArticleID, 2212 }, 2213 }; 2214 } 2215 return { 2216 Success => 1, 2217 Data => { 2218 TicketID => $TicketID, 2219 TicketNumber => $TicketData{TicketNumber}, 2220 }, 2221 }; 2222 } 2223 2224 # get updated TicketData 2225 %TicketData = (); 2226 %TicketData = $TicketObject->TicketGet( 2227 TicketID => $TicketID, 2228 DynamicFields => 1, 2229 UserID => $Param{UserID}, 2230 ); 2231 2232 # extract all dynamic fields from main ticket hash. 2233 my %TicketDynamicFields; 2234 TICKETATTRIBUTE: 2235 for my $TicketAttribute ( sort keys %TicketData ) { 2236 if ( $TicketAttribute =~ m{\A DynamicField_(.*) \z}msx ) { 2237 $TicketDynamicFields{$1} = { 2238 Name => $1, 2239 Value => $TicketData{$TicketAttribute}, 2240 }; 2241 delete $TicketData{$TicketAttribute}; 2242 } 2243 } 2244 2245 # add dynamic fields as array into 'DynamicField' hash key if any 2246 if (%TicketDynamicFields) { 2247 $TicketData{DynamicField} = [ sort { $a->{Name} cmp $b->{Name} } values %TicketDynamicFields ]; 2248 } 2249 2250 # get last ArticleID 2251 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 2252 my $ArticleBackendObject = $ArticleObject->BackendForArticle( 2253 ArticleID => $ArticleID, 2254 TicketID => $TicketID 2255 ); 2256 2257 my @Articles = $ArticleObject->ArticleList( 2258 TicketID => $TicketID, 2259 OnlyLast => 1, 2260 ); 2261 2262 my $LastArticleID = $Articles[0]->{ArticleID}; 2263 2264 # return ticket data if we have no article data 2265 if ( !$ArticleID && !$LastArticleID ) { 2266 return { 2267 Success => 1, 2268 Data => { 2269 TicketID => $TicketID, 2270 TicketNumber => $TicketData{TicketNumber}, 2271 Ticket => \%TicketData, 2272 }, 2273 }; 2274 } 2275 2276 # get Article and ArticleAttachement 2277 my %ArticleData = $ArticleBackendObject->ArticleGet( 2278 ArticleID => $ArticleID || $LastArticleID, 2279 DynamicFields => 1, 2280 TicketID => $TicketID, 2281 ); 2282 2283 # prepare Article DynamicFields 2284 my @ArticleDynamicFields; 2285 2286 # remove all dynamic fields from main ticket hash and set them into an array. 2287 ARTICLEATTRIBUTE: 2288 for my $ArticleAttribute ( sort keys %ArticleData ) { 2289 if ( $ArticleAttribute =~ m{\A DynamicField_(.*) \z}msx ) { 2290 if ( !exists $TicketDynamicFields{$1} ) { 2291 push @ArticleDynamicFields, { 2292 Name => $1, 2293 Value => $ArticleData{$ArticleAttribute}, 2294 }; 2295 } 2296 2297 delete $ArticleData{$ArticleAttribute}; 2298 } 2299 } 2300 2301 # add dynamic fields array into 'DynamicField' hash key if any 2302 if (@ArticleDynamicFields) { 2303 $ArticleData{DynamicField} = \@ArticleDynamicFields; 2304 } 2305 2306 # add attachment if the request includes attachments 2307 if ( IsArrayRefWithData($AttachmentList) ) { 2308 my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 2309 ArticleID => $ArticleData{ArticleID}, 2310 ); 2311 2312 my @Attachments; 2313 $Kernel::OM->Get('Kernel::System::Main')->Require('MIME::Base64'); 2314 ATTACHMENT: 2315 for my $FileID ( sort keys %AttachmentIndex ) { 2316 next ATTACHMENT if !$FileID; 2317 my %Attachment = $ArticleBackendObject->ArticleAttachment( 2318 ArticleID => $ArticleData{ArticleID}, 2319 FileID => $FileID, 2320 ); 2321 2322 next ATTACHMENT if !IsHashRefWithData( \%Attachment ); 2323 2324 # convert content to base64, but prevent 76 chars brake, see bug#14500. 2325 $Attachment{Content} = MIME::Base64::encode_base64( $Attachment{Content}, '' ); 2326 push @Attachments, {%Attachment}; 2327 } 2328 2329 # set Attachments data 2330 if (@Attachments) { 2331 $ArticleData{Attachment} = \@Attachments; 2332 } 2333 } 2334 2335 $TicketData{Article} = \%ArticleData; 2336 2337 # return ticket data and article data 2338 return { 2339 Success => 1, 2340 Data => { 2341 TicketID => $TicketID, 2342 TicketNumber => $TicketData{TicketNumber}, 2343 ArticleID => $ArticleData{ArticleID}, 2344 Ticket => \%TicketData, 2345 }, 2346 }; 2347} 2348 23491; 2350 2351=end Internal: 2352 2353=head1 TERMS AND CONDITIONS 2354 2355This software is part of the OTRS project (L<https://otrs.org/>). 2356 2357This software comes with ABSOLUTELY NO WARRANTY. For details, see 2358the enclosed file COPYING for license information (GPL). If you 2359did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 2360 2361=cut 2362