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::TicketCreate; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsString IsStringWithData); 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::TicketCreate - GenericInterface Ticket TicketCreate 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::TicketCreate'); 55 $Self->{Operation} = $Param{Operation}; 56 57 return $Self; 58} 59 60=head2 Run() 61 62perform TicketCreate Operation. This will return the created ticket number. 63 64 my $Result = $OperationObject->Run( 65 Data => { 66 UserLogin => 'some agent login', # UserLogin or CustomerUserLogin or SessionID is 67 # required 68 CustomerUserLogin => 'some customer login', 69 SessionID => 123, 70 71 Password => 'some password', # if UserLogin or CustomerUserLogin is sent then 72 # Password is required 73 74 Ticket => { 75 Title => 'some ticket title', 76 77 QueueID => 123, # QueueID or Queue is required 78 Queue => 'some queue name', 79 80 LockID => 123, # optional 81 Lock => 'some lock name', # optional 82 TypeID => 123, # optional 83 Type => 'some type name', # optional 84 ServiceID => 123, # optional 85 Service => 'some service name', # optional 86 SLAID => 123, # optional 87 SLA => 'some SLA name', # optional 88 89 StateID => 123, # StateID or State is required 90 State => 'some state name', 91 92 PriorityID => 123, # PriorityID or Priority is required 93 Priority => 'some priority name', 94 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 => { 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 ContentType => 'some content type', # ContentType or MimeType and Charset is required 124 MimeType => 'some mime type', 125 Charset => 'some charset', 126 HistoryType => 'some history type', # optional 127 HistoryComment => 'Some history comment', # optional 128 TimeUnit => 123, # optional 129 NoAgentNotify => 1, # optional 130 ForceNotificationToUserID => [1, 2, 3] # optional 131 ExcludeNotificationToUserID => [1, 2, 3] # optional 132 ExcludeMuteNotificationToUserID => [1, 2, 3] # optional 133 }, 134 135 DynamicField => [ # optional 136 { 137 Name => 'some name', 138 Value => $Value, # value type depends on the dynamic field 139 }, 140 # ... 141 ], 142 # or 143 # DynamicField => { 144 # Name => 'some name', 145 # Value => $Value, 146 #}, 147 148 Attachment => [ 149 { 150 Content => 'content' # base64 encoded 151 ContentType => 'some content type' 152 Filename => 'some fine name' 153 }, 154 # ... 155 ], 156 #or 157 #Attachment => { 158 # Content => 'content' 159 # ContentType => 'some content type' 160 # Filename => 'some fine name' 161 #}, 162 }, 163 ); 164 165 $Result = { 166 Success => 1, # 0 or 1 167 ErrorMessage => '', # in case of error 168 Data => { # result data payload after Operation 169 TicketID => 123, # Ticket ID number in OTRS (help desk system) 170 TicketNumber => 2324454323322 # Ticket Number in OTRS (Help desk system) 171 ArticleID => 43, # Article ID number in OTRS (help desk system) 172 Error => { # should not return errors 173 ErrorCode => 'Ticket.Create.ErrorCode' 174 ErrorMessage => 'Error Description' 175 }, 176 177 # If IncludeTicketData is enabled 178 Ticket => [ 179 { 180 TicketNumber => '20101027000001', 181 Title => 'some title', 182 TicketID => 123, 183 State => 'some state', 184 StateID => 123, 185 StateType => 'some state type', 186 Priority => 'some priority', 187 PriorityID => 123, 188 Lock => 'lock', 189 LockID => 123, 190 Queue => 'some queue', 191 QueueID => 123, 192 CustomerID => 'customer_id_123', 193 CustomerUserID => 'customer_user_id_123', 194 Owner => 'some_owner_login', 195 OwnerID => 123, 196 Type => 'some ticket type', 197 TypeID => 123, 198 SLA => 'some sla', 199 SLAID => 123, 200 Service => 'some service', 201 ServiceID => 123, 202 Responsible => 'some_responsible_login', 203 ResponsibleID => 123, 204 Age => 3456, 205 Created => '2010-10-27 20:15:00' 206 CreateBy => 123, 207 Changed => '2010-10-27 20:15:15', 208 ChangeBy => 123, 209 ArchiveFlag => 'y', 210 211 DynamicField => [ 212 { 213 Name => 'some name', 214 Value => 'some value', 215 }, 216 ], 217 218 # (time stamps of expected escalations) 219 EscalationResponseTime (unix time stamp of response time escalation) 220 EscalationUpdateTime (unix time stamp of update time escalation) 221 EscalationSolutionTime (unix time stamp of solution time escalation) 222 223 # (general escalation info of nearest escalation type) 224 EscalationDestinationIn (escalation in e. g. 1h 4m) 225 EscalationDestinationTime (date of escalation in unix time, e. g. 72193292) 226 EscalationDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 227 EscalationTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 228 EscalationTime (seconds total till escalation of nearest escalation time type - response, update or solution time, e. g. "3600") 229 230 # (detailed escalation info about first response, update and solution time) 231 FirstResponseTimeEscalation (if true, ticket is escalated) 232 FirstResponseTimeNotification (if true, notify - x% of escalation has reached) 233 FirstResponseTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 234 FirstResponseTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 235 FirstResponseTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 236 FirstResponseTime (seconds total till escalation, e. g. "3600") 237 238 UpdateTimeEscalation (if true, ticket is escalated) 239 UpdateTimeNotification (if true, notify - x% of escalation has reached) 240 UpdateTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 241 UpdateTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 242 UpdateTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 243 UpdateTime (seconds total till escalation, e. g. "3600") 244 245 SolutionTimeEscalation (if true, ticket is escalated) 246 SolutionTimeNotification (if true, notify - x% of escalation has reached) 247 SolutionTimeDestinationTime (date of escalation in unix time, e. g. 72193292) 248 SolutionTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00") 249 SolutionTimeWorkingTime (seconds of working/service time till escalation, e. g. "1800") 250 SolutionTime (seconds total till escalation, e. g. "3600") 251 252 Article => [ 253 { 254 ArticleID 255 From 256 To 257 Cc 258 Subject 259 Body 260 ReplyTo 261 MessageID 262 InReplyTo 263 References 264 SenderType 265 SenderTypeID 266 CommunicationChannelID 267 IsVisibleForCustomer 268 ContentType 269 Charset 270 MimeType 271 IncomingTime 272 273 DynamicField => [ 274 { 275 Name => 'some name', 276 Value => 'some value', 277 }, 278 ], 279 280 Attachment => [ 281 { 282 Content => "xxxx", # actual attachment contents, base64 enconded 283 ContentAlternative => "", 284 ContentID => "", 285 ContentType => "application/pdf", 286 Filename => "StdAttachment-Test1.pdf", 287 Filesize => "4.6 KBytes", 288 FilesizeRaw => 4722, 289 }, 290 ], 291 }, 292 ], 293 }, 294 ], 295 }, 296 }; 297 298=cut 299 300sub Run { 301 my ( $Self, %Param ) = @_; 302 303 my $Result = $Self->Init( 304 WebserviceID => $Self->{WebserviceID}, 305 ); 306 307 if ( !$Result->{Success} ) { 308 $Self->ReturnError( 309 ErrorCode => 'Webservice.InvalidConfiguration', 310 ErrorMessage => $Result->{ErrorMessage}, 311 ); 312 } 313 314 # check needed stuff 315 if ( 316 !$Param{Data}->{UserLogin} 317 && !$Param{Data}->{CustomerUserLogin} 318 && !$Param{Data}->{SessionID} 319 ) 320 { 321 return $Self->ReturnError( 322 ErrorCode => 'TicketCreate.MissingParameter', 323 ErrorMessage => "TicketCreate: UserLogin, CustomerUserLogin or SessionID is required!", 324 ); 325 } 326 327 if ( $Param{Data}->{UserLogin} || $Param{Data}->{CustomerUserLogin} ) { 328 329 if ( !$Param{Data}->{Password} ) 330 { 331 return $Self->ReturnError( 332 ErrorCode => 'TicketCreate.MissingParameter', 333 ErrorMessage => "TicketCreate: Password or SessionID is required!", 334 ); 335 } 336 } 337 338 # authenticate user 339 my ( $UserID, $UserType ) = $Self->Auth( 340 %Param, 341 ); 342 343 if ( !$UserID ) { 344 return $Self->ReturnError( 345 ErrorCode => 'TicketCreate.AuthFail', 346 ErrorMessage => "TicketCreate: User could not be authenticated!", 347 ); 348 } 349 350 my $PermissionUserID = $UserID; 351 if ( $UserType eq 'Customer' ) { 352 $UserID = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanelUserID'); 353 } 354 355 # check needed hashes 356 for my $Needed (qw(Ticket Article)) { 357 if ( !IsHashRefWithData( $Param{Data}->{$Needed} ) ) { 358 return $Self->ReturnError( 359 ErrorCode => 'TicketCreate.MissingParameter', 360 ErrorMessage => "TicketCreate: $Needed parameter is missing or not valid!", 361 ); 362 } 363 } 364 365 # check optional array/hashes 366 for my $Optional (qw(DynamicField Attachment)) { 367 if ( 368 defined $Param{Data}->{$Optional} 369 && !IsHashRefWithData( $Param{Data}->{$Optional} ) 370 && !IsArrayRefWithData( $Param{Data}->{$Optional} ) 371 ) 372 { 373 return $Self->ReturnError( 374 ErrorCode => 'TicketCreate.MissingParameter', 375 ErrorMessage => "TicketCreate: $Optional parameter is missing or not valid!", 376 ); 377 } 378 } 379 380 # isolate ticket parameter 381 my $Ticket = $Param{Data}->{Ticket}; 382 383 # remove leading and trailing spaces 384 for my $Attribute ( sort keys %{$Ticket} ) { 385 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 386 387 #remove leading spaces 388 $Ticket->{$Attribute} =~ s{\A\s+}{}; 389 390 #remove trailing spaces 391 $Ticket->{$Attribute} =~ s{\s+\z}{}; 392 } 393 } 394 if ( IsHashRefWithData( $Ticket->{PendingTime} ) ) { 395 for my $Attribute ( sort keys %{ $Ticket->{PendingTime} } ) { 396 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 397 398 #remove leading spaces 399 $Ticket->{PendingTime}->{$Attribute} =~ s{\A\s+}{}; 400 401 #remove trailing spaces 402 $Ticket->{PendingTime}->{$Attribute} =~ s{\s+\z}{}; 403 } 404 } 405 } 406 407 # check Ticket attribute values 408 my $TicketCheck = $Self->_CheckTicket( Ticket => $Ticket ); 409 410 if ( !$TicketCheck->{Success} ) { 411 return $Self->ReturnError( %{$TicketCheck} ); 412 } 413 414 # check create permissions 415 my $Permission = $Self->CheckCreatePermissions( 416 Ticket => $Ticket, 417 UserID => $PermissionUserID, 418 UserType => $UserType, 419 ); 420 421 if ( !$Permission ) { 422 return $Self->ReturnError( 423 ErrorCode => 'TicketCreate.AccessDenied', 424 ErrorMessage => "TicketCreate: Can not create tickets in given Queue or QueueID!", 425 ); 426 } 427 428 # isolate Article parameter 429 my $Article = $Param{Data}->{Article}; 430 $Article->{UserType} = $UserType; 431 432 # remove leading and trailing spaces 433 for my $Attribute ( sort keys %{$Article} ) { 434 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 435 436 #remove leading spaces 437 $Article->{$Attribute} =~ s{\A\s+}{}; 438 439 #remove trailing spaces 440 $Article->{$Attribute} =~ s{\s+\z}{}; 441 } 442 } 443 if ( IsHashRefWithData( $Article->{OrigHeader} ) ) { 444 for my $Attribute ( sort keys %{ $Article->{OrigHeader} } ) { 445 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 446 447 #remove leading spaces 448 $Article->{OrigHeader}->{$Attribute} =~ s{\A\s+}{}; 449 450 #remove trailing spaces 451 $Article->{OrigHeader}->{$Attribute} =~ s{\s+\z}{}; 452 } 453 } 454 } 455 456 # Check attributes that can be set by sysconfig. 457 if ( !$Article->{AutoResponseType} ) { 458 $Article->{AutoResponseType} = $Self->{Config}->{AutoResponseType} || ''; 459 } 460 461 # TODO: GenericInterface::Operation::TicketCreate###CommunicationChannel 462 if ( !$Article->{CommunicationChannelID} && !$Article->{CommunicationChannel} ) { 463 $Article->{CommunicationChannel} = 'Internal'; 464 } 465 if ( !defined $Article->{IsVisibleForCustomer} ) { 466 $Article->{IsVisibleForCustomer} = $Self->{Config}->{IsVisibleForCustomer} // 1; 467 } 468 if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) { 469 $Article->{SenderType} = $UserType eq 'User' ? 'agent' : 'customer'; 470 } 471 if ( !$Article->{HistoryType} ) { 472 $Article->{HistoryType} = $Self->{Config}->{HistoryType} || ''; 473 } 474 if ( !$Article->{HistoryComment} ) { 475 $Article->{HistoryComment} = $Self->{Config}->{HistoryComment} || ''; 476 } 477 478 # check Article attribute values 479 my $ArticleCheck = $Self->_CheckArticle( Article => $Article ); 480 481 if ( !$ArticleCheck->{Success} ) { 482 if ( !$ArticleCheck->{ErrorCode} ) { 483 return { 484 Success => 0, 485 %{$ArticleCheck}, 486 }; 487 } 488 return $Self->ReturnError( %{$ArticleCheck} ); 489 } 490 491 my $DynamicField; 492 my @DynamicFieldList; 493 494 if ( defined $Param{Data}->{DynamicField} ) { 495 496 # isolate DynamicField parameter 497 $DynamicField = $Param{Data}->{DynamicField}; 498 499 # homogenate input to array 500 if ( ref $DynamicField eq 'HASH' ) { 501 push @DynamicFieldList, $DynamicField; 502 } 503 else { 504 @DynamicFieldList = @{$DynamicField}; 505 } 506 507 # check DynamicField internal structure 508 for my $DynamicFieldItem (@DynamicFieldList) { 509 if ( !IsHashRefWithData($DynamicFieldItem) ) { 510 return { 511 ErrorCode => 'TicketCreate.InvalidParameter', 512 ErrorMessage => 513 "TicketCreate: Ticket->DynamicField parameter is invalid!", 514 }; 515 } 516 517 # remove leading and trailing spaces 518 for my $Attribute ( sort keys %{$DynamicFieldItem} ) { 519 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 520 521 #remove leading spaces 522 $DynamicFieldItem->{$Attribute} =~ s{\A\s+}{}; 523 524 #remove trailing spaces 525 $DynamicFieldItem->{$Attribute} =~ s{\s+\z}{}; 526 } 527 } 528 529 # check DynamicField attribute values 530 my $DynamicFieldCheck = $Self->_CheckDynamicField( DynamicField => $DynamicFieldItem ); 531 532 if ( !$DynamicFieldCheck->{Success} ) { 533 return $Self->ReturnError( %{$DynamicFieldCheck} ); 534 } 535 } 536 } 537 538 my $Attachment; 539 my @AttachmentList; 540 541 if ( defined $Param{Data}->{Attachment} ) { 542 543 # isolate Attachment parameter 544 $Attachment = $Param{Data}->{Attachment}; 545 546 # homogenate input to array 547 if ( ref $Attachment eq 'HASH' ) { 548 push @AttachmentList, $Attachment; 549 } 550 else { 551 @AttachmentList = @{$Attachment}; 552 } 553 554 # check Attachment internal structure 555 for my $AttachmentItem (@AttachmentList) { 556 if ( !IsHashRefWithData($AttachmentItem) ) { 557 return { 558 ErrorCode => 'TicketCreate.InvalidParameter', 559 ErrorMessage => 560 "TicketCreate: Ticket->Attachment parameter is invalid!", 561 }; 562 } 563 564 # remove leading and trailing spaces 565 for my $Attribute ( sort keys %{$AttachmentItem} ) { 566 if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) { 567 568 #remove leading spaces 569 $AttachmentItem->{$Attribute} =~ s{\A\s+}{}; 570 571 #remove trailing spaces 572 $AttachmentItem->{$Attribute} =~ s{\s+\z}{}; 573 } 574 } 575 576 # check Attachment attribute values 577 my $AttachmentCheck = $Self->_CheckAttachment( Attachment => $AttachmentItem ); 578 579 if ( !$AttachmentCheck->{Success} ) { 580 return $Self->ReturnError( %{$AttachmentCheck} ); 581 } 582 } 583 } 584 585 return $Self->_TicketCreate( 586 Ticket => $Ticket, 587 Article => $Article, 588 DynamicFieldList => \@DynamicFieldList, 589 AttachmentList => \@AttachmentList, 590 UserID => $UserID, 591 ); 592} 593 594=begin Internal: 595 596=head2 _CheckTicket() 597 598checks if the given ticket parameters are valid. 599 600 my $TicketCheck = $OperationObject->_CheckTicket( 601 Ticket => $Ticket, # all ticket parameters 602 ); 603 604 returns: 605 606 $TicketCheck = { 607 Success => 1, # if everything is OK 608 } 609 610 $TicketCheck = { 611 ErrorCode => 'Function.Error', # if error 612 ErrorMessage => 'Error description', 613 } 614 615=cut 616 617sub _CheckTicket { 618 my ( $Self, %Param ) = @_; 619 620 my $Ticket = $Param{Ticket}; 621 622 # check ticket internally 623 for my $Needed (qw(Title CustomerUser)) { 624 if ( !$Ticket->{$Needed} ) { 625 return { 626 ErrorCode => 'TicketCreate.MissingParameter', 627 ErrorMessage => "TicketCreate: Ticket->$Needed parameter is missing!", 628 }; 629 } 630 } 631 632 # check Ticket->CustomerUser 633 if ( !$Self->ValidateCustomer( %{$Ticket} ) ) { 634 return { 635 ErrorCode => 'TicketCreate.InvalidParameter', 636 ErrorMessage => 637 "TicketCreate: Ticket->CustomerUser parameter is invalid!", 638 }; 639 } 640 641 # check Ticket->Queue 642 if ( !$Ticket->{QueueID} && !$Ticket->{Queue} ) { 643 return { 644 ErrorCode => 'TicketCreate.MissingParameter', 645 ErrorMessage => "TicketCreate: Ticket->QueueID or Ticket->Queue parameter is required!", 646 }; 647 } 648 if ( !$Self->ValidateQueue( %{$Ticket} ) ) { 649 return { 650 ErrorCode => 'TicketCreate.InvalidParameter', 651 ErrorMessage => "TicketCreate: Ticket->QueueID or Ticket->Queue parameter is invalid!", 652 }; 653 } 654 655 # check Ticket->Lock 656 if ( $Ticket->{LockID} || $Ticket->{Lock} ) { 657 if ( !$Self->ValidateLock( %{$Ticket} ) ) { 658 return { 659 ErrorCode => 'TicketCreate.InvalidParameter', 660 ErrorMessage => "TicketCreate: Ticket->LockID or Ticket->Lock parameter is" 661 . " invalid!", 662 }; 663 } 664 } 665 666 # check Ticket->Type 667 # Ticket type could be required or not depending on sysconfig option 668 if ( 669 !$Ticket->{TypeID} 670 && !$Ticket->{Type} 671 && $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Type') 672 ) 673 { 674 return { 675 ErrorCode => 'TicketCreate.MissingParameter', 676 ErrorMessage => "TicketCreate: Ticket->TypeID or Ticket->Type parameter is required" 677 . " by sysconfig option!", 678 }; 679 } 680 if ( $Ticket->{TypeID} || $Ticket->{Type} ) { 681 if ( !$Self->ValidateType( %{$Ticket} ) ) { 682 return { 683 ErrorCode => 'TicketCreate.InvalidParameter', 684 ErrorMessage => 685 "TicketCreate: Ticket->TypeID or Ticket->Type parameter is invalid!", 686 }; 687 } 688 } 689 690 # check Ticket->Service 691 if ( $Ticket->{ServiceID} || $Ticket->{Service} ) { 692 if ( !$Self->ValidateService( %{$Ticket} ) ) { 693 return { 694 ErrorCode => 'TicketCreate.InvalidParameter', 695 ErrorMessage => 696 "TicketCreate: Ticket->ServiceID or Ticket->Service parameter is invalid!", 697 }; 698 } 699 } 700 701 # check Ticket->SLA 702 if ( $Ticket->{SLAID} || $Ticket->{SLA} ) { 703 if ( !$Self->ValidateSLA( %{$Ticket} ) ) { 704 return { 705 ErrorCode => 'TicketCreate.InvalidParameter', 706 ErrorMessage => 707 "TicketCreate: Ticket->SLAID or Ticket->SLA parameter is invalid!", 708 }; 709 } 710 } 711 712 # check Ticket->State 713 if ( !$Ticket->{StateID} && !$Ticket->{State} ) { 714 return { 715 ErrorCode => 'TicketCreate.MissingParameter', 716 ErrorMessage => "TicketCreate: Ticket->StateID or Ticket->State parameter is required!", 717 }; 718 } 719 if ( !$Self->ValidateState( %{$Ticket} ) ) { 720 return { 721 ErrorCode => 'TicketCreate.InvalidParameter', 722 ErrorMessage => "TicketCreate: Ticket->StateID or Ticket->State parameter is invalid!", 723 }; 724 } 725 726 # check Ticket->Priority 727 if ( !$Ticket->{PriorityID} && !$Ticket->{Priority} ) { 728 return { 729 ErrorCode => 'TicketCreate.MissingParameter', 730 ErrorMessage => "TicketCreate: Ticket->PriorityID or Ticket->Priority parameter is" 731 . " required!", 732 }; 733 } 734 if ( !$Self->ValidatePriority( %{$Ticket} ) ) { 735 return { 736 ErrorCode => 'TicketCreate.InvalidParameter', 737 ErrorMessage => "TicketCreate: Ticket->PriorityID or Ticket->Priority parameter is" 738 . " invalid!", 739 }; 740 } 741 742 # check Ticket->Owner 743 if ( $Ticket->{OwnerID} || $Ticket->{Owner} ) { 744 if ( !$Self->ValidateOwner( %{$Ticket} ) ) { 745 return { 746 ErrorCode => 'TicketCreate.InvalidParameter', 747 ErrorMessage => 748 "TicketCreate: Ticket->OwnerID or Ticket->Owner parameter is invalid!", 749 }; 750 } 751 } 752 753 # check Ticket->Responsible 754 if ( $Ticket->{ResponsibleID} || $Ticket->{Responsible} ) { 755 if ( !$Self->ValidateResponsible( %{$Ticket} ) ) { 756 return { 757 ErrorCode => 'TicketCreate.InvalidParameter', 758 ErrorMessage => "TicketCreate: Ticket->ResponsibleID or Ticket->Responsible" 759 . " parameter is invalid!", 760 }; 761 } 762 } 763 764 # check Ticket->PendingTime 765 if ( $Ticket->{PendingTime} ) { 766 if ( !$Self->ValidatePendingTime( %{$Ticket} ) ) { 767 return { 768 ErrorCode => 'TicketCreate.InvalidParameter', 769 ErrorMessage => "TicketCreate: Ticket->PendingTime parameter is invalid!", 770 }; 771 } 772 } 773 774 # if everything is OK then return Success 775 return { 776 Success => 1, 777 }; 778} 779 780=head2 _CheckArticle() 781 782checks if the given article parameter is valid. 783 784 my $ArticleCheck = $OperationObject->_CheckArticle( 785 Article => $Article, # all article parameters 786 ); 787 788 returns: 789 790 $ArticleCheck = { 791 Success => 1, # if everething is OK 792 } 793 794 $ArticleCheck = { 795 ErrorCode => 'Function.Error', # if error 796 ErrorMessage => 'Error description', 797 } 798 799=cut 800 801sub _CheckArticle { 802 my ( $Self, %Param ) = @_; 803 804 my $Article = $Param{Article}; 805 806 # check ticket internally 807 for my $Needed (qw(Subject Body AutoResponseType)) { 808 if ( !$Article->{$Needed} ) { 809 return { 810 ErrorCode => 'TicketCreate.MissingParameter', 811 ErrorMessage => "TicketCreate: Article->$Needed parameter is missing!", 812 }; 813 } 814 } 815 816 # check Article->AutoResponseType 817 if ( !$Article->{AutoResponseType} ) { 818 819 # return internal server error 820 return { 821 ErrorMessage => "TicketCreate: Article->AutoResponseType parameter is required!" 822 }; 823 } 824 825 if ( !$Self->ValidateAutoResponseType( %{$Article} ) ) { 826 return { 827 ErrorCode => 'TicketCreate.InvalidParameter', 828 ErrorMessage => "TicketCreate: Article->AutoResponseType parameter is invalid!", 829 }; 830 } 831 832 # check Article->CommunicationChannel 833 if ( !$Article->{CommunicationChannel} && !$Article->{CommunicationChannelID} ) { 834 835 # return internal server error 836 return { 837 ErrorMessage => "TicketCreate: Article->CommunicationChannelID or Article->CommunicationChannel parameter" 838 . " is required and Sysconfig CommunicationChannelID setting could not be read!" 839 }; 840 } 841 if ( !$Self->ValidateArticleCommunicationChannel( %{$Article} ) ) { 842 return { 843 ErrorCode => 'TicketCreate.InvalidParameter', 844 ErrorMessage => "TicketCreate: Article->CommunicationChannel or Article->CommunicationChannelID parameter" 845 . " is invalid or not supported!", 846 }; 847 } 848 849 # check Article->SenderType 850 if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) { 851 852 # return internal server error 853 return { 854 ErrorMessage => "TicketCreate: Article->SenderTypeID or Article->SenderType parameter" 855 . " is required and Sysconfig SenderTypeID setting could not be read!" 856 }; 857 } 858 if ( !$Self->ValidateSenderType( %{$Article} ) ) { 859 return { 860 ErrorCode => 'TicketCreate.InvalidParameter', 861 ErrorMessage => "TicketCreate: Article->SenderTypeID or Ticket->SenderType parameter" 862 . " is invalid!", 863 }; 864 } 865 866 # check Article->From 867 if ( $Article->{From} ) { 868 if ( !$Self->ValidateFrom( %{$Article} ) ) { 869 return { 870 ErrorCode => 'TicketCreate.InvalidParameter', 871 ErrorMessage => "TicketCreate: Article->From parameter is invalid!", 872 }; 873 } 874 } 875 876 # check Article->ContentType vs Article->MimeType and Article->Charset 877 if ( !$Article->{ContentType} && !$Article->{MimeType} && !$Article->{Charset} ) { 878 return { 879 ErrorCode => 'TicketCreate.MissingParameter', 880 ErrorMessage => "TicketCreate: Article->ContentType or Ticket->MimeType and" 881 . " Article->Charset parameters are required!", 882 }; 883 } 884 885 if ( $Article->{MimeType} && !$Article->{Charset} ) { 886 return { 887 ErrorCode => 'TicketCreate.MissingParameter', 888 ErrorMessage => "TicketCreate: Article->Charset is required!", 889 }; 890 } 891 892 if ( $Article->{Charset} && !$Article->{MimeType} ) { 893 return { 894 ErrorCode => 'TicketCreate.MissingParameter', 895 ErrorMessage => "TicketCreate: Article->MimeType is required!", 896 }; 897 } 898 899 # check Article->MimeType 900 if ( $Article->{MimeType} ) { 901 902 $Article->{MimeType} = lc $Article->{MimeType}; 903 904 if ( !$Self->ValidateMimeType( %{$Article} ) ) { 905 return { 906 ErrorCode => 'TicketCreate.InvalidParameter', 907 ErrorMessage => "TicketCreate: Article->MimeType is invalid!", 908 }; 909 } 910 } 911 912 # check Article->MimeType 913 if ( $Article->{Charset} ) { 914 915 $Article->{Charset} = lc $Article->{Charset}; 916 917 if ( !$Self->ValidateCharset( %{$Article} ) ) { 918 return { 919 ErrorCode => 'TicketCreate.InvalidParameter', 920 ErrorMessage => "TicketCreate: Article->Charset is invalid!", 921 }; 922 } 923 } 924 925 # check Article->ContentType 926 if ( $Article->{ContentType} ) { 927 928 $Article->{ContentType} = lc $Article->{ContentType}; 929 930 # check Charset part 931 my $Charset = ''; 932 if ( $Article->{ContentType} =~ /charset=/i ) { 933 $Charset = $Article->{ContentType}; 934 $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi; 935 $Charset =~ s/"|'//g; 936 $Charset =~ s/(.+?);.*/$1/g; 937 } 938 939 if ( !$Self->ValidateCharset( Charset => $Charset ) ) { 940 return { 941 ErrorCode => 'TicketCreate.InvalidParameter', 942 ErrorMessage => "TicketCreate: Article->ContentType is invalid!", 943 }; 944 } 945 946 # check MimeType part 947 my $MimeType = ''; 948 if ( $Article->{ContentType} =~ /^(\w+\/\w+)/i ) { 949 $MimeType = $1; 950 $MimeType =~ s/"|'//g; 951 } 952 953 if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) { 954 return { 955 ErrorCode => 'TicketCreate.InvalidParameter', 956 ErrorMessage => "TicketCreate: Article->ContentType is invalid!", 957 }; 958 } 959 } 960 961 # check Article->HistoryType 962 if ( !$Article->{HistoryType} ) { 963 964 # return internal server error 965 return { 966 ErrorMessage => "TicketCreate: Article-> HistoryType is required and Sysconfig" 967 . " HistoryType setting could not be read!" 968 }; 969 } 970 if ( !$Self->ValidateHistoryType( %{$Article} ) ) { 971 return { 972 ErrorCode => 'TicketCreate.InvalidParameter', 973 ErrorMessage => "TicketCreate: Article->HistoryType parameter is invalid!", 974 }; 975 } 976 977 # check Article->HistoryComment 978 if ( !$Article->{HistoryComment} ) { 979 980 # return internal server error 981 return { 982 ErrorMessage => "TicketCreate: Article->HistoryComment is required and Sysconfig" 983 . " HistoryComment setting could not be read!" 984 }; 985 } 986 987 # get config object 988 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 989 990 # check Article->TimeUnit 991 # TimeUnit could be required or not depending on sysconfig option 992 if ( 993 ( !defined $Article->{TimeUnit} || !IsStringWithData( $Article->{TimeUnit} ) ) 994 && $ConfigObject->{'Ticket::Frontend::AccountTime'} 995 && $ConfigObject->{'Ticket::Frontend::NeedAccountedTime'} 996 ) 997 { 998 return { 999 ErrorCode => 'TicketCreate.MissingParameter', 1000 ErrorMessage => "TicketCreate: Article->TimeUnit is required by sysconfig option!", 1001 }; 1002 } 1003 if ( $Article->{TimeUnit} ) { 1004 if ( !$Self->ValidateTimeUnit( %{$Article} ) ) { 1005 return { 1006 ErrorCode => 'TicketCreate.InvalidParameter', 1007 ErrorMessage => "TicketCreate: Article->TimeUnit parameter is invalid!", 1008 }; 1009 } 1010 } 1011 1012 # check Article->NoAgentNotify 1013 if ( $Article->{NoAgentNotify} && $Article->{NoAgentNotify} ne '1' ) { 1014 return { 1015 ErrorCode => 'TicketCreate.InvalidParameter', 1016 ErrorMessage => "TicketCreate: Article->NoAgent parameter is invalid!", 1017 }; 1018 } 1019 1020 # check Article array parameters 1021 for my $Attribute ( 1022 qw( ForceNotificationToUserID ExcludeNotificationToUserID ExcludeMuteNotificationToUserID ) 1023 ) 1024 { 1025 if ( defined $Article->{$Attribute} ) { 1026 1027 # check structure 1028 if ( IsHashRefWithData( $Article->{$Attribute} ) ) { 1029 return { 1030 ErrorCode => 'TicketCreate.InvalidParameter', 1031 ErrorMessage => "TicketCreate: Article->$Attribute parameter is invalid!", 1032 }; 1033 } 1034 else { 1035 if ( !IsArrayRefWithData( $Article->{$Attribute} ) ) { 1036 $Article->{$Attribute} = [ $Article->{$Attribute} ]; 1037 } 1038 for my $UserID ( @{ $Article->{$Attribute} } ) { 1039 if ( !$Self->ValidateUserID( UserID => $UserID ) ) { 1040 return { 1041 ErrorCode => 'TicketCreate.InvalidParameter', 1042 ErrorMessage => "TicketCreate: Article->$Attribute UserID=$UserID" 1043 . " parameter is invalid!", 1044 }; 1045 } 1046 } 1047 } 1048 } 1049 } 1050 1051 # if everything is OK then return Success 1052 return { 1053 Success => 1, 1054 }; 1055} 1056 1057=head2 _CheckDynamicField() 1058 1059checks if the given dynamic field parameter is valid. 1060 1061 my $DynamicFieldCheck = $OperationObject->_CheckDynamicField( 1062 DynamicField => $DynamicField, # all dynamic field parameters 1063 ); 1064 1065 returns: 1066 1067 $DynamicFieldCheck = { 1068 Success => 1, # if everething is OK 1069 } 1070 1071 $DynamicFieldCheck = { 1072 ErrorCode => 'Function.Error', # if error 1073 ErrorMessage => 'Error description', 1074 } 1075 1076=cut 1077 1078sub _CheckDynamicField { 1079 my ( $Self, %Param ) = @_; 1080 1081 my $DynamicField = $Param{DynamicField}; 1082 1083 # check DynamicField item internally 1084 for my $Needed (qw(Name Value)) { 1085 if ( 1086 !defined $DynamicField->{$Needed} 1087 || ( !IsString( $DynamicField->{$Needed} ) && ref $DynamicField->{$Needed} ne 'ARRAY' ) 1088 ) 1089 { 1090 return { 1091 ErrorCode => 'TicketCreate.MissingParameter', 1092 ErrorMessage => "TicketCreate: DynamicField->$Needed parameter is missing!", 1093 }; 1094 } 1095 } 1096 1097 # check DynamicField->Name 1098 if ( !$Self->ValidateDynamicFieldName( %{$DynamicField} ) ) { 1099 return { 1100 ErrorCode => 'TicketCreate.InvalidParameter', 1101 ErrorMessage => "TicketCreate: DynamicField->Name parameter is invalid!", 1102 }; 1103 } 1104 1105 # check DynamicField->Value 1106 if ( !$Self->ValidateDynamicFieldValue( %{$DynamicField} ) ) { 1107 return { 1108 ErrorCode => 'TicketCreate.InvalidParameter', 1109 ErrorMessage => "TicketCreate: DynamicField->Value parameter is invalid!", 1110 }; 1111 } 1112 1113 # if everything is OK then return Success 1114 return { 1115 Success => 1, 1116 }; 1117} 1118 1119=head2 _CheckAttachment() 1120 1121checks if the given attachment parameter is valid. 1122 1123 my $AttachmentCheck = $OperationObject->_CheckAttachment( 1124 Attachment => $Attachment, # all attachment parameters 1125 ); 1126 1127 returns: 1128 1129 $AttachmentCheck = { 1130 Success => 1, # if everething is OK 1131 } 1132 1133 $AttachmentCheck = { 1134 ErrorCode => 'Function.Error', # if error 1135 ErrorMessage => 'Error description', 1136 } 1137 1138=cut 1139 1140sub _CheckAttachment { 1141 my ( $Self, %Param ) = @_; 1142 1143 my $Attachment = $Param{Attachment}; 1144 1145 # check attachment item internally 1146 for my $Needed (qw(Content ContentType Filename)) { 1147 if ( !IsStringWithData( $Attachment->{$Needed} ) ) { 1148 return { 1149 ErrorCode => 'TicketCreate.MissingParameter', 1150 ErrorMessage => "TicketCreate: Attachment->$Needed parameter is missing!", 1151 }; 1152 } 1153 } 1154 1155 # check Article->ContentType 1156 if ( $Attachment->{ContentType} ) { 1157 1158 $Attachment->{ContentType} = lc $Attachment->{ContentType}; 1159 1160 # check Charset part 1161 my $Charset = ''; 1162 if ( $Attachment->{ContentType} =~ /charset=/i ) { 1163 $Charset = $Attachment->{ContentType}; 1164 $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi; 1165 $Charset =~ s/"|'//g; 1166 $Charset =~ s/(.+?);.*/$1/g; 1167 } 1168 1169 if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) ) { 1170 return { 1171 ErrorCode => 'TicketCreate.InvalidParameter', 1172 ErrorMessage => "TicketCreate: Attachment->ContentType is invalid!", 1173 }; 1174 } 1175 1176 # check MimeType part 1177 my $MimeType = ''; 1178 if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) { 1179 $MimeType = $1; 1180 $MimeType =~ s/"|'//g; 1181 } 1182 1183 if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) { 1184 return { 1185 ErrorCode => 'TicketCreate.InvalidParameter', 1186 ErrorMessage => "TicketCreate: Attachment->ContentType is invalid!", 1187 }; 1188 } 1189 } 1190 1191 # if everything is OK then return Success 1192 return { 1193 Success => 1, 1194 }; 1195} 1196 1197=head2 _TicketCreate() 1198 1199creates a ticket with its article and sets dynamic fields and attachments if specified. 1200 1201 my $Response = $OperationObject->_TicketCreate( 1202 Ticket => $Ticket, # all ticket parameters 1203 Article => $Article, # all attachment parameters 1204 DynamicField => $DynamicField, # all dynamic field parameters 1205 Attachment => $Attachment, # all attachment parameters 1206 UserID => 123, 1207 ); 1208 1209 returns: 1210 1211 $Response = { 1212 Success => 1, # if everything was OK 1213 Data => { 1214 TicketID => 123, 1215 TicketNumber => 'TN3422332', 1216 ArticleID => 123, 1217 } 1218 } 1219 1220 $Response = { 1221 Success => 0, # if unexpected error 1222 ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}", 1223 } 1224 1225=cut 1226 1227sub _TicketCreate { 1228 my ( $Self, %Param ) = @_; 1229 1230 my $Ticket = $Param{Ticket}; 1231 my $Article = $Param{Article}; 1232 my $DynamicFieldList = $Param{DynamicFieldList}; 1233 my $AttachmentList = $Param{AttachmentList}; 1234 my $CustomerUser = $Ticket->{CustomerUser} || ''; 1235 1236 # Get customer information, that will be used to create the ticket. 1237 # If TicketCreate CustomerUser parameter is defined, 1238 # check if there is CustomerUser in DB with such address, 1239 # If address, defined in $Ticket->{CustomerUser}, 1240 # is not valid, ValidateCustomer() will not allowed to run TicketCreate. 1241 # See more information in bug#14288. 1242 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 1243 my %CustomerUserData = $CustomerUserObject->CustomerUserDataGet( 1244 User => $Ticket->{CustomerUser}, 1245 ); 1246 1247 if ( !IsHashRefWithData( \%CustomerUserData ) ) { 1248 my %CustomerSearch = $CustomerUserObject->CustomerSearch( 1249 PostMasterSearch => $CustomerUser, 1250 Limit => 1, 1251 ); 1252 1253 if ( IsHashRefWithData( \%CustomerSearch ) ) { 1254 my @CustomerSearchResults = sort keys %CustomerSearch; 1255 $CustomerUser = $CustomerSearchResults[0]; 1256 1257 %CustomerUserData = $CustomerUserObject->CustomerUserDataGet( 1258 User => $CustomerUser, 1259 ); 1260 } 1261 1262 } 1263 else { 1264 $CustomerUser = $CustomerUserData{UserLogin}; 1265 } 1266 1267 my $CustomerID = $CustomerUserData{UserCustomerID} || ''; 1268 1269 # use user defined CustomerID if defined 1270 if ( defined $Ticket->{CustomerID} && $Ticket->{CustomerID} ne '' ) { 1271 $CustomerID = $Ticket->{CustomerID}; 1272 } 1273 1274 # get database object 1275 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 1276 1277 my $OwnerID; 1278 if ( $Ticket->{Owner} && !$Ticket->{OwnerID} ) { 1279 my %OwnerData = $UserObject->GetUserData( 1280 User => $Ticket->{Owner}, 1281 ); 1282 $OwnerID = $OwnerData{UserID}; 1283 } 1284 elsif ( defined $Ticket->{OwnerID} ) { 1285 $OwnerID = $Ticket->{OwnerID}; 1286 } 1287 1288 my $ResponsibleID; 1289 if ( $Ticket->{Responsible} && !$Ticket->{ResponsibleID} ) { 1290 my %ResponsibleData = $UserObject->GetUserData( 1291 User => $Ticket->{Responsible}, 1292 ); 1293 $ResponsibleID = $ResponsibleData{UserID}; 1294 } 1295 elsif ( defined $Ticket->{ResponsibleID} ) { 1296 $ResponsibleID = $Ticket->{ResponsibleID}; 1297 } 1298 1299 # get ticket object 1300 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 1301 1302 # create new ticket 1303 my $TicketID = $TicketObject->TicketCreate( 1304 Title => $Ticket->{Title}, 1305 QueueID => $Ticket->{QueueID} || '', 1306 Queue => $Ticket->{Queue} || '', 1307 Lock => 'unlock', 1308 TypeID => $Ticket->{TypeID} || '', 1309 Type => $Ticket->{Type} || '', 1310 ServiceID => $Ticket->{ServiceID} || '', 1311 Service => $Ticket->{Service} || '', 1312 SLAID => $Ticket->{SLAID} || '', 1313 SLA => $Ticket->{SLA} || '', 1314 StateID => $Ticket->{StateID} || '', 1315 State => $Ticket->{State} || '', 1316 PriorityID => $Ticket->{PriorityID} || '', 1317 Priority => $Ticket->{Priority} || '', 1318 OwnerID => 1, 1319 CustomerNo => $CustomerID, 1320 CustomerUser => $CustomerUser || '', 1321 UserID => $Param{UserID}, 1322 ); 1323 1324 if ( !$TicketID ) { 1325 return { 1326 Success => 0, 1327 ErrorMessage => 'Ticket could not be created, please contact the system administrator', 1328 }; 1329 } 1330 1331 # set lock if specified 1332 if ( $Ticket->{Lock} || $Ticket->{LockID} ) { 1333 $TicketObject->TicketLockSet( 1334 TicketID => $TicketID, 1335 LockID => $Ticket->{LockID} || '', 1336 Lock => $Ticket->{Lock} || '', 1337 UserID => $Param{UserID}, 1338 ); 1339 } 1340 1341 # get State Data 1342 my %StateData; 1343 my $StateID; 1344 1345 # get state object 1346 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 1347 1348 if ( $Ticket->{StateID} ) { 1349 $StateID = $Ticket->{StateID}; 1350 } 1351 else { 1352 $StateID = $StateObject->StateLookup( 1353 State => $Ticket->{State}, 1354 ); 1355 } 1356 1357 %StateData = $StateObject->StateGet( 1358 ID => $StateID, 1359 ); 1360 1361 # force unlock if state type is close 1362 if ( $StateData{TypeName} =~ /^close/i ) { 1363 1364 # set lock 1365 $TicketObject->TicketLockSet( 1366 TicketID => $TicketID, 1367 Lock => 'unlock', 1368 UserID => $Param{UserID}, 1369 ); 1370 } 1371 1372 # set pending time 1373 elsif ( $StateData{TypeName} =~ /^pending/i ) { 1374 1375 # set pending time 1376 if ( defined $Ticket->{PendingTime} ) { 1377 $TicketObject->TicketPendingTimeSet( 1378 UserID => $Param{UserID}, 1379 TicketID => $TicketID, 1380 %{ $Ticket->{PendingTime} }, 1381 ); 1382 } 1383 } 1384 1385 # set dynamic fields (only for object type 'ticket') 1386 if ( IsArrayRefWithData($DynamicFieldList) ) { 1387 1388 DYNAMICFIELD: 1389 for my $DynamicField ( @{$DynamicFieldList} ) { 1390 next DYNAMICFIELD if !$Self->ValidateDynamicFieldObjectType( %{$DynamicField} ); 1391 1392 my $Result = $Self->SetDynamicFieldValue( 1393 %{$DynamicField}, 1394 TicketID => $TicketID, 1395 UserID => $Param{UserID}, 1396 ); 1397 1398 if ( !$Result->{Success} ) { 1399 my $ErrorMessage = 1400 $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be" 1401 . " set, please contact the system administrator"; 1402 1403 return { 1404 Success => 0, 1405 ErrorMessage => $ErrorMessage, 1406 }; 1407 } 1408 } 1409 } 1410 1411 if ( !defined $Article->{NoAgentNotify} ) { 1412 1413 # check if new owner is given (then send no agent notify) 1414 $Article->{NoAgentNotify} = 0; 1415 if ($OwnerID) { 1416 $Article->{NoAgentNotify} = 1; 1417 } 1418 } 1419 1420 # set Article From 1421 my $From; 1422 if ( $Article->{From} ) { 1423 $From = $Article->{From}; 1424 } 1425 1426 # use data from customer user (if customer user is in database) 1427 elsif ( IsHashRefWithData( \%CustomerUserData ) ) { 1428 $From = '"' . $CustomerUserData{UserFullname} . '"' 1429 . ' <' . $CustomerUserData{UserEmail} . '>'; 1430 } 1431 1432 # otherwise use customer user as sent from the request (it should be an email) 1433 else { 1434 $From = $CustomerUser; 1435 } 1436 1437 # set Article To 1438 my $To; 1439 if ( $Ticket->{Queue} ) { 1440 $To = $Ticket->{Queue}; 1441 } 1442 else { 1443 $To = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup( 1444 QueueID => $Ticket->{QueueID}, 1445 ); 1446 } 1447 1448 if ( !$Article->{CommunicationChannel} ) { 1449 1450 my %CommunicationChannel = $Kernel::OM->Get('Kernel::System::CommunicationChannel')->ChannelGet( 1451 ChannelID => $Article->{CommunicationChannelID}, 1452 ); 1453 $Article->{CommunicationChannel} = $CommunicationChannel{ChannelName}; 1454 } 1455 1456 my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel( 1457 ChannelName => $Article->{CommunicationChannel}, 1458 ); 1459 1460 my $PlainBody = $Article->{Body}; 1461 1462 # Convert article body to plain text, if HTML content was supplied. This is necessary since auto response code 1463 # expects plain text content. Please see bug#13397 for more information. 1464 if ( $Article->{ContentType} =~ /text\/html/i || $Article->{MimeType} =~ /text\/html/i ) { 1465 $PlainBody = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii( 1466 String => $Article->{Body}, 1467 ); 1468 } 1469 1470 # Create article. 1471 my $ArticleID = $ArticleBackendObject->ArticleCreate( 1472 NoAgentNotify => $Article->{NoAgentNotify} || 0, 1473 TicketID => $TicketID, 1474 SenderTypeID => $Article->{SenderTypeID} || '', 1475 SenderType => $Article->{SenderType} || '', 1476 IsVisibleForCustomer => $Article->{IsVisibleForCustomer}, 1477 From => $From, 1478 To => $To, 1479 Subject => $Article->{Subject}, 1480 Body => $Article->{Body}, 1481 MimeType => $Article->{MimeType} || '', 1482 Charset => $Article->{Charset} || '', 1483 ContentType => $Article->{ContentType} || '', 1484 UserID => $Param{UserID}, 1485 HistoryType => $Article->{HistoryType}, 1486 HistoryComment => $Article->{HistoryComment} || '%%', 1487 AutoResponseType => $Article->{AutoResponseType}, 1488 OrigHeader => { 1489 From => $From, 1490 To => $To, 1491 Subject => $Article->{Subject}, 1492 Body => $PlainBody, 1493 }, 1494 ); 1495 1496 if ( !$ArticleID ) { 1497 return { 1498 Success => 0, 1499 ErrorMessage => 'Article could not be created, please contact the system administrator', 1500 }; 1501 } 1502 1503 # set owner (if owner or owner id is given) 1504 if ($OwnerID) { 1505 $TicketObject->TicketOwnerSet( 1506 TicketID => $TicketID, 1507 NewUserID => $OwnerID, 1508 UserID => $Param{UserID}, 1509 ); 1510 1511 # set lock if no lock was defined 1512 if ( !$Ticket->{Lock} && !$Ticket->{LockID} ) { 1513 $TicketObject->TicketLockSet( 1514 TicketID => $TicketID, 1515 Lock => 'lock', 1516 UserID => $Param{UserID}, 1517 ); 1518 } 1519 } 1520 1521 # else set owner to current agent but do not lock it 1522 else { 1523 $TicketObject->TicketOwnerSet( 1524 TicketID => $TicketID, 1525 NewUserID => $Param{UserID}, 1526 SendNoNotification => 1, 1527 UserID => $Param{UserID}, 1528 ); 1529 } 1530 1531 # set responsible 1532 if ($ResponsibleID) { 1533 $TicketObject->TicketResponsibleSet( 1534 TicketID => $TicketID, 1535 NewUserID => $ResponsibleID, 1536 UserID => $Param{UserID}, 1537 ); 1538 } 1539 1540 # time accounting 1541 if ( $Article->{TimeUnit} ) { 1542 $TicketObject->TicketAccountTime( 1543 TicketID => $TicketID, 1544 ArticleID => $ArticleID, 1545 TimeUnit => $Article->{TimeUnit}, 1546 UserID => $Param{UserID}, 1547 ); 1548 } 1549 1550 # set dynamic fields (only for object type 'article') 1551 if ( IsArrayRefWithData($DynamicFieldList) ) { 1552 1553 DYNAMICFIELD: 1554 for my $DynamicField ( @{$DynamicFieldList} ) { 1555 1556 my $IsArticleDynamicField = $Self->ValidateDynamicFieldObjectType( 1557 %{$DynamicField}, 1558 Article => 1, 1559 ); 1560 next DYNAMICFIELD if !$IsArticleDynamicField; 1561 1562 my $Result = $Self->SetDynamicFieldValue( 1563 %{$DynamicField}, 1564 TicketID => $TicketID, 1565 ArticleID => $ArticleID, 1566 UserID => $Param{UserID}, 1567 ); 1568 1569 if ( !$Result->{Success} ) { 1570 my $ErrorMessage = 1571 $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be" 1572 . " set, please contact the system administrator"; 1573 1574 return { 1575 Success => 0, 1576 ErrorMessage => $ErrorMessage, 1577 }; 1578 } 1579 } 1580 } 1581 1582 # set attachments 1583 if ( IsArrayRefWithData($AttachmentList) ) { 1584 1585 for my $Attachment ( @{$AttachmentList} ) { 1586 my $Result = $Self->CreateAttachment( 1587 TicketID => $TicketID, 1588 Attachment => $Attachment, 1589 ArticleID => $ArticleID, 1590 UserID => $Param{UserID} 1591 ); 1592 1593 if ( !$Result->{Success} ) { 1594 my $ErrorMessage = 1595 $Result->{ErrorMessage} || "Attachment could not be created, please contact" 1596 . " the system administrator"; 1597 1598 return { 1599 Success => 0, 1600 ErrorMessage => $ErrorMessage, 1601 }; 1602 } 1603 } 1604 } 1605 1606 # get ticket data 1607 my %TicketData = $TicketObject->TicketGet( 1608 TicketID => $TicketID, 1609 DynamicFields => 1, 1610 UserID => $Param{UserID}, 1611 ); 1612 1613 if ( !IsHashRefWithData( \%TicketData ) ) { 1614 return { 1615 Success => 0, 1616 ErrorMessage => 'Could not get new ticket information, please contact the system' 1617 . ' administrator', 1618 }; 1619 } 1620 1621 # get web service configuration 1622 my $Webservice = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet( 1623 ID => $Self->{WebserviceID}, 1624 ); 1625 1626 my $IncludeTicketData; 1627 1628 # Get operation config, if operation name was supplied. 1629 if ( $Self->{Operation} ) { 1630 my $OperationConfig = $Webservice->{Config}->{Provider}->{Operation}->{ $Self->{Operation} }; 1631 $IncludeTicketData = $OperationConfig->{IncludeTicketData}; 1632 } 1633 1634 if ( !$IncludeTicketData ) { 1635 return { 1636 Success => 1, 1637 Data => { 1638 TicketID => $TicketID, 1639 TicketNumber => $TicketData{TicketNumber}, 1640 ArticleID => $ArticleID, 1641 }, 1642 }; 1643 } 1644 1645 # extract all dynamic fields from main ticket hash. 1646 my %TicketDynamicFields; 1647 TICKETATTRIBUTE: 1648 for my $TicketAttribute ( sort keys %TicketData ) { 1649 if ( $TicketAttribute =~ m{\A DynamicField_(.*) \z}msx ) { 1650 $TicketDynamicFields{$1} = { 1651 Name => $1, 1652 Value => $TicketData{$TicketAttribute}, 1653 }; 1654 delete $TicketData{$TicketAttribute}; 1655 } 1656 } 1657 1658 # add dynamic fields as array into 'DynamicField' hash key if any 1659 if (%TicketDynamicFields) { 1660 $TicketData{DynamicField} = [ sort { $a->{Name} cmp $b->{Name} } values %TicketDynamicFields ]; 1661 } 1662 1663 # prepare TicketData and get Article 1664 my %ArticleData = $ArticleBackendObject->ArticleGet( 1665 TicketID => $TicketID, 1666 ArticleID => $ArticleID, 1667 DynamicFields => 1, 1668 ); 1669 1670 # prepare Article DynamicFields 1671 my @ArticleDynamicFields; 1672 1673 # remove all dynamic fields form main ticket hash and set them into an array. 1674 ARTICLEATTRIBUTE: 1675 for my $ArticleAttribute ( sort keys %ArticleData ) { 1676 if ( $ArticleAttribute =~ m{\A DynamicField_(.*) \z}msx ) { 1677 if ( !exists $TicketDynamicFields{$1} ) { 1678 push @ArticleDynamicFields, { 1679 Name => $1, 1680 Value => $ArticleData{$ArticleAttribute}, 1681 }; 1682 } 1683 1684 delete $ArticleData{$ArticleAttribute}; 1685 } 1686 } 1687 1688 # add dynamic fields array into 'DynamicField' hash key if any 1689 if (@ArticleDynamicFields) { 1690 $ArticleData{DynamicField} = \@ArticleDynamicFields; 1691 } 1692 1693 # add attachment if the request includes attachments 1694 if ( IsArrayRefWithData($AttachmentList) ) { 1695 my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex( 1696 ArticleID => $ArticleID, 1697 ); 1698 1699 my @Attachments; 1700 $Kernel::OM->Get('Kernel::System::Main')->Require('MIME::Base64'); 1701 ATTACHMENT: 1702 for my $FileID ( sort keys %AttachmentIndex ) { 1703 next ATTACHMENT if !$FileID; 1704 my %Attachment = $ArticleBackendObject->ArticleAttachment( 1705 ArticleID => $ArticleID, 1706 FileID => $FileID, 1707 ); 1708 1709 next ATTACHMENT if !IsHashRefWithData( \%Attachment ); 1710 1711 # convert content to base64, but prevent 76 chars brake, see bug#14500. 1712 $Attachment{Content} = MIME::Base64::encode_base64( $Attachment{Content}, '' ); 1713 push @Attachments, {%Attachment}; 1714 } 1715 1716 # set Attachments data 1717 if (@Attachments) { 1718 $ArticleData{Attachment} = \@Attachments; 1719 } 1720 } 1721 1722 $TicketData{Article} = \%ArticleData; 1723 1724 return { 1725 Success => 1, 1726 Data => { 1727 TicketID => $TicketID, 1728 TicketNumber => $TicketData{TicketNumber}, 1729 ArticleID => $ArticleID, 1730 Ticket => \%TicketData, 1731 }, 1732 }; 1733} 1734 17351; 1736 1737=end Internal: 1738 1739=head1 TERMS AND CONDITIONS 1740 1741This software is part of the OTRS project (L<https://otrs.org/>). 1742 1743This software comes with ABSOLUTELY NO WARRANTY. For details, see 1744the enclosed file COPYING for license information (GPL). If you 1745did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>. 1746 1747=cut 1748