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::Modules::AgentTicketEmail; 10 11use strict; 12use warnings; 13 14use Mail::Address; 15 16use Kernel::System::VariableCheck qw(:all); 17use Kernel::Language qw(Translatable); 18 19our $ObjectManagerDisabled = 1; 20 21sub new { 22 my ( $Type, %Param ) = @_; 23 24 # allocate new hash for object 25 my $Self = {%Param}; 26 bless( $Self, $Type ); 27 28 my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}"); 29 30 # get the dynamic fields for this screen 31 $Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 32 Valid => 1, 33 ObjectType => [ 'Ticket', 'Article' ], 34 FieldFilter => $Config->{DynamicField} || {}, 35 ); 36 37 # get form id 38 $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'FormID' ); 39 40 # create form id 41 if ( !$Self->{FormID} ) { 42 $Self->{FormID} = $Kernel::OM->Get('Kernel::System::Web::UploadCache')->FormIDCreate(); 43 } 44 45 return $Self; 46} 47 48sub Run { 49 my ( $Self, %Param ) = @_; 50 51 my $Output; 52 53 # store last queue screen 54 if ( $Self->{LastScreenOverview} && $Self->{LastScreenOverview} !~ /Action=AgentTicketEmail/ ) { 55 $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( 56 SessionID => $Self->{SessionID}, 57 Key => 'LastScreenOverview', 58 Value => $Self->{RequestedURL}, 59 ); 60 } 61 62 # get param object 63 my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 64 65 # get upload cache object 66 my $UploadCacheObject = $Kernel::OM->Get('Kernel::System::Web::UploadCache'); 67 68 # get needed objects 69 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 70 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 71 my $MainObject = $Kernel::OM->Get('Kernel::System::Main'); 72 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 73 74 my $Debug = $Param{Debug} || 0; 75 my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}"); 76 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 77 78 # get params 79 my %GetParam; 80 for my $Key ( 81 qw(Year Month Day Hour Minute To Cc Bcc TimeUnits PriorityID Subject Body 82 TypeID ServiceID SLAID OwnerAll ResponsibleAll NewResponsibleID NewUserID 83 NextStateID StandardTemplateID Dest ArticleID LinkTicketID 84 ) 85 ) 86 { 87 $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); 88 } 89 90 # ACL compatibility translation 91 my %ACLCompatGetParam; 92 $ACLCompatGetParam{OwnerID} = $GetParam{NewUserID}; 93 94 # hash for check duplicated entries 95 my %AddressesList; 96 97 # MultipleCustomer To-field 98 my @MultipleCustomer; 99 my $CustomersNumber = $ParamObject->GetParam( Param => 'CustomerTicketCounterToCustomer' ) || 0; 100 my $Selected = $ParamObject->GetParam( Param => 'CustomerSelected' ) || ''; 101 102 # get check item object 103 my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem'); 104 105 if ($CustomersNumber) { 106 my $CustomerCounter = 1; 107 for my $Count ( 1 ... $CustomersNumber ) { 108 my $CustomerElement = $ParamObject->GetParam( Param => 'CustomerTicketText_' . $Count ); 109 my $CustomerSelected = ( $Selected eq $Count ? 'checked="checked"' : '' ); 110 my $CustomerKey = $ParamObject->GetParam( Param => 'CustomerKey_' . $Count ) 111 || ''; 112 if ($CustomerElement) { 113 114 if ( $GetParam{To} ) { 115 $GetParam{To} .= ', ' . $CustomerElement; 116 } 117 else { 118 $GetParam{To} = $CustomerElement; 119 } 120 121 my $CustomerErrorMsg = 'CustomerGenericServerErrorMsg'; 122 my $CustomerError = ''; 123 my $CustomerDisabled = ''; 124 my $CountAux = $CustomerCounter++; 125 126 # check email address 127 for my $Email ( Mail::Address->parse($CustomerElement) ) { 128 if ( !$CheckItemObject->CheckEmail( Address => $Email->address() ) ) 129 { 130 $CustomerErrorMsg = $CheckItemObject->CheckErrorType() 131 . 'ServerErrorMsg'; 132 $CustomerError = 'ServerError'; 133 } 134 } 135 136 # check for duplicated entries 137 if ( defined $AddressesList{$CustomerElement} && $CustomerError eq '' ) { 138 $CustomerErrorMsg = 'IsDuplicatedServerErrorMsg'; 139 $CustomerError = 'ServerError'; 140 } 141 142 if ( $CustomerError ne '' ) { 143 $CustomerDisabled = 'disabled="disabled"'; 144 $CountAux = $Count . 'Error'; 145 } 146 147 push @MultipleCustomer, { 148 Count => $CountAux, 149 CustomerElement => $CustomerElement, 150 CustomerSelected => $CustomerSelected, 151 CustomerKey => $CustomerKey, 152 CustomerError => $CustomerError, 153 CustomerErrorMsg => $CustomerErrorMsg, 154 CustomerDisabled => $CustomerDisabled, 155 }; 156 $AddressesList{$CustomerElement} = 1; 157 } 158 } 159 } 160 161 # MultipleCustomer Cc-field 162 my @MultipleCustomerCc; 163 my $CustomersNumberCc = $ParamObject->GetParam( Param => 'CustomerTicketCounterCcCustomer' ) || 0; 164 165 if ($CustomersNumberCc) { 166 my $CustomerCounterCc = 1; 167 for my $Count ( 1 ... $CustomersNumberCc ) { 168 my $CustomerElementCc = $ParamObject->GetParam( Param => 'CcCustomerTicketText_' . $Count ); 169 my $CustomerKeyCc = $ParamObject->GetParam( Param => 'CcCustomerKey_' . $Count ) 170 || ''; 171 172 if ($CustomerElementCc) { 173 my $CustomerErrorMsgCc = 'CustomerGenericServerErrorMsg'; 174 my $CustomerErrorCc = ''; 175 my $CustomerDisabledCc = ''; 176 my $CountAuxCc = $CustomerCounterCc++; 177 178 if ( $GetParam{Cc} ) { 179 $GetParam{Cc} .= ', ' . $CustomerElementCc; 180 } 181 else { 182 $GetParam{Cc} = $CustomerElementCc; 183 } 184 185 # check email address 186 for my $Email ( Mail::Address->parse($CustomerElementCc) ) { 187 if ( !$CheckItemObject->CheckEmail( Address => $Email->address() ) ) 188 { 189 $CustomerErrorMsgCc = $CheckItemObject->CheckErrorType() 190 . 'ServerErrorMsg'; 191 $CustomerErrorCc = 'ServerError'; 192 } 193 } 194 195 # check for duplicated entries 196 if ( defined $AddressesList{$CustomerElementCc} && $CustomerErrorCc eq '' ) { 197 $CustomerErrorMsgCc = 'IsDuplicatedServerErrorMsg'; 198 $CustomerErrorCc = 'ServerError'; 199 } 200 201 if ( $CustomerErrorCc ne '' ) { 202 $CustomerDisabledCc = 'disabled="disabled"'; 203 $CountAuxCc = $Count . 'Error'; 204 } 205 206 push @MultipleCustomerCc, { 207 Count => $CountAuxCc, 208 CustomerElement => $CustomerElementCc, 209 CustomerKey => $CustomerKeyCc, 210 CustomerError => $CustomerErrorCc, 211 CustomerErrorMsg => $CustomerErrorMsgCc, 212 CustomerDisabled => $CustomerDisabledCc, 213 }; 214 $AddressesList{$CustomerElementCc} = 1; 215 } 216 } 217 } 218 219 # MultipleCustomer Bcc-field 220 my @MultipleCustomerBcc; 221 my $CustomersNumberBcc = $ParamObject->GetParam( Param => 'CustomerTicketCounterBccCustomer' ) || 0; 222 223 if ($CustomersNumberBcc) { 224 my $CustomerCounterBcc = 1; 225 for my $Count ( 1 ... $CustomersNumberBcc ) { 226 my $CustomerElementBcc = $ParamObject->GetParam( Param => 'BccCustomerTicketText_' . $Count ); 227 my $CustomerKeyBcc = $ParamObject->GetParam( Param => 'BccCustomerKey_' . $Count ) 228 || ''; 229 230 if ($CustomerElementBcc) { 231 232 my $CustomerDisabledBcc = ''; 233 my $CountAuxBcc = $CustomerCounterBcc++; 234 my $CustomerErrorMsgBcc = 'CustomerGenericServerErrorMsg'; 235 my $CustomerErrorBcc = ''; 236 237 if ( $GetParam{Bcc} ) { 238 $GetParam{Bcc} .= ', ' . $CustomerElementBcc; 239 } 240 else { 241 $GetParam{Bcc} = $CustomerElementBcc; 242 } 243 244 # check email address 245 for my $Email ( Mail::Address->parse($CustomerElementBcc) ) { 246 if ( !$CheckItemObject->CheckEmail( Address => $Email->address() ) ) 247 { 248 $CustomerErrorMsgBcc = $CheckItemObject->CheckErrorType() 249 . 'ServerErrorMsg'; 250 $CustomerErrorBcc = 'ServerError'; 251 } 252 } 253 254 # check for duplicated entries 255 if ( defined $AddressesList{$CustomerElementBcc} && $CustomerErrorBcc eq '' ) { 256 $CustomerErrorMsgBcc = 'IsDuplicatedServerErrorMsg'; 257 $CustomerErrorBcc = 'ServerError'; 258 } 259 260 if ( $CustomerErrorBcc ne '' ) { 261 $CustomerDisabledBcc = 'disabled="disabled"'; 262 $CountAuxBcc = $Count . 'Error'; 263 } 264 265 push @MultipleCustomerBcc, { 266 Count => $CountAuxBcc, 267 CustomerElement => $CustomerElementBcc, 268 CustomerKey => $CustomerKeyBcc, 269 CustomerError => $CustomerErrorBcc, 270 CustomerErrorMsg => $CustomerErrorMsgBcc, 271 CustomerDisabled => $CustomerDisabledBcc, 272 }; 273 $AddressesList{$CustomerElementBcc} = 1; 274 } 275 } 276 } 277 278 # set an empty value if not defined 279 $GetParam{Cc} = '' if !defined $GetParam{Cc}; 280 $GetParam{Bcc} = '' if !defined $GetParam{Bcc}; 281 282 # get Dynamic fields form ParamObject 283 my %DynamicFieldValues; 284 285 # get needed objects 286 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 287 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 288 289 # cycle through the activated Dynamic Fields for this screen 290 DYNAMICFIELD: 291 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 292 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 293 294 # extract the dynamic field value from the web request 295 $DynamicFieldValues{ $DynamicFieldConfig->{Name} } = $DynamicFieldBackendObject->EditFieldValueGet( 296 DynamicFieldConfig => $DynamicFieldConfig, 297 ParamObject => $ParamObject, 298 LayoutObject => $LayoutObject, 299 ); 300 } 301 302 # convert dynamic field values into a structure for ACLs 303 my %DynamicFieldACLParameters; 304 DYNAMICFIELD: 305 for my $DynamicField ( sort keys %DynamicFieldValues ) { 306 next DYNAMICFIELD if !$DynamicField; 307 next DYNAMICFIELD if !$DynamicFieldValues{$DynamicField}; 308 309 $DynamicFieldACLParameters{ 'DynamicField_' . $DynamicField } = $DynamicFieldValues{$DynamicField}; 310 } 311 $GetParam{DynamicField} = \%DynamicFieldACLParameters; 312 313 # transform pending time, time stamp based on user time zone 314 if ( 315 defined $GetParam{Year} 316 && defined $GetParam{Month} 317 && defined $GetParam{Day} 318 && defined $GetParam{Hour} 319 && defined $GetParam{Minute} 320 ) 321 { 322 %GetParam = $LayoutObject->TransformDateSelection( 323 %GetParam, 324 ); 325 } 326 327 if ( !$Self->{Subaction} || $Self->{Subaction} eq 'Created' ) { 328 my %Ticket; 329 if ( $Self->{TicketID} ) { 330 %Ticket = $TicketObject->TicketGet( TicketID => $Self->{TicketID} ); 331 } 332 333 # header 334 $Output .= $LayoutObject->Header(); 335 $Output .= $LayoutObject->NavigationBar(); 336 337 # notify info 338 if ( $Self->{TicketID} && $Self->{Subaction} eq 'Created' ) { 339 340 $Output .= $LayoutObject->Notify( 341 Info => $LayoutObject->{LanguageObject}->Translate( 342 'Ticket "%s" created!', 343 $Ticket{TicketNumber}, 344 ), 345 Link => $LayoutObject->{Baselink} 346 . 'Action=AgentTicketZoom;TicketID=' 347 . $Ticket{TicketID}, 348 ); 349 } 350 351 # store last queue screen 352 if ( 353 $Self->{LastScreenOverview} 354 && $Self->{LastScreenOverview} !~ /Action=AgentTicketEmail/ 355 && $Self->{RequestedURL} !~ /Action=AgentTicketEmail.*LinkTicketID=/ 356 ) 357 { 358 $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( 359 SessionID => $Self->{SessionID}, 360 Key => 'LastScreenOverview', 361 Value => $Self->{RequestedURL}, 362 ); 363 } 364 365 # get split article if given 366 # get ArticleID 367 my %Article; 368 my %CustomerData; 369 my $ArticleFrom = ''; 370 my %SplitTicketData; 371 if ( $GetParam{ArticleID} ) { 372 373 my $Access = $TicketObject->TicketPermission( 374 Type => 'ro', 375 TicketID => $Self->{TicketID}, 376 UserID => $Self->{UserID} 377 ); 378 379 if ( !$Access ) { 380 return $LayoutObject->NoPermission( 381 Message => "You need ro permission!", 382 WithHeader => 'yes', 383 ); 384 } 385 386 # Get information from original ticket (SplitTicket). 387 %SplitTicketData = $TicketObject->TicketGet( 388 TicketID => $Self->{TicketID}, 389 DynamicFields => 1, 390 UserID => $Self->{UserID}, 391 ); 392 393 my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForArticle( 394 TicketID => $Self->{TicketID}, 395 ArticleID => $GetParam{ArticleID}, 396 ); 397 398 %Article = $ArticleBackendObject->ArticleGet( 399 TicketID => $Self->{TicketID}, 400 ArticleID => $GetParam{ArticleID}, 401 ); 402 403 # check if article is from the same TicketID as we checked permissions for. 404 if ( $Article{TicketID} ne $Self->{TicketID} ) { 405 return $LayoutObject->ErrorScreen( 406 Message => $LayoutObject->{LanguageObject}->Translate( 407 'Article does not belong to ticket %s!', $Self->{TicketID} 408 ), 409 ); 410 } 411 412 $Article{Subject} = $TicketObject->TicketSubjectClean( 413 TicketNumber => $Ticket{TicketNumber}, 414 Subject => $Article{Subject} || '', 415 ); 416 417 # save article from for addresses list 418 $ArticleFrom = $Article{From}; 419 420 # if To is present 421 # and is no a queue 422 # and also is no a system address 423 # set To as article from 424 if ( IsStringWithData( $Article{To} ) ) { 425 my %Queues = $QueueObject->QueueList(); 426 427 if ( $ConfigObject->{CustomerPanelOwnSelection} ) { 428 for my $Queue ( sort keys %{ $ConfigObject->{CustomerPanelOwnSelection} } ) { 429 my $Value = $ConfigObject->{CustomerPanelOwnSelection}->{$Queue}; 430 $Queues{$Queue} = $Value; 431 } 432 } 433 434 my %QueueLookup = reverse %Queues; 435 my %SystemAddressLookup 436 = reverse $Kernel::OM->Get('Kernel::System::SystemAddress')->SystemAddressList(); 437 my @ArticleFromAddress; 438 my $SystemAddressEmail; 439 440 if ($ArticleFrom) { 441 @ArticleFromAddress = Mail::Address->parse($ArticleFrom); 442 $SystemAddressEmail = $ArticleFromAddress[0]->address(); 443 } 444 445 if ( !defined $QueueLookup{ $Article{To} } && defined $SystemAddressLookup{$SystemAddressEmail} ) { 446 $ArticleFrom = $Article{To}; 447 } 448 } 449 450 # body preparation for plain text processing 451 $Article{Body} = $LayoutObject->ArticleQuote( 452 TicketID => $Article{TicketID}, 453 ArticleID => $GetParam{ArticleID}, 454 FormID => $Self->{FormID}, 455 UploadCacheObject => $UploadCacheObject, 456 AttachmentsInclude => 1, 457 ); 458 if ( $LayoutObject->{BrowserRichText} ) { 459 $Article{ContentType} = 'text/html'; 460 } 461 else { 462 $Article{ContentType} = 'text/plain'; 463 } 464 465 my %SafetyCheckResult = $Kernel::OM->Get('Kernel::System::HTMLUtils')->Safety( 466 String => $Article{Body}, 467 468 # Strip out external content if BlockLoadingRemoteContent is enabled. 469 NoExtSrcLoad => $ConfigObject->Get('Ticket::Frontend::BlockLoadingRemoteContent'), 470 471 # Disallow potentially unsafe content. 472 NoApplet => 1, 473 NoObject => 1, 474 NoEmbed => 1, 475 NoSVG => 1, 476 NoJavaScript => 1, 477 ); 478 $Article{Body} = $SafetyCheckResult{String}; 479 480 # show customer info 481 if ( $ConfigObject->Get('Ticket::Frontend::CustomerInfoCompose') ) { 482 if ( $Article{CustomerUserID} ) { 483 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 484 User => $Article{CustomerUserID}, 485 ); 486 } 487 elsif ( $Article{CustomerID} ) { 488 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 489 CustomerID => $Article{CustomerID}, 490 ); 491 } 492 elsif ( $SplitTicketData{CustomerUserID} ) { 493 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 494 User => $SplitTicketData{CustomerUserID}, 495 ); 496 } 497 elsif ( $SplitTicketData{CustomerID} ) { 498 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 499 CustomerID => $SplitTicketData{CustomerID}, 500 ); 501 } 502 } 503 if ( $Article{CustomerUserID} ) { 504 my %CustomerUserList = $CustomerUserObject->CustomerSearch( 505 UserLogin => $Article{CustomerUserID}, 506 ); 507 for my $KeyCustomerUserList ( sort keys %CustomerUserList ) { 508 $Article{From} = $CustomerUserList{$KeyCustomerUserList}; 509 } 510 } 511 } 512 513 # multiple addresses list 514 # check email address 515 my $CountFrom = scalar @MultipleCustomer || 1; 516 my %CustomerDataFrom; 517 if ( $Article{CustomerUserID} ) { 518 %CustomerDataFrom = $CustomerUserObject->CustomerUserDataGet( 519 User => $Article{CustomerUserID}, 520 ); 521 } 522 523 for my $Email ( Mail::Address->parse($ArticleFrom) ) { 524 525 my $CountAux = $CountFrom; 526 my $CustomerError = ''; 527 my $CustomerErrorMsg = 'CustomerGenericServerErrorMsg'; 528 my $CustomerDisabled = ''; 529 my $CustomerSelected = $CountFrom eq '1' ? 'checked="checked"' : ''; 530 my $EmailAddress = $Email->address(); 531 if ( !$CheckItemObject->CheckEmail( Address => $EmailAddress ) ) 532 { 533 $CustomerErrorMsg = $CheckItemObject->CheckErrorType() 534 . 'ServerErrorMsg'; 535 $CustomerError = 'ServerError'; 536 } 537 538 # check for duplicated entries 539 if ( defined $AddressesList{$Email} && $CustomerError eq '' ) { 540 $CustomerErrorMsg = 'IsDuplicatedServerErrorMsg'; 541 $CustomerError = 'ServerError'; 542 } 543 544 if ( $CustomerError ne '' ) { 545 $CustomerDisabled = 'disabled="disabled"'; 546 $CountAux = $CountFrom . 'Error'; 547 } 548 549 my $Phrase = ''; 550 if ( $Email->phrase() ) { 551 $Phrase = $Email->phrase(); 552 } 553 554 my $CustomerKey = ''; 555 if ( 556 defined $CustomerDataFrom{UserEmail} 557 && $CustomerDataFrom{UserEmail} eq $EmailAddress 558 ) 559 { 560 $CustomerKey = $Article{CustomerUserID}; 561 } 562 elsif ($EmailAddress) { 563 my %List = $CustomerUserObject->CustomerSearch( 564 PostMasterSearch => $EmailAddress, 565 ); 566 567 for my $UserLogin ( sort keys %List ) { 568 569 # Set right one if there is more than one customer user with the same email address. 570 if ( $Phrase && $List{$UserLogin} =~ /$Phrase/ ) { 571 $CustomerKey = $UserLogin; 572 } 573 } 574 } 575 576 my $CustomerElement = $EmailAddress; 577 if ($Phrase) { 578 $CustomerElement = $Phrase . " <$EmailAddress>"; 579 } 580 581 if ( $CustomerSelected && $CustomerKey ) { 582 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 583 User => $CustomerKey, 584 ); 585 } 586 587 push @MultipleCustomer, { 588 Count => $CountAux, 589 CustomerElement => $CustomerElement, 590 CustomerSelected => $CustomerSelected, 591 CustomerKey => $CustomerKey, 592 CustomerError => $CustomerError, 593 CustomerErrorMsg => $CustomerErrorMsg, 594 CustomerDisabled => $CustomerDisabled, 595 }; 596 $AddressesList{$EmailAddress} = 1; 597 $CountFrom++; 598 } 599 600 # get user preferences 601 my %UserPreferences = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 602 UserID => $Self->{UserID}, 603 ); 604 605 # store the dynamic fields default values or used specific default values to be used as 606 # ACLs info for all fields 607 my %DynamicFieldDefaults; 608 609 # cycle through the activated Dynamic Fields for this screen 610 DYNAMICFIELD: 611 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 612 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 613 next DYNAMICFIELD if !IsHashRefWithData( $DynamicFieldConfig->{Config} ); 614 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 615 616 # get default value from dynamic field config (if any) 617 my $DefaultValue = $DynamicFieldConfig->{Config}->{DefaultValue} || ''; 618 619 # override the value from user preferences if is set 620 if ( $UserPreferences{ 'UserDynamicField_' . $DynamicFieldConfig->{Name} } ) { 621 $DefaultValue = $UserPreferences{ 'UserDynamicField_' . $DynamicFieldConfig->{Name} }; 622 } 623 624 next DYNAMICFIELD if $DefaultValue eq ''; 625 next DYNAMICFIELD 626 if ref $DefaultValue eq 'ARRAY' && !IsArrayRefWithData($DefaultValue); 627 628 $DynamicFieldDefaults{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = $DefaultValue; 629 } 630 $GetParam{DynamicField} = \%DynamicFieldDefaults; 631 632 # get split article if given 633 # create html strings for all dynamic fields 634 my %DynamicFieldHTML; 635 636 my %SplitTicketParam; 637 638 # in case of split a TicketID and ArticleID are always given, send the TicketID to calculate 639 # ACLs based on parent information 640 if ( $Self->{TicketID} && $Article{ArticleID} ) { 641 $SplitTicketParam{TicketID} = $Self->{TicketID}; 642 } 643 644 # fix to bug# 8068 Field & DynamicField preselection on TicketSplit 645 # when splitting a ticket the selected attributes must remain in the new ticket screen 646 # this information will be available in the SplitTicketParam hash 647 if ( $SplitTicketParam{TicketID} ) { 648 649 # get information from original ticket (SplitTicket) 650 my %SplitTicketData = $TicketObject->TicketGet( 651 TicketID => $SplitTicketParam{TicketID}, 652 DynamicFields => 1, 653 UserID => $Self->{UserID}, 654 ); 655 656 # set simple IDs to pass them to the mask 657 for my $SplitedParam (qw(TypeID ServiceID SLAID PriorityID)) { 658 $SplitTicketParam{$SplitedParam} = $SplitTicketData{$SplitedParam}; 659 } 660 661 # set StateID as NextStateID 662 $SplitTicketParam{NextStateID} = $SplitTicketData{StateID}; 663 664 # set Owner and Responsible 665 $SplitTicketParam{UserSelected} = $SplitTicketData{OwnerID}; 666 $SplitTicketParam{ResponsibleUserSelected} = $SplitTicketData{ResponsibleID}; 667 668 # set additional information needed for Owner and Responsible 669 if ( $SplitTicketData{QueueID} ) { 670 $SplitTicketParam{QueueID} = $SplitTicketData{QueueID}; 671 } 672 $SplitTicketParam{AllUsers} = 1; 673 674 # set the selected queue in format ID||Name 675 $SplitTicketParam{FromSelected} = $SplitTicketData{QueueID} . '||' . $SplitTicketData{Queue}; 676 677 for my $Key ( sort keys %SplitTicketData ) { 678 if ( $Key =~ /DynamicField\_(.*)/ ) { 679 $SplitTicketParam{DynamicField}{$1} = $SplitTicketData{$Key}; 680 delete $SplitTicketParam{$Key}; 681 } 682 } 683 } 684 685 # cycle through the activated Dynamic Fields for this screen 686 DYNAMICFIELD: 687 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 688 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 689 690 my $PossibleValuesFilter; 691 692 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 693 DynamicFieldConfig => $DynamicFieldConfig, 694 Behavior => 'IsACLReducible', 695 ); 696 697 if ($IsACLReducible) { 698 699 # get PossibleValues 700 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 701 DynamicFieldConfig => $DynamicFieldConfig, 702 ); 703 704 # check if field has PossibleValues property in its configuration 705 if ( IsHashRefWithData($PossibleValues) ) { 706 707 # convert possible values key => value to key => key for ACLs using a Hash slice 708 my %AclData = %{$PossibleValues}; 709 @AclData{ keys %AclData } = keys %AclData; 710 711 # set possible values filter from ACLs 712 my $ACL = $TicketObject->TicketAcl( 713 %GetParam, 714 %ACLCompatGetParam, 715 %SplitTicketParam, 716 Action => $Self->{Action}, 717 ReturnType => 'Ticket', 718 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 719 Data => \%AclData, 720 UserID => $Self->{UserID}, 721 ); 722 if ($ACL) { 723 my %Filter = $TicketObject->TicketAclData(); 724 725 # convert Filer key => key back to key => value using map 726 %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } 727 keys %Filter; 728 } 729 } 730 } 731 732 # to store dynamic field value from database (or undefined) 733 my $Value; 734 735 # in case of split a TicketID and ArticleID are always given, Get the value 736 # from DB this cases 737 if ( $Self->{TicketID} && $Article{ArticleID} ) { 738 739 # select TicketID or ArticleID to get the value depending on dynamic field configuration 740 my $ObjectID = $DynamicFieldConfig->{ObjectType} eq 'Ticket' 741 ? $Self->{TicketID} 742 : $Article{ArticleID}; 743 744 # get value stored on the database (split) 745 $Value = $DynamicFieldBackendObject->ValueGet( 746 DynamicFieldConfig => $DynamicFieldConfig, 747 ObjectID => $ObjectID, 748 ); 749 } 750 751 # otherwise (on a new ticket). Check if the user has a user specific default value for 752 # the dynamic field, otherwise will use Dynamic Field default value 753 else { 754 755 # override the value from user preferences if is set 756 if ( $UserPreferences{ 'UserDynamicField_' . $DynamicFieldConfig->{Name} } ) { 757 $Value = $UserPreferences{ 'UserDynamicField_' . $DynamicFieldConfig->{Name} }; 758 } 759 } 760 761 # get field html 762 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = 763 $DynamicFieldBackendObject->EditFieldRender( 764 DynamicFieldConfig => $DynamicFieldConfig, 765 PossibleValuesFilter => $PossibleValuesFilter, 766 Value => $Value, 767 Mandatory => 768 $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, 769 LayoutObject => $LayoutObject, 770 ParamObject => $ParamObject, 771 AJAXUpdate => 1, 772 UpdatableFields => $Self->_GetFieldsToUpdate(), 773 ); 774 } 775 776 # run compose modules 777 if ( 778 ref $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') eq 779 'HASH' 780 ) 781 { 782 783 # Get Queue settings if 'Dest' param was set in the URL. 784 my %GetParam; 785 $GetParam{Dest} = $ParamObject->GetParam( Param => 'Dest' ); 786 787 if ( $GetParam{Dest} && $GetParam{Dest} =~ /^(\d{1,100})\|\|.+?$/ ) { 788 $GetParam{QueueID} = $1; 789 my %Queue = $QueueObject->GetSystemAddress( QueueID => $GetParam{QueueID} ); 790 $GetParam{From} = $Queue{Email}; 791 } 792 793 my %Jobs = %{ $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') }; 794 for my $Job ( sort keys %Jobs ) { 795 796 # load module 797 if ( !$MainObject->Require( $Jobs{$Job}->{Module} ) ) { 798 return $LayoutObject->FatalError(); 799 } 800 801 my $Object = $Jobs{$Job}->{Module}->new( 802 %{$Self}, 803 Debug => $Debug, 804 ); 805 806 # get params 807 PARAMETER: 808 for my $Parameter ( $Object->Option( %GetParam, Config => $Jobs{$Job} ) ) { 809 if ( $Jobs{$Job}->{ParamType} && $Jobs{$Job}->{ParamType} ne 'Single' ) { 810 @{ $GetParam{$Parameter} } = $ParamObject->GetArray( Param => $Parameter ); 811 next PARAMETER; 812 } 813 814 $GetParam{$Parameter} = $ParamObject->GetParam( Param => $Parameter ); 815 } 816 817 # run module 818 my $NewParams = $Object->Run( %GetParam, Config => $Jobs{$Job} ); 819 820 if ($NewParams) { 821 for my $Parameter ( $Object->Option( %GetParam, Config => $Jobs{$Job} ) ) { 822 $GetParam{$Parameter} = $NewParams; 823 } 824 } 825 } 826 } 827 828 # get all attachments meta data 829 my @Attachments = $UploadCacheObject->FormIDGetAllFilesMeta( 830 FormID => $Self->{FormID}, 831 ); 832 833 # get and format default subject and body 834 my $Subject = $Article{Subject}; 835 if ( !$Subject ) { 836 $Subject = $LayoutObject->Output( 837 Template => $Config->{Subject} || '', 838 ); 839 } 840 my $Body = $Article{Body} || ''; 841 if ( !$Body ) { 842 $Body = $LayoutObject->Output( 843 Template => $Config->{Body} || '', 844 ); 845 } 846 847 # make sure body is rich text (if body is based on config) 848 if ( !$GetParam{ArticleID} && $LayoutObject->{BrowserRichText} ) { 849 $Body = $LayoutObject->Ascii2RichText( 850 String => $Body, 851 ); 852 } 853 854 # in case of ticket split set $Self->{QueueID} as the QueueID of the original ticket, 855 # in order to set correct ACLs on page load (initial). See bug 8687. 856 if ( 857 IsHashRefWithData( \%SplitTicketParam ) 858 && $SplitTicketParam{QueueID} 859 && !$Self->{QueueID} 860 ) 861 { 862 $Self->{QueueID} = $SplitTicketParam{QueueID}; 863 } 864 865 my $Dest = ''; 866 if ( !$Self->{QueueID} && $GetParam{Dest} ) { 867 868 my @QueueParts = split( /\|\|/, $GetParam{Dest} ); 869 $Self->{QueueID} = $QueueParts[0]; 870 $Dest = $GetParam{Dest}; 871 } 872 873 # html output 874 my $Services = $Self->_GetServices( 875 %GetParam, 876 %ACLCompatGetParam, 877 %SplitTicketParam, 878 CustomerUserID => $CustomerData{UserLogin} || '', 879 QueueID => $Self->{QueueID} || 1, 880 ); 881 my $SLAs = $Self->_GetSLAs( 882 QueueID => $Self->{QueueID} || 1, 883 Services => $Services, 884 %GetParam, 885 %ACLCompatGetParam, 886 %SplitTicketParam, 887 ); 888 $Output .= $Self->_MaskEmailNew( 889 QueueID => $Self->{QueueID}, 890 NextStates => $Self->_GetNextStates( 891 %GetParam, 892 %ACLCompatGetParam, 893 %SplitTicketParam, 894 CustomerUserID => $CustomerData{UserLogin} || '', 895 QueueID => $Self->{QueueID} || 1 896 ), 897 Priorities => $Self->_GetPriorities( 898 %GetParam, 899 %ACLCompatGetParam, 900 %SplitTicketParam, 901 CustomerUserID => $CustomerData{UserLogin} || '', 902 QueueID => $Self->{QueueID} || 1 903 ), 904 Types => $Self->_GetTypes( 905 %GetParam, 906 %ACLCompatGetParam, 907 %SplitTicketParam, 908 CustomerUserID => $CustomerData{UserLogin} || '', 909 QueueID => $Self->{QueueID} || 1 910 ), 911 Services => $Services, 912 SLAs => $SLAs, 913 StandardTemplates => $Self->_GetStandardTemplates( 914 %GetParam, 915 %ACLCompatGetParam, 916 %SplitTicketParam, 917 QueueID => $Self->{QueueID} || '', 918 ), 919 Users => $Self->_GetUsers( 920 %GetParam, 921 %ACLCompatGetParam, 922 QueueID => $Self->{QueueID}, 923 %SplitTicketParam, 924 ), 925 ResponsibleUsers => $Self->_GetResponsibles( 926 %GetParam, 927 %ACLCompatGetParam, 928 QueueID => $Self->{QueueID}, 929 %SplitTicketParam, 930 ), 931 FromList => $Self->_GetTos( 932 %GetParam, 933 %ACLCompatGetParam, 934 %SplitTicketParam, 935 CustomerUserID => $CustomerData{UserLogin} || '', 936 QueueID => $Self->{QueueID} 937 ), 938 TimeUnits => $Self->_GetTimeUnits( 939 %GetParam, 940 %ACLCompatGetParam, 941 %SplitTicketParam, 942 ArticleID => $Article{ArticleID}, 943 ), 944 FromSelected => $Dest, 945 To => $Article{From} // '', 946 Subject => $Subject, 947 Body => $Body, 948 CustomerID => $SplitTicketData{CustomerID}, 949 CustomerUser => $SplitTicketData{CustomerUserID}, 950 CustomerData => \%CustomerData, 951 Attachments => \@Attachments, 952 LinkTicketID => $GetParam{LinkTicketID} || '', 953 TimeUnitsRequired => ( 954 $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') 955 ? 'Validate_Required' 956 : '' 957 ), 958 %SplitTicketParam, 959 DynamicFieldHTML => \%DynamicFieldHTML, 960 MultipleCustomer => \@MultipleCustomer, 961 MultipleCustomerCc => \@MultipleCustomerCc, 962 MultipleCustomerBcc => \@MultipleCustomerBcc, 963 ); 964 965 $Output .= $LayoutObject->Footer(); 966 return $Output; 967 } 968 969 # deliver signature 970 elsif ( $Self->{Subaction} eq 'Signature' ) { 971 my $CustomerUser = $ParamObject->GetParam( Param => 'SelectedCustomerUser' ) || ''; 972 my $QueueID = $ParamObject->GetParam( Param => 'QueueID' ); 973 if ( !$QueueID ) { 974 my $Dest = $ParamObject->GetParam( Param => 'Dest' ) || ''; 975 ($QueueID) = split( /\|\|/, $Dest ); 976 } 977 978 # start with empty signature (no queue selected) - if we have a queue, get the sig. 979 my $Signature = ''; 980 if ($QueueID) { 981 $Signature = $Self->_GetSignature( 982 QueueID => $QueueID, 983 CustomerUserID => $CustomerUser, 984 ); 985 } 986 my $MimeType = 'text/plain'; 987 if ( $LayoutObject->{BrowserRichText} ) { 988 $MimeType = 'text/html'; 989 $Signature = $LayoutObject->RichTextDocumentComplete( 990 String => $Signature, 991 ); 992 } 993 994 return $LayoutObject->Attachment( 995 ContentType => $MimeType . '; charset=' . $LayoutObject->{Charset}, 996 Content => $Signature, 997 Type => 'inline', 998 NoCache => 1, 999 ); 1000 } 1001 1002 # create new ticket and article 1003 elsif ( $Self->{Subaction} eq 'StoreNew' ) { 1004 1005 my %Error; 1006 my $NextStateID = $ParamObject->GetParam( Param => 'NextStateID' ) || ''; 1007 my %StateData; 1008 if ($NextStateID) { 1009 %StateData = $Kernel::OM->Get('Kernel::System::State')->StateGet( 1010 ID => $NextStateID, 1011 ); 1012 } 1013 my $NextState = $StateData{Name}; 1014 my $NewResponsibleID = $ParamObject->GetParam( Param => 'NewResponsibleID' ) || ''; 1015 my $NewUserID = $ParamObject->GetParam( Param => 'NewUserID' ) || ''; 1016 my $Dest = $ParamObject->GetParam( Param => 'Dest' ) || ''; 1017 1018 # see if only a name has been passed 1019 if ( $Dest && $Dest !~ m{ \A (\d+)? \| \| .+ \z }xms ) { 1020 1021 # see if we can get an ID for this queue name 1022 my $DestID = $QueueObject->QueueLookup( 1023 Queue => $Dest, 1024 ); 1025 1026 if ($DestID) { 1027 $Dest = $DestID . '||' . $Dest; 1028 } 1029 else { 1030 $Dest = ''; 1031 } 1032 } 1033 1034 my ( $NewQueueID, $From ) = split( /\|\|/, $Dest ); 1035 if ( !$NewQueueID ) { 1036 $GetParam{OwnerAll} = 1; 1037 } 1038 else { 1039 my %Queue = $QueueObject->GetSystemAddress( QueueID => $NewQueueID ); 1040 $GetParam{From} = $Queue{Email}; 1041 } 1042 1043 my $CustomerUser = $ParamObject->GetParam( Param => 'CustomerUser' ) 1044 || $ParamObject->GetParam( Param => 'PreSelectedCustomerUser' ) 1045 || $ParamObject->GetParam( Param => 'SelectedCustomerUser' ) 1046 || ''; 1047 my $CustomerID = $ParamObject->GetParam( Param => 'CustomerID' ) || ''; 1048 my $SelectedCustomerUser = $ParamObject->GetParam( Param => 'SelectedCustomerUser' ) 1049 || ''; 1050 my $ExpandCustomerName = $ParamObject->GetParam( Param => 'ExpandCustomerName' ) 1051 || 0; 1052 my %FromExternalCustomer; 1053 $FromExternalCustomer{Customer} = $ParamObject->GetParam( Param => 'PreSelectedCustomerUser' ) 1054 || $ParamObject->GetParam( Param => 'CustomerUser' ) 1055 || ''; 1056 $GetParam{QueueID} = $NewQueueID; 1057 $GetParam{ExpandCustomerName} = $ExpandCustomerName; 1058 1059 # get sender queue from 1060 my $Signature = ''; 1061 if ($NewQueueID) { 1062 $Signature = $Self->_GetSignature( 1063 QueueID => $NewQueueID, 1064 CustomerUserID => $CustomerUser 1065 ); 1066 } 1067 1068 if ( $ParamObject->GetParam( Param => 'OwnerAllRefresh' ) ) { 1069 $GetParam{OwnerAll} = 1; 1070 $ExpandCustomerName = 3; 1071 } 1072 if ( $ParamObject->GetParam( Param => 'ResponsibleAllRefresh' ) ) { 1073 $GetParam{ResponsibleAll} = 1; 1074 $ExpandCustomerName = 3; 1075 } 1076 if ( $ParamObject->GetParam( Param => 'ClearTo' ) ) { 1077 $GetParam{To} = ''; 1078 $ExpandCustomerName = 3; 1079 } 1080 for my $Number ( 1 .. 2 ) { 1081 my $Item = $ParamObject->GetParam( Param => "ExpandCustomerName$Number" ) || 0; 1082 if ( $Number == 1 && $Item ) { 1083 $ExpandCustomerName = 1; 1084 } 1085 elsif ( $Number == 2 && $Item ) { 1086 $ExpandCustomerName = 2; 1087 } 1088 } 1089 1090 # create html strings for all dynamic fields 1091 my %DynamicFieldHTML; 1092 1093 # cycle through the activated Dynamic Fields for this screen 1094 DYNAMICFIELD: 1095 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1096 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1097 1098 my $PossibleValuesFilter; 1099 1100 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 1101 DynamicFieldConfig => $DynamicFieldConfig, 1102 Behavior => 'IsACLReducible', 1103 ); 1104 1105 if ($IsACLReducible) { 1106 1107 # get PossibleValues 1108 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 1109 DynamicFieldConfig => $DynamicFieldConfig, 1110 ); 1111 1112 # check if field has PossibleValues property in its configuration 1113 if ( IsHashRefWithData($PossibleValues) ) { 1114 1115 # convert possible values key => value to key => key for ACLs using a Hash slice 1116 my %AclData = %{$PossibleValues}; 1117 @AclData{ keys %AclData } = keys %AclData; 1118 1119 # set possible values filter from ACLs 1120 my $ACL = $TicketObject->TicketAcl( 1121 %GetParam, 1122 %ACLCompatGetParam, 1123 CustomerUserID => $CustomerUser || '', 1124 Action => $Self->{Action}, 1125 ReturnType => 'Ticket', 1126 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 1127 Data => \%AclData, 1128 UserID => $Self->{UserID}, 1129 ); 1130 if ($ACL) { 1131 my %Filter = $TicketObject->TicketAclData(); 1132 1133 # convert Filer key => key back to key => value using map 1134 %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } 1135 keys %Filter; 1136 } 1137 } 1138 } 1139 1140 my $ValidationResult; 1141 1142 # do not validate on attachment upload 1143 if ( !$ExpandCustomerName ) { 1144 1145 $ValidationResult = $DynamicFieldBackendObject->EditFieldValueValidate( 1146 DynamicFieldConfig => $DynamicFieldConfig, 1147 PossibleValuesFilter => $PossibleValuesFilter, 1148 ParamObject => $ParamObject, 1149 Mandatory => 1150 $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, 1151 ); 1152 1153 if ( !IsHashRefWithData($ValidationResult) ) { 1154 return $LayoutObject->ErrorScreen( 1155 Message => 1156 $LayoutObject->{LanguageObject} 1157 ->Translate( 'Could not perform validation on field %s!', $DynamicFieldConfig->{Label} ), 1158 Comment => Translatable('Please contact the administrator.'), 1159 ); 1160 } 1161 1162 # propagate validation error to the Error variable to be detected by the frontend 1163 if ( $ValidationResult->{ServerError} ) { 1164 $Error{ $DynamicFieldConfig->{Name} } = ' ServerError'; 1165 } 1166 } 1167 1168 # get field html 1169 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} } = $DynamicFieldBackendObject->EditFieldRender( 1170 DynamicFieldConfig => $DynamicFieldConfig, 1171 PossibleValuesFilter => $PossibleValuesFilter, 1172 Mandatory => 1173 $Config->{DynamicField}->{ $DynamicFieldConfig->{Name} } == 2, 1174 ServerError => $ValidationResult->{ServerError} || '', 1175 ErrorMessage => $ValidationResult->{ErrorMessage} || '', 1176 LayoutObject => $LayoutObject, 1177 ParamObject => $ParamObject, 1178 AJAXUpdate => 1, 1179 UpdatableFields => $Self->_GetFieldsToUpdate(), 1180 ); 1181 } 1182 1183 # get all attachments meta data 1184 my @Attachments = $UploadCacheObject->FormIDGetAllFilesMeta( 1185 FormID => $Self->{FormID}, 1186 ); 1187 1188 # get customer user object 1189 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 1190 1191 # Expand Customer Name 1192 my %CustomerUserData; 1193 if ( $ExpandCustomerName == 1 ) { 1194 1195 # search customer 1196 my %CustomerUserList; 1197 %CustomerUserList = $CustomerUserObject->CustomerSearch( 1198 Search => $GetParam{To}, 1199 ); 1200 1201 # check if just one customer user exists 1202 # if just one, fillup CustomerUserID and CustomerID 1203 $Param{CustomerUserListCount} = 0; 1204 for my $CustomerUserKey ( sort keys %CustomerUserList ) { 1205 $Param{CustomerUserListCount}++; 1206 $Param{CustomerUserListLast} = $CustomerUserList{$CustomerUserKey}; 1207 $Param{CustomerUserListLastUser} = $CustomerUserKey; 1208 } 1209 if ( $Param{CustomerUserListCount} == 1 ) { 1210 $GetParam{To} = $Param{CustomerUserListLast}; 1211 $Error{ExpandCustomerName} = 1; 1212 my %CustomerUserData = $CustomerUserObject->CustomerUserDataGet( 1213 User => $Param{CustomerUserListLastUser}, 1214 ); 1215 if ( $CustomerUserData{UserCustomerID} ) { 1216 $CustomerID = $CustomerUserData{UserCustomerID}; 1217 } 1218 if ( $CustomerUserData{UserLogin} ) { 1219 $CustomerUser = $CustomerUserData{UserLogin}; 1220 } 1221 } 1222 1223 # if more the one customer user exists, show list 1224 # and clean CustomerUserID and CustomerID 1225 else { 1226 1227 # don't check email syntax on multi customer select 1228 $ConfigObject->Set( 1229 Key => 'CheckEmailAddresses', 1230 Value => 0 1231 ); 1232 $CustomerID = ''; 1233 1234 # clear to if there is no customer found 1235 if ( !%CustomerUserList ) { 1236 $GetParam{To} = ''; 1237 } 1238 $Error{ExpandCustomerName} = 1; 1239 } 1240 } 1241 1242 # get from and customer id if customer user is given 1243 elsif ( $ExpandCustomerName == 2 ) { 1244 %CustomerUserData = $CustomerUserObject->CustomerUserDataGet( 1245 User => $CustomerUser, 1246 ); 1247 my %CustomerUserList = $CustomerUserObject->CustomerSearch( 1248 UserLogin => $CustomerUser, 1249 ); 1250 for my $CustomerUserKey ( sort keys %CustomerUserList ) { 1251 $GetParam{To} = $CustomerUserList{$CustomerUserKey}; 1252 } 1253 if ( $CustomerUserData{UserCustomerID} ) { 1254 $CustomerID = $CustomerUserData{UserCustomerID}; 1255 } 1256 if ( $CustomerUserData{UserLogin} ) { 1257 $CustomerUser = $CustomerUserData{UserLogin}; 1258 } 1259 if ( $FromExternalCustomer{Customer} ) { 1260 my %ExternalCustomerUserData = $CustomerUserObject->CustomerUserDataGet( 1261 User => $FromExternalCustomer{Customer}, 1262 ); 1263 $FromExternalCustomer{Email} = $ExternalCustomerUserData{UserMailString}; 1264 } 1265 $Error{ExpandCustomerName} = 1; 1266 } 1267 1268 # if a new destination queue is selected 1269 elsif ( $ExpandCustomerName == 3 ) { 1270 $Error{NoSubmit} = 1; 1271 $CustomerUser = $SelectedCustomerUser; 1272 } 1273 1274 # show customer info 1275 my %CustomerData; 1276 if ( $ConfigObject->Get('Ticket::Frontend::CustomerInfoCompose') ) { 1277 if ( $CustomerUser || $SelectedCustomerUser ) { 1278 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1279 User => $CustomerUser || $SelectedCustomerUser, 1280 ); 1281 } 1282 elsif ($CustomerID) { 1283 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1284 CustomerID => $CustomerID, 1285 ); 1286 } 1287 } 1288 1289 # check email address 1290 PARAMETER: 1291 for my $Parameter (qw(To Cc Bcc)) { 1292 next PARAMETER if !$GetParam{$Parameter}; 1293 for my $Email ( Mail::Address->parse( $GetParam{$Parameter} ) ) { 1294 if ( !$CheckItemObject->CheckEmail( Address => $Email->address() ) ) { 1295 $Error{ $Parameter . 'ErrorType' } 1296 = $Parameter . $CheckItemObject->CheckErrorType() . 'ServerErrorMsg'; 1297 $Error{ $Parameter . 'Invalid' } = 'ServerError'; 1298 } 1299 1300 my $IsLocal = $Kernel::OM->Get('Kernel::System::SystemAddress')->SystemAddressIsLocalAddress( 1301 Address => $Email->address() 1302 ); 1303 if ($IsLocal) { 1304 $Error{ $Parameter . 'IsLocalAddress' } = 'ServerError'; 1305 } 1306 } 1307 } 1308 1309 # if it is not a subaction about attachments, check for server errors 1310 if ( !$ExpandCustomerName ) { 1311 if ( !$GetParam{To} ) { 1312 $Error{'ToInvalid'} = 'ServerError'; 1313 } 1314 if ( !$GetParam{Subject} ) { 1315 $Error{'SubjectInvalid'} = 'ServerError'; 1316 } 1317 if ( !$NewQueueID ) { 1318 $Error{'DestinationInvalid'} = 'ServerError'; 1319 } 1320 if ( !$GetParam{Body} ) { 1321 $Error{'BodyInvalid'} = 'ServerError'; 1322 } 1323 1324 # check if date is valid 1325 if ( 1326 !$ExpandCustomerName 1327 && $StateData{TypeName} 1328 && $StateData{TypeName} =~ /^pending/i 1329 ) 1330 { 1331 1332 # convert pending date to a datetime object 1333 my $PendingDateTimeObject = $Kernel::OM->Create( 1334 'Kernel::System::DateTime', 1335 ObjectParams => { 1336 %GetParam, 1337 Second => 0, 1338 }, 1339 ); 1340 1341 # get current system epoch 1342 my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 1343 1344 if ( !$PendingDateTimeObject || $PendingDateTimeObject < $CurSystemDateTimeObject ) { 1345 $Error{'DateInvalid'} = 'ServerError'; 1346 } 1347 } 1348 1349 if ( 1350 $ConfigObject->Get('Ticket::Service') 1351 && $GetParam{SLAID} 1352 && !$GetParam{ServiceID} 1353 ) 1354 { 1355 $Error{'ServiceInvalid'} = 'ServerError'; 1356 } 1357 1358 # check mandatory service 1359 if ( 1360 $ConfigObject->Get('Ticket::Service') 1361 && $Config->{ServiceMandatory} 1362 && !$GetParam{ServiceID} 1363 ) 1364 { 1365 $Error{'ServiceInvalid'} = ' ServerError'; 1366 } 1367 1368 # check mandatory sla 1369 if ( 1370 $ConfigObject->Get('Ticket::Service') 1371 && $Config->{SLAMandatory} 1372 && !$GetParam{SLAID} 1373 ) 1374 { 1375 $Error{'SLAInvalid'} = ' ServerError'; 1376 } 1377 1378 if ( $ConfigObject->Get('Ticket::Type') && !$GetParam{TypeID} ) { 1379 $Error{'TypeInvalid'} = 'ServerError'; 1380 } 1381 if ( 1382 $ConfigObject->Get('Ticket::Frontend::AccountTime') 1383 && $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') 1384 && $GetParam{TimeUnits} eq '' 1385 ) 1386 { 1387 $Error{'TimeUnitsInvalid'} = 'ServerError'; 1388 } 1389 } 1390 1391 # run compose modules 1392 my %ArticleParam; 1393 if ( ref $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') eq 'HASH' ) { 1394 my %Jobs = %{ $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') }; 1395 for my $Job ( sort keys %Jobs ) { 1396 1397 # load module 1398 if ( !$MainObject->Require( $Jobs{$Job}->{Module} ) ) { 1399 return $LayoutObject->FatalError(); 1400 } 1401 1402 my $Object = $Jobs{$Job}->{Module}->new( 1403 %{$Self}, 1404 Debug => $Debug, 1405 ); 1406 1407 # get params 1408 my $Multiple; 1409 PARAMETER: 1410 for my $Parameter ( $Object->Option( %GetParam, Config => $Jobs{$Job} ) ) { 1411 1412 if ( $Jobs{$Job}->{ParamType} && $Jobs{$Job}->{ParamType} ne 'Single' ) { 1413 @{ $GetParam{$Parameter} } = $ParamObject->GetArray( Param => $Parameter ); 1414 $Multiple = 1; 1415 next PARAMETER; 1416 } 1417 1418 $GetParam{$Parameter} = $ParamObject->GetParam( Param => $Parameter ); 1419 } 1420 1421 # run module 1422 $Object->Run( 1423 %GetParam, 1424 StoreNew => 1, 1425 Config => $Jobs{$Job} 1426 ); 1427 1428 # get options that have been removed from the selection 1429 # and add them back to the selection so that the submit 1430 # will contain options that were hidden from the agent 1431 my $Key = $Object->Option( %GetParam, Config => $Jobs{$Job} ); 1432 1433 if ( $Object->can('GetOptionsToRemoveAJAX') ) { 1434 my @RemovedOptions = $Object->GetOptionsToRemoveAJAX(%GetParam); 1435 if (@RemovedOptions) { 1436 if ($Multiple) { 1437 for my $RemovedOption (@RemovedOptions) { 1438 push @{ $GetParam{$Key} }, $RemovedOption; 1439 } 1440 } 1441 else { 1442 $GetParam{$Key} = shift @RemovedOptions; 1443 } 1444 } 1445 } 1446 1447 # ticket params 1448 %ArticleParam = ( 1449 %ArticleParam, 1450 $Object->ArticleOption( %GetParam, %ArticleParam, Config => $Jobs{$Job} ), 1451 ); 1452 1453 # get errors 1454 %Error = ( 1455 %Error, 1456 $Object->Error( %GetParam, Config => $Jobs{$Job} ), 1457 ); 1458 } 1459 } 1460 1461 if (%Error) { 1462 1463 if ( $Error{ToIsLocalAddress} ) { 1464 $LayoutObject->Block( 1465 Name => 'ToIsLocalAddressServerErrorMsg', 1466 Data => \%GetParam, 1467 ); 1468 } 1469 1470 if ( $Error{CcIsLocalAddress} ) { 1471 $LayoutObject->Block( 1472 Name => 'CcIsLocalAddressServerErrorMsg', 1473 Data => \%GetParam, 1474 ); 1475 } 1476 1477 if ( $Error{BccIsLocalAddress} ) { 1478 $LayoutObject->Block( 1479 Name => 'BccIsLocalAddressServerErrorMsg', 1480 Data => \%GetParam, 1481 ); 1482 } 1483 1484 # get and format default subject and body 1485 my $Subject = $LayoutObject->Output( 1486 Template => $Config->{Subject} || '', 1487 ); 1488 1489 my $Body = $LayoutObject->Output( 1490 Template => $Config->{Body} || '', 1491 ); 1492 1493 # make sure body is rich text 1494 if ( $LayoutObject->{BrowserRichText} ) { 1495 $Body = $LayoutObject->Ascii2RichText( 1496 String => $Body, 1497 ); 1498 } 1499 1500 #set Body and Subject parameters for Output 1501 if ( !$GetParam{Subject} ) { 1502 $GetParam{Subject} = $Subject; 1503 } 1504 1505 if ( !$GetParam{Body} ) { 1506 $GetParam{Body} = $Body; 1507 } 1508 1509 # get services 1510 my $Services = $Self->_GetServices( 1511 %GetParam, 1512 %ACLCompatGetParam, 1513 CustomerUserID => $CustomerUser || '', 1514 QueueID => $NewQueueID || 1, 1515 ); 1516 1517 # reset previous ServiceID to reset SLA-List if no service is selected 1518 if ( !$GetParam{ServiceID} || !$Services->{ $GetParam{ServiceID} } ) { 1519 $GetParam{ServiceID} = ''; 1520 } 1521 1522 my $SLAs = $Self->_GetSLAs( 1523 %GetParam, 1524 %ACLCompatGetParam, 1525 QueueID => $NewQueueID || 1, 1526 Services => $Services, 1527 ); 1528 1529 # header 1530 $Output .= $LayoutObject->Header(); 1531 $Output .= $LayoutObject->NavigationBar(); 1532 1533 # html output 1534 $Output .= $Self->_MaskEmailNew( 1535 QueueID => $Self->{QueueID}, 1536 Users => $Self->_GetUsers( 1537 %GetParam, 1538 %ACLCompatGetParam, 1539 QueueID => $NewQueueID, 1540 AllUsers => $GetParam{OwnerAll} 1541 ), 1542 UserSelected => $NewUserID, 1543 ResponsibleUsers => $Self->_GetResponsibles( 1544 %GetParam, 1545 %ACLCompatGetParam, 1546 QueueID => $NewQueueID, 1547 AllUsers => $GetParam{ResponsibleAll} 1548 ), 1549 ResponsibleUserSelected => $NewResponsibleID, 1550 NextStates => $Self->_GetNextStates( 1551 %GetParam, 1552 %ACLCompatGetParam, 1553 CustomerUserID => $CustomerUser || '', 1554 QueueID => $NewQueueID || 1, 1555 ), 1556 NextState => $NextState, 1557 Priorities => $Self->_GetPriorities( 1558 %GetParam, 1559 %ACLCompatGetParam, 1560 CustomerUserID => $CustomerUser || '', 1561 QueueID => $NewQueueID || 1, 1562 ), 1563 Types => $Self->_GetTypes( 1564 %GetParam, 1565 %ACLCompatGetParam, 1566 CustomerUserID => $CustomerUser || '', 1567 QueueID => $NewQueueID || 1, 1568 ), 1569 Services => $Services, 1570 SLAs => $SLAs, 1571 StandardTemplates => $Self->_GetStandardTemplates( 1572 %GetParam, 1573 %ACLCompatGetParam, 1574 QueueID => $NewQueueID || '', 1575 ), 1576 CustomerID => $LayoutObject->Ascii2Html( Text => $CustomerID ), 1577 CustomerUser => $CustomerUser, 1578 CustomerData => \%CustomerData, 1579 TimeUnitsRequired => ( 1580 $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') 1581 ? 'Validate_Required' 1582 : '' 1583 ), 1584 FromList => $Self->_GetTos(), 1585 FromSelected => $Dest, 1586 Subject => $LayoutObject->Ascii2Html( Text => $GetParam{Subject} ), 1587 Body => $LayoutObject->Ascii2Html( Text => $GetParam{Body} ), 1588 Errors => \%Error, 1589 Attachments => \@Attachments, 1590 Signature => $Signature, 1591 %GetParam, 1592 DynamicFieldHTML => \%DynamicFieldHTML, 1593 MultipleCustomer => \@MultipleCustomer, 1594 MultipleCustomerCc => \@MultipleCustomerCc, 1595 MultipleCustomerBcc => \@MultipleCustomerBcc, 1596 FromExternalCustomer => \%FromExternalCustomer, 1597 ); 1598 1599 $Output .= $LayoutObject->Footer(); 1600 return $Output; 1601 } 1602 1603 # challenge token check for write action 1604 $LayoutObject->ChallengeTokenCheck(); 1605 1606 # create new ticket, do db insert 1607 my $TicketID = $TicketObject->TicketCreate( 1608 Title => $GetParam{Subject}, 1609 QueueID => $NewQueueID, 1610 Subject => $GetParam{Subject}, 1611 Lock => 'unlock', 1612 TypeID => $GetParam{TypeID}, 1613 ServiceID => $GetParam{ServiceID}, 1614 SLAID => $GetParam{SLAID}, 1615 StateID => $NextStateID, 1616 PriorityID => $GetParam{PriorityID}, 1617 OwnerID => 1, 1618 CustomerID => $CustomerID, 1619 CustomerUser => $SelectedCustomerUser, 1620 UserID => $Self->{UserID}, 1621 ); 1622 1623 # set ticket dynamic fields 1624 # cycle through the activated Dynamic Fields for this screen 1625 DYNAMICFIELD: 1626 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1627 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1628 next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Ticket'; 1629 1630 # set the value 1631 my $Success = $DynamicFieldBackendObject->ValueSet( 1632 DynamicFieldConfig => $DynamicFieldConfig, 1633 ObjectID => $TicketID, 1634 Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, 1635 UserID => $Self->{UserID}, 1636 ); 1637 } 1638 1639 # get pre loaded attachment 1640 @Attachments = $UploadCacheObject->FormIDGetAllFilesData( 1641 FormID => $Self->{FormID}, 1642 ); 1643 1644 # get submit attachment 1645 my %UploadStuff = $ParamObject->GetUploadAll( 1646 Param => 'FileUpload', 1647 ); 1648 if (%UploadStuff) { 1649 push @Attachments, \%UploadStuff; 1650 } 1651 1652 # prepare subject 1653 my $Tn = $TicketObject->TicketNumberLookup( TicketID => $TicketID ); 1654 $GetParam{Subject} = $TicketObject->TicketSubjectBuild( 1655 TicketNumber => $Tn, 1656 Subject => $GetParam{Subject} || '', 1657 Type => 'New', 1658 ); 1659 1660 # check if new owner is given (then send no agent notify) 1661 my $NoAgentNotify = 0; 1662 if ($NewUserID) { 1663 $NoAgentNotify = 1; 1664 } 1665 1666 my $MimeType = 'text/plain'; 1667 if ( $LayoutObject->{BrowserRichText} ) { 1668 $MimeType = 'text/html'; 1669 $GetParam{Body} .= '<br/><br/>' . $Signature; 1670 1671 # remove unused inline images 1672 my @NewAttachmentData; 1673 ATTACHMENT: 1674 for my $Attachment (@Attachments) { 1675 my $ContentID = $Attachment->{ContentID}; 1676 if ( 1677 $ContentID 1678 && ( $Attachment->{ContentType} =~ /image/i ) 1679 && ( $Attachment->{Disposition} eq 'inline' ) 1680 ) 1681 { 1682 my $ContentIDHTMLQuote = $LayoutObject->Ascii2Html( 1683 Text => $ContentID, 1684 ); 1685 1686 # workaround for link encode of rich text editor, see bug#5053 1687 my $ContentIDLinkEncode = $LayoutObject->LinkEncode($ContentID); 1688 $GetParam{Body} =~ s/(ContentID=)$ContentIDLinkEncode/$1$ContentID/g; 1689 1690 # ignore attachment if not linked in body 1691 next ATTACHMENT 1692 if $GetParam{Body} !~ /(\Q$ContentIDHTMLQuote\E|\Q$ContentID\E)/i; 1693 } 1694 1695 # remember inline images and normal attachments 1696 push @NewAttachmentData, \%{$Attachment}; 1697 } 1698 @Attachments = @NewAttachmentData; 1699 1700 # verify html document 1701 $GetParam{Body} = $LayoutObject->RichTextDocumentComplete( 1702 String => $GetParam{Body}, 1703 ); 1704 } 1705 else { 1706 $GetParam{Body} .= "\n\n" . $Signature; 1707 } 1708 1709 # lookup sender 1710 my $TemplateGenerator = $Kernel::OM->Get('Kernel::System::TemplateGenerator'); 1711 my $Sender = $TemplateGenerator->Sender( 1712 QueueID => $NewQueueID, 1713 UserID => $Self->{UserID}, 1714 ); 1715 1716 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 1717 my $ArticleBackendObject = $ArticleObject->BackendForChannel( ChannelName => 'Email' ); 1718 1719 # send email 1720 my $ArticleID = $ArticleBackendObject->ArticleSend( 1721 NoAgentNotify => $NoAgentNotify, 1722 Attachment => \@Attachments, 1723 TicketID => $TicketID, 1724 SenderType => $Config->{SenderType}, 1725 IsVisibleForCustomer => $Config->{IsVisibleForCustomer}, 1726 From => $Sender, 1727 To => $GetParam{To}, 1728 Cc => $GetParam{Cc}, 1729 Bcc => $GetParam{Bcc}, 1730 Subject => $GetParam{Subject}, 1731 Body => $GetParam{Body}, 1732 Charset => $LayoutObject->{UserCharset}, 1733 MimeType => $MimeType, 1734 UserID => $Self->{UserID}, 1735 HistoryType => $Config->{HistoryType}, 1736 HistoryComment => $Config->{HistoryComment} 1737 || "\%\%$GetParam{To}, $GetParam{Cc}, $GetParam{Bcc}", 1738 %ArticleParam, 1739 ); 1740 if ( !$ArticleID ) { 1741 return $LayoutObject->ErrorScreen(); 1742 } 1743 1744 # set article dynamic fields 1745 # cycle through the activated Dynamic Fields for this screen 1746 DYNAMICFIELD: 1747 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1748 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1749 next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'Article'; 1750 1751 # set the value 1752 my $Success = $DynamicFieldBackendObject->ValueSet( 1753 DynamicFieldConfig => $DynamicFieldConfig, 1754 ObjectID => $ArticleID, 1755 Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, 1756 UserID => $Self->{UserID}, 1757 ); 1758 } 1759 1760 # remove pre-submitted attachments 1761 $UploadCacheObject->FormIDRemove( FormID => $Self->{FormID} ); 1762 1763 # link tickets 1764 if ( 1765 $GetParam{LinkTicketID} 1766 && $Config->{SplitLinkType} 1767 && $Config->{SplitLinkType}->{LinkType} 1768 && $Config->{SplitLinkType}->{Direction} 1769 ) 1770 { 1771 my $Access = $TicketObject->TicketPermission( 1772 Type => 'ro', 1773 TicketID => $GetParam{LinkTicketID}, 1774 UserID => $Self->{UserID} 1775 ); 1776 1777 if ( !$Access ) { 1778 return $LayoutObject->NoPermission( 1779 Message => "You need ro permission!", 1780 WithHeader => 'yes', 1781 ); 1782 } 1783 1784 my $SourceKey = $GetParam{LinkTicketID}; 1785 my $TargetKey = $TicketID; 1786 1787 if ( $Config->{SplitLinkType}->{Direction} eq 'Source' ) { 1788 $SourceKey = $TicketID; 1789 $TargetKey = $GetParam{LinkTicketID}; 1790 } 1791 1792 # link the tickets 1793 $Kernel::OM->Get('Kernel::System::LinkObject')->LinkAdd( 1794 SourceObject => 'Ticket', 1795 SourceKey => $SourceKey, 1796 TargetObject => 'Ticket', 1797 TargetKey => $TargetKey, 1798 Type => $Config->{SplitLinkType}->{LinkType} || 'Normal', 1799 State => 'Valid', 1800 UserID => $Self->{UserID}, 1801 ); 1802 } 1803 1804 # set owner (if new user id is given) 1805 if ($NewUserID) { 1806 $TicketObject->TicketOwnerSet( 1807 TicketID => $TicketID, 1808 NewUserID => $NewUserID, 1809 UserID => $Self->{UserID}, 1810 ); 1811 1812 # set lock 1813 $TicketObject->TicketLockSet( 1814 TicketID => $TicketID, 1815 Lock => 'lock', 1816 UserID => $Self->{UserID}, 1817 ); 1818 } 1819 1820 # else set owner to current agent but do not lock it 1821 else { 1822 $TicketObject->TicketOwnerSet( 1823 TicketID => $TicketID, 1824 NewUserID => $Self->{UserID}, 1825 SendNoNotification => 1, 1826 UserID => $Self->{UserID}, 1827 ); 1828 } 1829 1830 # set responsible (if new user id is given) 1831 if ($NewResponsibleID) { 1832 $TicketObject->TicketResponsibleSet( 1833 TicketID => $TicketID, 1834 NewUserID => $NewResponsibleID, 1835 UserID => $Self->{UserID}, 1836 ); 1837 } 1838 1839 # time accounting 1840 if ( $GetParam{TimeUnits} ) { 1841 $TicketObject->TicketAccountTime( 1842 TicketID => $TicketID, 1843 ArticleID => $ArticleID, 1844 TimeUnit => $GetParam{TimeUnits}, 1845 UserID => $Self->{UserID}, 1846 ); 1847 } 1848 1849 # should i set an unlock? 1850 if ( $StateData{TypeName} =~ /^close/i ) { 1851 1852 # set lock 1853 $TicketObject->TicketLockSet( 1854 TicketID => $TicketID, 1855 Lock => 'unlock', 1856 UserID => $Self->{UserID}, 1857 ); 1858 } 1859 1860 # set pending time 1861 elsif ( $StateData{TypeName} =~ /^pending/i ) { 1862 1863 # set pending time 1864 $TicketObject->TicketPendingTimeSet( 1865 UserID => $Self->{UserID}, 1866 TicketID => $TicketID, 1867 %GetParam, 1868 ); 1869 } 1870 1871 # get redirect screen 1872 my $NextScreen = $Self->{UserCreateNextMask} || 'AgentTicketEmail'; 1873 1874 # redirect 1875 return $LayoutObject->Redirect( 1876 OP => "Action=$NextScreen;Subaction=Created;TicketID=$TicketID", 1877 ); 1878 } 1879 elsif ( $Self->{Subaction} eq 'AJAXUpdate' ) { 1880 my $Dest = $ParamObject->GetParam( Param => 'Dest' ) || ''; 1881 my $CustomerUser = $ParamObject->GetParam( Param => 'SelectedCustomerUser' ); 1882 my $ElementChanged = $ParamObject->GetParam( Param => 'ElementChanged' ) || ''; 1883 1884 # get From based on selected queue 1885 my $QueueID = ''; 1886 if ( $Dest =~ /^(\d{1,100})\|\|.+?$/ ) { 1887 $QueueID = $1; 1888 my %Queue = $QueueObject->GetSystemAddress( QueueID => $QueueID ); 1889 $GetParam{From} = $Queue{Email}; 1890 } 1891 1892 # get list type 1893 my $TreeView = 0; 1894 if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { 1895 $TreeView = 1; 1896 } 1897 1898 my $Tos = $Self->_GetTos( 1899 %GetParam, 1900 %ACLCompatGetParam, 1901 CustomerUserID => $CustomerUser || '', 1902 QueueID => $QueueID, 1903 ); 1904 1905 my $NewTos; 1906 1907 if ($Tos) { 1908 TOs: 1909 for my $KeyTo ( sort keys %{$Tos} ) { 1910 next TOs if ( $Tos->{$KeyTo} eq '-' ); 1911 $NewTos->{"$KeyTo||$Tos->{$KeyTo}"} = $Tos->{$KeyTo}; 1912 } 1913 } 1914 my $Signature = ''; 1915 if ($QueueID) { 1916 $Signature = $Self->_GetSignature( 1917 QueueID => $QueueID, 1918 CustomerUserID => $CustomerUser, 1919 ); 1920 } 1921 my $Users = $Self->_GetUsers( 1922 %GetParam, 1923 %ACLCompatGetParam, 1924 QueueID => $QueueID, 1925 AllUsers => $GetParam{OwnerAll}, 1926 ); 1927 my $ResponsibleUsers = $Self->_GetResponsibles( 1928 %GetParam, 1929 %ACLCompatGetParam, 1930 QueueID => $QueueID, 1931 AllUsers => $GetParam{ResponsibleAll}, 1932 ); 1933 my $NextStates = $Self->_GetNextStates( 1934 %GetParam, 1935 %ACLCompatGetParam, 1936 CustomerUserID => $CustomerUser || '', 1937 QueueID => $QueueID || 1, 1938 ); 1939 my $Priorities = $Self->_GetPriorities( 1940 %GetParam, 1941 %ACLCompatGetParam, 1942 CustomerUserID => $CustomerUser || '', 1943 QueueID => $QueueID || 1, 1944 ); 1945 my $Services = $Self->_GetServices( 1946 %GetParam, 1947 %ACLCompatGetParam, 1948 CustomerUserID => $CustomerUser || '', 1949 QueueID => $QueueID || 1, 1950 ); 1951 my $SLAs = $Self->_GetSLAs( 1952 %GetParam, 1953 %ACLCompatGetParam, 1954 CustomerUserID => $CustomerUser || '', 1955 QueueID => $QueueID || 1, 1956 Services => $Services, 1957 ); 1958 my $StandardTemplates = $Self->_GetStandardTemplates( 1959 %GetParam, 1960 %ACLCompatGetParam, 1961 QueueID => $QueueID || '', 1962 ); 1963 my $Types = $Self->_GetTypes( 1964 %GetParam, 1965 %ACLCompatGetParam, 1966 CustomerUserID => $CustomerUser || '', 1967 QueueID => $QueueID || 1, 1968 ); 1969 1970 # update Dynamic Fields Possible Values via AJAX 1971 my @DynamicFieldAJAX; 1972 1973 # cycle through the activated Dynamic Fields for this screen 1974 DYNAMICFIELD: 1975 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1976 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1977 1978 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 1979 DynamicFieldConfig => $DynamicFieldConfig, 1980 Behavior => 'IsACLReducible', 1981 ); 1982 next DYNAMICFIELD if !$IsACLReducible; 1983 1984 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 1985 DynamicFieldConfig => $DynamicFieldConfig, 1986 ); 1987 1988 # convert possible values key => value to key => key for ACLs using a Hash slice 1989 my %AclData = %{$PossibleValues}; 1990 @AclData{ keys %AclData } = keys %AclData; 1991 1992 # set possible values filter from ACLs 1993 my $ACL = $TicketObject->TicketAcl( 1994 %GetParam, 1995 %ACLCompatGetParam, 1996 CustomerUserID => $CustomerUser || '', 1997 Action => $Self->{Action}, 1998 TicketID => $Self->{TicketID}, 1999 QueueID => $QueueID || 0, 2000 ReturnType => 'Ticket', 2001 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 2002 Data => \%AclData, 2003 UserID => $Self->{UserID}, 2004 ); 2005 if ($ACL) { 2006 my %Filter = $TicketObject->TicketAclData(); 2007 2008 # convert Filer key => key back to key => value using map 2009 %{$PossibleValues} = map { $_ => $PossibleValues->{$_} } keys %Filter; 2010 } 2011 2012 my $DataValues = $DynamicFieldBackendObject->BuildSelectionDataGet( 2013 DynamicFieldConfig => $DynamicFieldConfig, 2014 PossibleValues => $PossibleValues, 2015 Value => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, 2016 ) || $PossibleValues; 2017 2018 # add dynamic field to the list of fields to update 2019 push( 2020 @DynamicFieldAJAX, 2021 { 2022 Name => 'DynamicField_' . $DynamicFieldConfig->{Name}, 2023 Data => $DataValues, 2024 SelectedID => $DynamicFieldValues{ $DynamicFieldConfig->{Name} }, 2025 Translation => $DynamicFieldConfig->{Config}->{TranslatableValues} || 0, 2026 Max => 100, 2027 } 2028 ); 2029 } 2030 2031 my @TemplateAJAX; 2032 2033 # update ticket body and attachements if needed. 2034 if ( $ElementChanged eq 'StandardTemplateID' ) { 2035 my @TicketAttachments; 2036 my $TemplateText; 2037 2038 # remove all attachments from the Upload cache 2039 my $RemoveSuccess = $UploadCacheObject->FormIDRemove( 2040 FormID => $Self->{FormID}, 2041 ); 2042 if ( !$RemoveSuccess ) { 2043 $Kernel::OM->Get('Kernel::System::Log')->Log( 2044 Priority => 'error', 2045 Message => "Form attachments could not be deleted!", 2046 ); 2047 } 2048 2049 # get the template text and set new attachments if a template is selected 2050 if ( IsPositiveInteger( $GetParam{StandardTemplateID} ) ) { 2051 my $TemplateGenerator = $Kernel::OM->Get('Kernel::System::TemplateGenerator'); 2052 2053 # set template text, replace smart tags (limited as ticket is not created) 2054 $TemplateText = $TemplateGenerator->Template( 2055 TemplateID => $GetParam{StandardTemplateID}, 2056 UserID => $Self->{UserID}, 2057 CustomerUserID => $CustomerUser, 2058 ); 2059 2060 # create StdAttachmentObject 2061 my $StdAttachmentObject = $Kernel::OM->Get('Kernel::System::StdAttachment'); 2062 2063 # add std. attachments to ticket 2064 my %AllStdAttachments = $StdAttachmentObject->StdAttachmentStandardTemplateMemberList( 2065 StandardTemplateID => $GetParam{StandardTemplateID}, 2066 ); 2067 for ( sort keys %AllStdAttachments ) { 2068 my %AttachmentsData = $StdAttachmentObject->StdAttachmentGet( ID => $_ ); 2069 $UploadCacheObject->FormIDAddFile( 2070 FormID => $Self->{FormID}, 2071 Disposition => 'attachment', 2072 %AttachmentsData, 2073 ); 2074 } 2075 2076 # send a list of attachments in the upload cache back to the clientside JavaScript 2077 # which renders then the list of currently uploaded attachments 2078 @TicketAttachments = $UploadCacheObject->FormIDGetAllFilesMeta( 2079 FormID => $Self->{FormID}, 2080 ); 2081 2082 for my $Attachment (@TicketAttachments) { 2083 $Attachment->{Filesize} = $LayoutObject->HumanReadableDataSize( 2084 Size => $Attachment->{Filesize}, 2085 ); 2086 } 2087 } 2088 2089 @TemplateAJAX = ( 2090 { 2091 Name => 'UseTemplateCreate', 2092 Data => '0', 2093 }, 2094 { 2095 Name => 'RichText', 2096 Data => $TemplateText || '', 2097 }, 2098 { 2099 Name => 'TicketAttachments', 2100 Data => \@TicketAttachments, 2101 KeepData => 1, 2102 }, 2103 ); 2104 } 2105 2106 my @ExtendedData; 2107 2108 # run compose modules 2109 if ( ref $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') eq 'HASH' ) { 2110 2111 # use QueueID from web request in compose modules 2112 $GetParam{QueueID} = $QueueID; 2113 2114 my %Jobs = %{ $ConfigObject->Get('Ticket::Frontend::ArticleComposeModule') }; 2115 JOB: 2116 for my $Job ( sort keys %Jobs ) { 2117 2118 # load module 2119 next JOB if !$MainObject->Require( $Jobs{$Job}->{Module} ); 2120 2121 my $Object = $Jobs{$Job}->{Module}->new( 2122 %{$Self}, 2123 Debug => $Debug, 2124 ); 2125 2126 my $Multiple; 2127 2128 # get params 2129 PARAMETER: 2130 for my $Parameter ( $Object->Option( %GetParam, Config => $Jobs{$Job} ) ) { 2131 2132 if ( $Jobs{$Job}->{ParamType} && $Jobs{$Job}->{ParamType} ne 'Single' ) { 2133 @{ $GetParam{$Parameter} } = $ParamObject->GetArray( Param => $Parameter ); 2134 $Multiple = 1; 2135 next PARAMETER; 2136 } 2137 2138 $GetParam{$Parameter} = $ParamObject->GetParam( Param => $Parameter ); 2139 } 2140 2141 # run module 2142 my %Data = $Object->Data( %GetParam, Config => $Jobs{$Job} ); 2143 2144 # get AJAX param values 2145 if ( $Object->can('GetParamAJAX') ) { 2146 %GetParam = ( %GetParam, $Object->GetParamAJAX(%GetParam) ); 2147 } 2148 2149 # get options that have to be removed from the selection visible 2150 # to the agent. These options will be added again on submit. 2151 if ( $Object->can('GetOptionsToRemoveAJAX') ) { 2152 my @OptionsToRemove = $Object->GetOptionsToRemoveAJAX(%GetParam); 2153 2154 for my $OptionToRemove (@OptionsToRemove) { 2155 delete $Data{$OptionToRemove}; 2156 } 2157 } 2158 2159 my $Key = $Object->Option( %GetParam, Config => $Jobs{$Job} ); 2160 2161 if ($Key) { 2162 push( 2163 @ExtendedData, 2164 { 2165 Name => $Key, 2166 Data => \%Data, 2167 SelectedID => $GetParam{$Key}, 2168 Translation => 1, 2169 PossibleNone => 1, 2170 Multiple => $Multiple, 2171 Max => 100, 2172 } 2173 ); 2174 } 2175 } 2176 } 2177 2178 # convert Signature to ASCII, if RichText is on 2179 if ( $LayoutObject->{BrowserRichText} ) { 2180 2181 # $Signature = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii( String => $Signature, ); 2182 } 2183 2184 my $JSON = $LayoutObject->BuildSelectionJSON( 2185 [ 2186 { 2187 Name => 'Dest', 2188 Data => $NewTos, 2189 SelectedID => $Dest, 2190 Translation => 0, 2191 PossibleNone => 1, 2192 TreeView => $TreeView, 2193 Max => 100, 2194 }, 2195 { 2196 Name => 'Signature', 2197 Data => $Signature, 2198 Translation => 1, 2199 PossibleNone => 1, 2200 Max => 100, 2201 }, 2202 { 2203 Name => 'NewUserID', 2204 Data => $Users, 2205 SelectedID => $GetParam{NewUserID}, 2206 Translation => 0, 2207 PossibleNone => 1, 2208 Max => 100, 2209 }, 2210 { 2211 Name => 'NewResponsibleID', 2212 Data => $ResponsibleUsers, 2213 SelectedID => $GetParam{NewResponsibleID}, 2214 Translation => 0, 2215 PossibleNone => 1, 2216 Max => 100, 2217 }, 2218 { 2219 Name => 'NextStateID', 2220 Data => $NextStates, 2221 SelectedID => $GetParam{NextStateID}, 2222 Translation => 1, 2223 Max => 100, 2224 }, 2225 { 2226 Name => 'PriorityID', 2227 Data => $Priorities, 2228 SelectedID => $GetParam{PriorityID}, 2229 Translation => 1, 2230 Max => 100, 2231 }, 2232 { 2233 Name => 'ServiceID', 2234 Data => $Services, 2235 SelectedID => $GetParam{ServiceID}, 2236 PossibleNone => 1, 2237 Translation => 0, 2238 TreeView => $TreeView, 2239 Max => 100, 2240 }, 2241 { 2242 Name => 'SLAID', 2243 Data => $SLAs, 2244 SelectedID => $GetParam{SLAID}, 2245 PossibleNone => 1, 2246 Translation => 0, 2247 Max => 100, 2248 }, 2249 { 2250 Name => 'StandardTemplateID', 2251 Data => $StandardTemplates, 2252 SelectedID => $GetParam{StandardTemplateID}, 2253 PossibleNone => 1, 2254 Translation => 1, 2255 Max => 100, 2256 }, 2257 { 2258 Name => 'TypeID', 2259 Data => $Types, 2260 SelectedID => $GetParam{TypeID}, 2261 PossibleNone => 1, 2262 Translation => 0, 2263 Max => 100, 2264 }, 2265 @DynamicFieldAJAX, 2266 @TemplateAJAX, 2267 @ExtendedData, 2268 ], 2269 ); 2270 return $LayoutObject->Attachment( 2271 ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, 2272 Content => $JSON, 2273 Type => 'inline', 2274 NoCache => 1, 2275 ); 2276 } 2277 else { 2278 return $LayoutObject->ErrorScreen( 2279 Message => Translatable('No Subaction!'), 2280 Comment => Translatable('Please contact the administrator.'), 2281 ); 2282 } 2283} 2284 2285sub _GetNextStates { 2286 my ( $Self, %Param ) = @_; 2287 2288 my %NextStates; 2289 if ( $Param{QueueID} || $Param{TicketID} ) { 2290 %NextStates = $Kernel::OM->Get('Kernel::System::Ticket')->TicketStateList( 2291 %Param, 2292 Action => $Self->{Action}, 2293 UserID => $Self->{UserID}, 2294 ); 2295 } 2296 return \%NextStates; 2297} 2298 2299sub _GetUsers { 2300 my ( $Self, %Param ) = @_; 2301 2302 # get users 2303 my %ShownUsers; 2304 my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList( 2305 Type => 'Long', 2306 Valid => 1, 2307 ); 2308 2309 # get ticket object 2310 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 2311 2312 # just show only users with selected custom queue 2313 if ( $Param{QueueID} && !$Param{AllUsers} ) { 2314 my @UserIDs = $TicketObject->GetSubscribedUserIDsByQueueID(%Param); 2315 for my $GroupMemberKey ( sort keys %AllGroupsMembers ) { 2316 my $Hit = 0; 2317 for my $UID (@UserIDs) { 2318 if ( $UID eq $GroupMemberKey ) { 2319 $Hit = 1; 2320 } 2321 } 2322 if ( !$Hit ) { 2323 delete $AllGroupsMembers{$GroupMemberKey}; 2324 } 2325 } 2326 } 2327 2328 # check show users 2329 if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) { 2330 %ShownUsers = %AllGroupsMembers; 2331 } 2332 2333 # show all users who are owner or rw in the queue group 2334 elsif ( $Param{QueueID} ) { 2335 my $GID = $Kernel::OM->Get('Kernel::System::Queue')->GetQueueGroupID( QueueID => $Param{QueueID} ); 2336 my %MemberList = $Kernel::OM->Get('Kernel::System::Group')->PermissionGroupGet( 2337 GroupID => $GID, 2338 Type => 'owner', 2339 ); 2340 for my $MemberKey ( sort keys %MemberList ) { 2341 if ( $AllGroupsMembers{$MemberKey} ) { 2342 $ShownUsers{$MemberKey} = $AllGroupsMembers{$MemberKey}; 2343 } 2344 } 2345 } 2346 2347 # workflow 2348 my $ACL = $TicketObject->TicketAcl( 2349 %Param, 2350 Action => $Self->{Action}, 2351 ReturnType => 'Ticket', 2352 ReturnSubType => 'Owner', 2353 Data => \%ShownUsers, 2354 UserID => $Self->{UserID}, 2355 ); 2356 2357 return { $TicketObject->TicketAclData() } if $ACL; 2358 2359 return \%ShownUsers; 2360} 2361 2362sub _GetResponsibles { 2363 my ( $Self, %Param ) = @_; 2364 2365 # get users 2366 my %ShownUsers; 2367 my %AllGroupsMembers = $Kernel::OM->Get('Kernel::System::User')->UserList( 2368 Type => 'Long', 2369 Valid => 1, 2370 ); 2371 2372 # get ticket object 2373 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 2374 2375 # just show only users with selected custom queue 2376 if ( $Param{QueueID} && !$Param{AllUsers} ) { 2377 my @UserIDs = $TicketObject->GetSubscribedUserIDsByQueueID(%Param); 2378 for my $GroupMemberKey ( sort keys %AllGroupsMembers ) { 2379 my $Hit = 0; 2380 for my $UID (@UserIDs) { 2381 if ( $UID eq $GroupMemberKey ) { 2382 $Hit = 1; 2383 } 2384 } 2385 if ( !$Hit ) { 2386 delete $AllGroupsMembers{$GroupMemberKey}; 2387 } 2388 } 2389 } 2390 2391 # check show users 2392 if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ChangeOwnerToEveryone') ) { 2393 %ShownUsers = %AllGroupsMembers; 2394 } 2395 2396 # show all users who are responsible or rw in the queue group 2397 elsif ( $Param{QueueID} ) { 2398 my $GID = $Kernel::OM->Get('Kernel::System::Queue')->GetQueueGroupID( QueueID => $Param{QueueID} ); 2399 my %MemberList = $Kernel::OM->Get('Kernel::System::Group')->PermissionGroupGet( 2400 GroupID => $GID, 2401 Type => 'responsible', 2402 ); 2403 for my $MemberKey ( sort keys %MemberList ) { 2404 if ( $AllGroupsMembers{$MemberKey} ) { 2405 $ShownUsers{$MemberKey} = $AllGroupsMembers{$MemberKey}; 2406 } 2407 } 2408 } 2409 2410 # workflow 2411 my $ACL = $TicketObject->TicketAcl( 2412 %Param, 2413 Action => $Self->{Action}, 2414 ReturnType => 'Ticket', 2415 ReturnSubType => 'Responsible', 2416 Data => \%ShownUsers, 2417 UserID => $Self->{UserID}, 2418 ); 2419 2420 return { $TicketObject->TicketAclData() } if $ACL; 2421 2422 return \%ShownUsers; 2423} 2424 2425sub _GetPriorities { 2426 my ( $Self, %Param ) = @_; 2427 2428 # get priority 2429 my %Priorities; 2430 if ( $Param{QueueID} || $Param{TicketID} ) { 2431 %Priorities = $Kernel::OM->Get('Kernel::System::Ticket')->TicketPriorityList( 2432 %Param, 2433 Action => $Self->{Action}, 2434 UserID => $Self->{UserID}, 2435 ); 2436 } 2437 return \%Priorities; 2438} 2439 2440sub _GetTypes { 2441 my ( $Self, %Param ) = @_; 2442 2443 # get type 2444 my %Type; 2445 if ( $Param{QueueID} || $Param{TicketID} ) { 2446 %Type = $Kernel::OM->Get('Kernel::System::Ticket')->TicketTypeList( 2447 %Param, 2448 Action => $Self->{Action}, 2449 UserID => $Self->{UserID}, 2450 ); 2451 } 2452 return \%Type; 2453} 2454 2455sub _GetServices { 2456 my ( $Self, %Param ) = @_; 2457 2458 # get service 2459 my %Service; 2460 2461 # check needed 2462 return \%Service if !$Param{QueueID} && !$Param{TicketID}; 2463 2464 # get options for default services for unknown customers 2465 my $DefaultServiceUnknownCustomer 2466 = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Service::Default::UnknownCustomer'); 2467 2468 # check if no CustomerUserID is selected 2469 # if $DefaultServiceUnknownCustomer = 0 leave CustomerUserID empty, it will not get any services 2470 # if $DefaultServiceUnknownCustomer = 1 set CustomerUserID to get default services 2471 if ( !$Param{CustomerUserID} && $DefaultServiceUnknownCustomer ) { 2472 $Param{CustomerUserID} = '<DEFAULT>'; 2473 } 2474 2475 # get service list 2476 if ( $Param{CustomerUserID} ) { 2477 %Service = $Kernel::OM->Get('Kernel::System::Ticket')->TicketServiceList( 2478 %Param, 2479 Action => $Self->{Action}, 2480 UserID => $Self->{UserID}, 2481 ); 2482 } 2483 return \%Service; 2484} 2485 2486sub _GetSLAs { 2487 my ( $Self, %Param ) = @_; 2488 2489 # get sla 2490 my %SLA; 2491 if ( $Param{ServiceID} && $Param{Services} && %{ $Param{Services} } ) { 2492 if ( $Param{Services}->{ $Param{ServiceID} } ) { 2493 %SLA = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSLAList( 2494 %Param, 2495 Action => $Self->{Action}, 2496 UserID => $Self->{UserID}, 2497 ); 2498 } 2499 } 2500 return \%SLA; 2501} 2502 2503sub _GetTos { 2504 my ( $Self, %Param ) = @_; 2505 2506 # get config object 2507 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 2508 2509 # check own selection 2510 my %NewTos; 2511 if ( $ConfigObject->Get('Ticket::Frontend::NewQueueOwnSelection') ) { 2512 %NewTos = %{ $ConfigObject->Get('Ticket::Frontend::NewQueueOwnSelection') }; 2513 } 2514 else { 2515 2516 # SelectionType Queue or SystemAddress? 2517 my %Tos; 2518 if ( $ConfigObject->Get('Ticket::Frontend::NewQueueSelectionType') eq 'Queue' ) { 2519 %Tos = $Kernel::OM->Get('Kernel::System::Ticket')->MoveList( 2520 %Param, 2521 Type => 'create', 2522 Action => $Self->{Action}, 2523 UserID => $Self->{UserID}, 2524 ); 2525 } 2526 else { 2527 %Tos = $Kernel::OM->Get('Kernel::System::SystemAddress')->SystemAddressQueueList(); 2528 } 2529 2530 # get create permission queues 2531 my %UserGroups = $Kernel::OM->Get('Kernel::System::Group')->PermissionUserGet( 2532 UserID => $Self->{UserID}, 2533 Type => 'create', 2534 ); 2535 2536 # build selection string 2537 QUEUEID: 2538 for my $QueueID ( sort keys %Tos ) { 2539 my %QueueData = $Kernel::OM->Get('Kernel::System::Queue')->QueueGet( ID => $QueueID ); 2540 2541 # permission check, can we create new tickets in queue 2542 next QUEUEID if !$UserGroups{ $QueueData{GroupID} }; 2543 2544 my $String = $ConfigObject->Get('Ticket::Frontend::NewQueueSelectionString') 2545 || '<Realname> <<Email>> - Queue: <Queue>'; 2546 $String =~ s/<Queue>/$QueueData{Name}/g; 2547 $String =~ s/<QueueComment>/$QueueData{Comment}/g; 2548 2549 # remove trailing spaces 2550 $String =~ s{\s+\z}{} if !$QueueData{Comment}; 2551 2552 if ( $ConfigObject->Get('Ticket::Frontend::NewQueueSelectionType') ne 'Queue' ) 2553 { 2554 my %SystemAddressData = $Kernel::OM->Get('Kernel::System::SystemAddress')->SystemAddressGet( 2555 ID => $Tos{$QueueID}, 2556 ); 2557 $String =~ s/<Realname>/$SystemAddressData{Realname}/g; 2558 $String =~ s/<Email>/$SystemAddressData{Name}/g; 2559 } 2560 $NewTos{$QueueID} = $String; 2561 } 2562 } 2563 2564 # add empty selection 2565 $NewTos{''} = '-'; 2566 return \%NewTos; 2567} 2568 2569sub _GetSignature { 2570 my ( $Self, %Param ) = @_; 2571 2572 # prepare signature 2573 my $TemplateGenerator = $Kernel::OM->Get('Kernel::System::TemplateGenerator'); 2574 my $Signature = $TemplateGenerator->Signature( 2575 QueueID => $Param{QueueID}, 2576 Data => \%Param, 2577 UserID => $Self->{UserID}, 2578 ); 2579 2580 return $Signature; 2581} 2582 2583sub _GetTimeUnits { 2584 my ( $Self, %Param ) = @_; 2585 2586 my $AccountedTime = ''; 2587 2588 # Get accounted time if AccountTime config item is enabled. 2589 if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Frontend::AccountTime') && defined $Param{ArticleID} ) { 2590 $AccountedTime = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleAccountedTimeGet( 2591 ArticleID => $Param{ArticleID}, 2592 ); 2593 } 2594 2595 return $AccountedTime ? $AccountedTime : ''; 2596} 2597 2598sub _GetStandardTemplates { 2599 my ( $Self, %Param ) = @_; 2600 2601 my %Templates; 2602 my $QueueID = $Param{QueueID} || ''; 2603 2604 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 2605 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 2606 2607 if ( !$QueueID ) { 2608 my $UserDefaultQueue = $ConfigObject->Get('Ticket::Frontend::UserDefaultQueue') || ''; 2609 2610 if ($UserDefaultQueue) { 2611 $QueueID = $QueueObject->QueueLookup( Queue => $UserDefaultQueue ); 2612 } 2613 } 2614 2615 # check needed 2616 return \%Templates if !$QueueID && !$Param{TicketID}; 2617 2618 if ( !$QueueID && $Param{TicketID} ) { 2619 2620 # get QueueID from the ticket 2621 my %Ticket = $Kernel::OM->Get('Kernel::System::Ticket')->TicketGet( 2622 TicketID => $Param{TicketID}, 2623 DynamicFields => 0, 2624 UserID => $Self->{UserID}, 2625 ); 2626 $QueueID = $Ticket{QueueID} || ''; 2627 } 2628 2629 # fetch all std. templates 2630 my %StandardTemplates = $QueueObject->QueueStandardTemplateMemberList( 2631 QueueID => $QueueID, 2632 TemplateTypes => 1, 2633 ); 2634 2635 # return empty hash if there are no templates for this screen 2636 return \%Templates if !IsHashRefWithData( $StandardTemplates{Create} ); 2637 2638 # return just the templates for this screen 2639 return $StandardTemplates{Create}; 2640} 2641 2642sub _MaskEmailNew { 2643 my ( $Self, %Param ) = @_; 2644 2645 $Param{FormID} = $Self->{FormID}; 2646 2647 # get config object 2648 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 2649 2650 # get list type 2651 my $TreeView = 0; 2652 if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { 2653 $TreeView = 1; 2654 } 2655 2656 # get layout object 2657 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 2658 2659 # set JS data 2660 $LayoutObject->AddJSData( 2661 Key => 'CustomerSearch', 2662 Value => { 2663 ShowCustomerTickets => $ConfigObject->Get('Ticket::Frontend::ShowCustomerTickets'), 2664 }, 2665 ); 2666 2667 # build string 2668 $Param{OptionStrg} = $LayoutObject->BuildSelection( 2669 Data => $Param{Users}, 2670 SelectedID => $Param{UserSelected}, 2671 Translation => 0, 2672 PossibleNone => 1, 2673 Name => 'NewUserID', 2674 Class => 'Modernize', 2675 ); 2676 2677 my $Config = $Kernel::OM->Get('Kernel::Config')->Get("Ticket::Frontend::$Self->{Action}"); 2678 2679 # build next states string 2680 $Param{NextStatesStrg} = $LayoutObject->BuildSelection( 2681 Data => $Param{NextStates}, 2682 Name => 'NextStateID', 2683 Class => 'Modernize', 2684 Translation => 1, 2685 SelectedValue => $Param{NextState} || $Config->{StateDefault}, 2686 ); 2687 2688 # build Destination string 2689 my %NewTo; 2690 if ( $Param{FromList} ) { 2691 for my $FromKey ( sort keys %{ $Param{FromList} } ) { 2692 $NewTo{"$FromKey||$Param{FromList}->{$FromKey}"} = $Param{FromList}->{$FromKey}; 2693 } 2694 } 2695 if ( !$Param{FromSelected} ) { 2696 my $UserDefaultQueue = $ConfigObject->Get('Ticket::Frontend::UserDefaultQueue') || ''; 2697 if ($UserDefaultQueue) { 2698 my $QueueID = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup( Queue => $UserDefaultQueue ); 2699 if ($QueueID) { 2700 $Param{FromSelected} = "$QueueID||$UserDefaultQueue"; 2701 } 2702 } 2703 } 2704 2705 if ( $ConfigObject->Get('Ticket::Frontend::NewQueueSelectionType') eq 'Queue' ) { 2706 $Param{FromStrg} = $LayoutObject->AgentQueueListOption( 2707 Data => \%NewTo, 2708 Multiple => 0, 2709 Size => 0, 2710 Class => 'Validate_Required Modernize ' . ( $Param{Errors}->{DestinationInvalid} || ' ' ), 2711 Name => 'Dest', 2712 TreeView => $TreeView, 2713 SelectedID => $Param{FromSelected}, 2714 OnChangeSubmit => 0, 2715 ); 2716 } 2717 else { 2718 $Param{FromStrg} = $LayoutObject->BuildSelection( 2719 Data => \%NewTo, 2720 Class => 'Validate_Required Modernize ' . ( $Param{Errors}->{DestinationInvalid} || ' ' ), 2721 Name => 'Dest', 2722 TreeView => $TreeView, 2723 SelectedID => $Param{FromSelected}, 2724 ); 2725 } 2726 2727 # customer info string 2728 if ( $ConfigObject->Get('Ticket::Frontend::CustomerInfoCompose') ) { 2729 $Param{CustomerTable} = $LayoutObject->AgentCustomerViewTable( 2730 Data => $Param{CustomerData}, 2731 Max => $ConfigObject->Get('Ticket::Frontend::CustomerInfoComposeMaxSize'), 2732 ); 2733 $LayoutObject->Block( 2734 Name => 'CustomerTable', 2735 Data => \%Param, 2736 ); 2737 } 2738 2739 # prepare errors! 2740 if ( $Param{Errors} ) { 2741 for my $ErrorKey ( sort keys %{ $Param{Errors} } ) { 2742 $Param{$ErrorKey} = $LayoutObject->Ascii2Html( Text => $Param{Errors}->{$ErrorKey} ); 2743 } 2744 } 2745 2746 # From external 2747 my $ShowErrors = 1; 2748 if ( 2749 defined $Param{FromExternalCustomer} 2750 && 2751 defined $Param{FromExternalCustomer}->{Email} && 2752 defined $Param{FromExternalCustomer}->{Customer} 2753 ) 2754 { 2755 $ShowErrors = 0; 2756 $LayoutObject->Block( 2757 Name => 'FromExternalCustomer', 2758 Data => $Param{FromExternalCustomer}, 2759 ); 2760 2761 $LayoutObject->AddJSData( 2762 Key => 'DataEmail', 2763 Value => $Param{FromExternalCustomer}->{Email}, 2764 ); 2765 $LayoutObject->AddJSData( 2766 Key => 'DataCustomer', 2767 Value => $Param{FromExternalCustomer}->{Customer}, 2768 ); 2769 } 2770 2771 # Cc 2772 my $CustomerCounterCc = 0; 2773 if ( $Param{MultipleCustomerCc} ) { 2774 for my $Item ( @{ $Param{MultipleCustomerCc} } ) { 2775 if ( !$ShowErrors ) { 2776 2777 # set empty values for errors 2778 $Item->{CustomerError} = ''; 2779 $Item->{CustomerDisabled} = ''; 2780 $Item->{CustomerErrorMsg} = 'CustomerGenericServerErrorMsg'; 2781 } 2782 $LayoutObject->Block( 2783 Name => 'CcMultipleCustomer', 2784 Data => $Item, 2785 ); 2786 $LayoutObject->Block( 2787 Name => 'Cc' . $Item->{CustomerErrorMsg}, 2788 Data => $Item, 2789 ); 2790 if ( $Item->{CustomerError} ) { 2791 $LayoutObject->Block( 2792 Name => 'CcCustomerErrorExplantion', 2793 ); 2794 } 2795 $CustomerCounterCc++; 2796 } 2797 } 2798 2799 if ( !$CustomerCounterCc ) { 2800 $Param{CcCustomerHiddenContainer} = 'Hidden'; 2801 } 2802 2803 # set customer counter 2804 $LayoutObject->Block( 2805 Name => 'CcMultipleCustomerCounter', 2806 Data => { 2807 CustomerCounter => $CustomerCounterCc++, 2808 }, 2809 ); 2810 2811 # Bcc 2812 my $CustomerCounterBcc = 0; 2813 if ( $Param{MultipleCustomerBcc} ) { 2814 for my $Item ( @{ $Param{MultipleCustomerBcc} } ) { 2815 if ( !$ShowErrors ) { 2816 2817 # set empty values for errors 2818 $Item->{CustomerError} = ''; 2819 $Item->{CustomerDisabled} = ''; 2820 $Item->{CustomerErrorMsg} = 'CustomerGenericServerErrorMsg'; 2821 } 2822 $LayoutObject->Block( 2823 Name => 'BccMultipleCustomer', 2824 Data => $Item, 2825 ); 2826 $LayoutObject->Block( 2827 Name => 'Bcc' . $Item->{CustomerErrorMsg}, 2828 Data => $Item, 2829 ); 2830 if ( $Item->{CustomerError} ) { 2831 $LayoutObject->Block( 2832 Name => 'BccCustomerErrorExplantion', 2833 ); 2834 } 2835 $CustomerCounterBcc++; 2836 } 2837 } 2838 2839 if ( !$CustomerCounterBcc ) { 2840 $Param{BccCustomerHiddenContainer} = 'Hidden'; 2841 } 2842 2843 # set customer counter 2844 $LayoutObject->Block( 2845 Name => 'BccMultipleCustomerCounter', 2846 Data => { 2847 CustomerCounter => $CustomerCounterBcc++, 2848 }, 2849 ); 2850 2851 # To 2852 my $CustomerCounter = 0; 2853 if ( $Param{MultipleCustomer} ) { 2854 for my $Item ( @{ $Param{MultipleCustomer} } ) { 2855 if ( !$ShowErrors ) { 2856 2857 # set empty values for errors 2858 $Item->{CustomerError} = ''; 2859 $Item->{CustomerDisabled} = ''; 2860 $Item->{CustomerErrorMsg} = 'CustomerGenericServerErrorMsg'; 2861 } 2862 $LayoutObject->Block( 2863 Name => 'MultipleCustomer', 2864 Data => $Item, 2865 ); 2866 $LayoutObject->Block( 2867 Name => $Item->{CustomerErrorMsg}, 2868 Data => $Item, 2869 ); 2870 if ( $Item->{CustomerError} ) { 2871 $LayoutObject->Block( 2872 Name => 'CustomerErrorExplantion', 2873 ); 2874 } 2875 $CustomerCounter++; 2876 } 2877 } 2878 2879 if ( !$CustomerCounter ) { 2880 $Param{CustomerHiddenContainer} = 'Hidden'; 2881 } 2882 2883 # set customer counter 2884 $LayoutObject->Block( 2885 Name => 'MultipleCustomerCounter', 2886 Data => { 2887 CustomerCounter => $CustomerCounter++, 2888 }, 2889 ); 2890 2891 if ( $Param{ToInvalid} && $Param{Errors} && !$Param{Errors}->{ToErrorType} ) { 2892 $LayoutObject->Block( 2893 Name => 'ToServerErrorMsg', 2894 ); 2895 } 2896 if ( $Param{Errors}->{ToErrorType} || !$ShowErrors ) { 2897 $Param{ToInvalid} = ''; 2898 } 2899 2900 if ( $Param{CcInvalid} && $Param{Errors} && !$Param{Errors}->{CcErrorType} ) { 2901 $LayoutObject->Block( 2902 Name => 'CcServerErrorMsg', 2903 ); 2904 } 2905 if ( $Param{Errors}->{CcErrorType} || !$ShowErrors ) { 2906 $Param{CcInvalid} = ''; 2907 } 2908 2909 if ( $Param{BccInvalid} && $Param{Errors} && !$Param{Errors}->{BccErrorType} ) { 2910 $LayoutObject->Block( 2911 Name => 'BccServerErrorMsg', 2912 ); 2913 } 2914 if ( $Param{Errors}->{BccErrorType} || !$ShowErrors ) { 2915 $Param{BccInvalid} = ''; 2916 } 2917 2918 my $DynamicFieldNames = $Self->_GetFieldsToUpdate( 2919 OnlyDynamicFields => 1 2920 ); 2921 2922 $LayoutObject->AddJSData( 2923 Key => 'DynamicFieldNames', 2924 Value => $DynamicFieldNames, 2925 ); 2926 2927 # build type string 2928 if ( $ConfigObject->Get('Ticket::Type') ) { 2929 $Param{TypeStrg} = $LayoutObject->BuildSelection( 2930 Data => $Param{Types}, 2931 Name => 'TypeID', 2932 Class => 'Validate_Required Modernize ' . ( $Param{Errors}->{TypeInvalid} || ' ' ), 2933 SelectedID => $Param{TypeID}, 2934 PossibleNone => 1, 2935 Sort => 'AlphanumericValue', 2936 Translation => 0, 2937 ); 2938 $LayoutObject->Block( 2939 Name => 'TicketType', 2940 Data => {%Param}, 2941 ); 2942 } 2943 2944 # build service string 2945 if ( $ConfigObject->Get('Ticket::Service') ) { 2946 2947 $Param{ServiceStrg} = $LayoutObject->BuildSelection( 2948 Data => $Param{Services}, 2949 Name => 'ServiceID', 2950 Class => 'Modernize ' 2951 . ( $Config->{ServiceMandatory} ? 'Validate_Required ' : '' ) 2952 . ( $Param{Errors}->{ServiceInvalid} || '' ), 2953 SelectedID => $Param{ServiceID}, 2954 PossibleNone => 1, 2955 TreeView => $TreeView, 2956 Sort => 'TreeView', 2957 Translation => 0, 2958 Max => 200, 2959 ); 2960 $LayoutObject->Block( 2961 Name => 'TicketService', 2962 Data => { 2963 ServiceMandatory => $Config->{ServiceMandatory} || 0, 2964 %Param, 2965 }, 2966 ); 2967 2968 $Param{SLAStrg} = $LayoutObject->BuildSelection( 2969 Data => $Param{SLAs}, 2970 Name => 'SLAID', 2971 SelectedID => $Param{SLAID}, 2972 Class => 'Modernize ' 2973 . ( $Config->{SLAMandatory} ? 'Validate_Required ' : '' ) 2974 . ( $Param{Errors}->{SLAInvalid} || '' ), 2975 PossibleNone => 1, 2976 Sort => 'AlphanumericValue', 2977 Translation => 0, 2978 Max => 200, 2979 ); 2980 $LayoutObject->Block( 2981 Name => 'TicketSLA', 2982 Data => { 2983 SLAMandatory => $Config->{SLAMandatory} || 0, 2984 %Param, 2985 }, 2986 ); 2987 } 2988 2989 # check if exists create templates regardless the queue 2990 my %StandardTemplates = $Kernel::OM->Get('Kernel::System::StandardTemplate')->StandardTemplateList( 2991 Valid => 1, 2992 Type => 'Create', 2993 ); 2994 2995 # build text template string 2996 if ( IsHashRefWithData( \%StandardTemplates ) ) { 2997 $Param{StandardTemplateStrg} = $LayoutObject->BuildSelection( 2998 Data => $Param{StandardTemplates} || {}, 2999 Name => 'StandardTemplateID', 3000 SelectedID => $Param{StandardTemplateID} || '', 3001 Class => 'Modernize', 3002 PossibleNone => 1, 3003 Sort => 'AlphanumericValue', 3004 Translation => 1, 3005 Max => 200, 3006 ); 3007 $LayoutObject->Block( 3008 Name => 'StandardTemplate', 3009 Data => {%Param}, 3010 ); 3011 } 3012 3013 # build priority string 3014 if ( !$Param{PriorityID} ) { 3015 $Param{Priority} = $Config->{Priority}; 3016 } 3017 $Param{PriorityStrg} = $LayoutObject->BuildSelection( 3018 Data => $Param{Priorities}, 3019 Name => 'PriorityID', 3020 SelectedID => $Param{PriorityID}, 3021 Class => 'Modernize', 3022 SelectedValue => $Param{Priority}, 3023 Translation => 1, 3024 ); 3025 3026 # pending data string 3027 $Param{PendingDateString} = $LayoutObject->BuildDateSelection( 3028 %Param, 3029 Format => 'DateInputFormatLong', 3030 YearPeriodPast => 0, 3031 YearPeriodFuture => 5, 3032 DiffTime => $ConfigObject->Get('Ticket::Frontend::PendingDiffTime') || 0, 3033 Class => $Param{Errors}->{DateInvalid} || ' ', 3034 Validate => 1, 3035 ValidateDateInFuture => 1, 3036 ); 3037 3038 # show owner selection 3039 if ( $ConfigObject->Get('Ticket::Frontend::NewOwnerSelection') ) { 3040 $LayoutObject->Block( 3041 Name => 'OwnerSelection', 3042 Data => \%Param, 3043 ); 3044 } 3045 3046 # show responsible selection 3047 if ( 3048 $ConfigObject->Get('Ticket::Responsible') 3049 && $ConfigObject->Get('Ticket::Frontend::NewResponsibleSelection') 3050 ) 3051 { 3052 $Param{ResponsibleUsers}->{''} = '-'; 3053 $Param{ResponsibleOptionStrg} = $LayoutObject->BuildSelection( 3054 Data => $Param{ResponsibleUsers}, 3055 SelectedID => $Param{ResponsibleUserSelected}, 3056 Name => 'NewResponsibleID', 3057 Class => 'Modernize', 3058 ); 3059 $LayoutObject->Block( 3060 Name => 'ResponsibleSelection', 3061 Data => \%Param, 3062 ); 3063 } 3064 3065 # Dynamic fields 3066 # cycle through the activated Dynamic Fields for this screen 3067 DYNAMICFIELD: 3068 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 3069 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 3070 3071 # skip fields that HTML could not be retrieved 3072 next DYNAMICFIELD if !IsHashRefWithData( 3073 $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} } 3074 ); 3075 3076 # get the html strings form $Param 3077 my $DynamicFieldHTML = $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} }; 3078 3079 $LayoutObject->Block( 3080 Name => 'DynamicField', 3081 Data => { 3082 Name => $DynamicFieldConfig->{Name}, 3083 Label => $DynamicFieldHTML->{Label}, 3084 Field => $DynamicFieldHTML->{Field}, 3085 }, 3086 ); 3087 3088 # example of dynamic fields order customization 3089 $LayoutObject->Block( 3090 Name => 'DynamicField_' . $DynamicFieldConfig->{Name}, 3091 Data => { 3092 Name => $DynamicFieldConfig->{Name}, 3093 Label => $DynamicFieldHTML->{Label}, 3094 Field => $DynamicFieldHTML->{Field}, 3095 }, 3096 ); 3097 } 3098 3099 # show time accounting box 3100 if ( $ConfigObject->Get('Ticket::Frontend::AccountTime') ) { 3101 if ( $ConfigObject->Get('Ticket::Frontend::NeedAccountedTime') ) { 3102 $LayoutObject->Block( 3103 Name => 'TimeUnitsLabelMandatory', 3104 Data => \%Param, 3105 ); 3106 } 3107 else { 3108 $LayoutObject->Block( 3109 Name => 'TimeUnitsLabel', 3110 Data => \%Param, 3111 ); 3112 } 3113 $LayoutObject->Block( 3114 Name => 'TimeUnits', 3115 Data => \%Param, 3116 ); 3117 } 3118 3119 # Show the customer user address book if the module is registered and java script support is available. 3120 if ( 3121 $ConfigObject->Get('Frontend::Module')->{AgentCustomerUserAddressBook} 3122 && $LayoutObject->{BrowserJavaScriptSupport} 3123 ) 3124 { 3125 $Param{OptionCustomerUserAddressBook} = 1; 3126 } 3127 3128 # show customer edit link 3129 my $OptionCustomer = $LayoutObject->Permission( 3130 Action => 'AdminCustomerUser', 3131 Type => 'rw', 3132 ); 3133 3134 my $ShownOptionsBlock; 3135 3136 if ($OptionCustomer) { 3137 3138 # check if need to call Options block 3139 if ( !$ShownOptionsBlock ) { 3140 $LayoutObject->Block( 3141 Name => 'TicketOptions', 3142 Data => { 3143 %Param, 3144 }, 3145 ); 3146 3147 # set flag to "true" in order to prevent calling the Options block again 3148 $ShownOptionsBlock = 1; 3149 } 3150 3151 $LayoutObject->Block( 3152 Name => 'OptionCustomer', 3153 Data => { 3154 %Param, 3155 }, 3156 ); 3157 } 3158 3159 # show attachments 3160 ATTACHMENT: 3161 for my $Attachment ( @{ $Param{Attachments} } ) { 3162 if ( 3163 $Attachment->{ContentID} 3164 && $LayoutObject->{BrowserRichText} 3165 && ( $Attachment->{ContentType} =~ /image/i ) 3166 && ( $Attachment->{Disposition} eq 'inline' ) 3167 ) 3168 { 3169 next ATTACHMENT; 3170 } 3171 3172 push @{ $Param{AttachmentList} }, $Attachment; 3173 } 3174 3175 # add rich text editor 3176 if ( $LayoutObject->{BrowserRichText} ) { 3177 3178 # use height/width defined for this screen 3179 $Param{RichTextHeight} = $Config->{RichTextHeight} || 0; 3180 $Param{RichTextWidth} = $Config->{RichTextWidth} || 0; 3181 3182 # set up rich text editor 3183 $LayoutObject->SetRichTextParameters( 3184 Data => \%Param, 3185 ); 3186 } 3187 3188 # get output back 3189 return $LayoutObject->Output( 3190 TemplateFile => 'AgentTicketEmail', 3191 Data => \%Param 3192 ); 3193} 3194 3195sub _GetFieldsToUpdate { 3196 my ( $Self, %Param ) = @_; 3197 3198 my @UpdatableFields; 3199 3200 # set the fields that can be updateable via AJAXUpdate 3201 if ( !$Param{OnlyDynamicFields} ) { 3202 @UpdatableFields = qw( 3203 TypeID Dest NextStateID PriorityID ServiceID SLAID SignKeyID CryptKeyID To Cc Bcc 3204 StandardTemplateID 3205 ); 3206 } 3207 3208 # cycle through the activated Dynamic Fields for this screen 3209 DYNAMICFIELD: 3210 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 3211 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 3212 3213 my $IsACLReducible = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior( 3214 DynamicFieldConfig => $DynamicFieldConfig, 3215 Behavior => 'IsACLReducible', 3216 ); 3217 next DYNAMICFIELD if !$IsACLReducible; 3218 3219 push @UpdatableFields, 'DynamicField_' . $DynamicFieldConfig->{Name}; 3220 } 3221 3222 return \@UpdatableFields; 3223} 3224 32251; 3226