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::AdminGenericAgent; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15use Kernel::Language qw(Translatable); 16 17our $ObjectManagerDisabled = 1; 18 19sub new { 20 my ( $Type, %Param ) = @_; 21 22 # allocate new hash for object 23 my $Self = {%Param}; 24 bless( $Self, $Type ); 25 26 # get the dynamic fields for ticket object 27 $Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 28 Valid => 1, 29 ObjectType => ['Ticket'], 30 ); 31 32 return $Self; 33} 34 35sub Run { 36 my ( $Self, %Param ) = @_; 37 38 # get layout object 39 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 40 41 # secure mode message (don't allow this action till secure mode is enabled) 42 if ( !$Kernel::OM->Get('Kernel::Config')->Get('SecureMode') ) { 43 return $LayoutObject->SecureMode(); 44 } 45 46 # get param object 47 my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 48 49 # get config data 50 $Self->{Profile} = $ParamObject->GetParam( Param => 'Profile' ) || ''; 51 $Self->{OldProfile} = $ParamObject->GetParam( Param => 'OldProfile' ) || ''; 52 $Self->{Subaction} = $ParamObject->GetParam( Param => 'Subaction' ) || ''; 53 54 # get needed objects 55 my $CheckItemObject = $Kernel::OM->Get('Kernel::System::CheckItem'); 56 my $GenericAgentObject = $Kernel::OM->Get('Kernel::System::GenericAgent'); 57 58 # ---------------------------------------------------------- # 59 # run a generic agent job -> "run now" 60 # ---------------------------------------------------------- # 61 if ( $Self->{Subaction} eq 'RunNow' ) { 62 63 # challenge token check for write action 64 $LayoutObject->ChallengeTokenCheck(); 65 66 my $Run = $GenericAgentObject->JobRun( 67 Job => $Self->{Profile}, 68 UserID => 1, 69 ); 70 71 # redirect 72 if ($Run) { 73 return $LayoutObject->Redirect( 74 OP => "Action=$Self->{Action}", 75 ); 76 } 77 78 # redirect 79 return $LayoutObject->ErrorScreen(); 80 } 81 82 if ( $Self->{Subaction} eq 'Run' ) { 83 84 return $Self->_MaskRun(); 85 } 86 87 # --------------------------------------------------------------- # 88 # save generic agent job and show a view of all affected tickets 89 # --------------------------------------------------------------- # 90 # show result site 91 if ( $Self->{Subaction} eq 'UpdateAction' ) { 92 93 # challenge token check for write action 94 $LayoutObject->ChallengeTokenCheck(); 95 96 my ( %GetParam, %Errors ); 97 98 # challenge token check for write action 99 $LayoutObject->ChallengeTokenCheck(); 100 101 # get single params 102 for my $Parameter ( 103 qw(TicketNumber Title MIMEBase_From MIMEBase_To MIMEBase_Cc MIMEBase_Subject MIMEBase_Body CustomerID 104 CustomerUserLogin Agent SearchInArchive 105 NewTitle 106 NewCustomerID NewPendingTime NewPendingTimeType NewCustomerUserLogin 107 NewStateID NewQueueID NewPriorityID NewOwnerID NewResponsibleID 108 NewTypeID NewServiceID NewSLAID 109 NewNoteFrom NewNoteSubject NewNoteBody NewNoteIsVisibleForCustomer NewNoteTimeUnits NewModule 110 NewParamKey1 NewParamKey2 NewParamKey3 NewParamKey4 111 NewParamValue1 NewParamValue2 NewParamValue3 NewParamValue4 112 NewParamKey5 NewParamKey6 NewParamKey7 NewParamKey8 113 NewParamValue5 NewParamValue6 NewParamValue7 NewParamValue8 114 NewLockID NewDelete NewCMD NewSendNoNotification NewArchiveFlag 115 ScheduleLastRun Valid 116 ) 117 ) 118 { 119 $GetParam{$Parameter} = $ParamObject->GetParam( Param => $Parameter ); 120 121 # remove leading and trailing blank spaces 122 if ( $GetParam{$Parameter} ) { 123 $CheckItemObject->StringClean( 124 StringRef => \$GetParam{$Parameter}, 125 ); 126 } 127 } 128 129 for my $Type ( 130 qw(Time ChangeTime CloseTime LastChangeTime LastCloseTime TimePending EscalationTime EscalationResponseTime EscalationUpdateTime EscalationSolutionTime) 131 ) 132 { 133 my $Key = $Type . 'SearchType'; 134 $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); 135 } 136 for my $Type ( 137 qw( 138 TicketCreate TicketChange 139 TicketClose TicketLastChange 140 TicketLastClose 141 TicketPending 142 TicketEscalation TicketEscalationResponse 143 TicketEscalationUpdate TicketEscalationSolution 144 ) 145 ) 146 { 147 for my $Attribute ( 148 qw( 149 TimePoint TimePointFormat TimePointStart 150 TimeStart TimeStartDay TimeStartMonth TimeStopMonth 151 TimeStop TimeStopDay TimeStopYear TimeStartYear 152 ) 153 ) 154 { 155 my $Key = $Type . $Attribute; 156 $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); 157 } 158 159 # validate data 160 for my $Attribute ( 161 qw(TimeStartDay TimeStartMonth TimeStopMonth TimeStopDay) 162 ) 163 { 164 my $Key = $Type . $Attribute; 165 166 if ( $GetParam{$Key} ) { 167 $GetParam{$Key} = sprintf( '%02d', $GetParam{$Key} ); 168 } 169 } 170 } 171 172 # get dynamic fields to set from web request 173 # to store dynamic fields profile data 174 my %DynamicFieldValues; 175 176 # get dynamic field backend object 177 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 178 179 # cycle trough the activated Dynamic Fields for this screen 180 DYNAMICFIELD: 181 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 182 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 183 184 # extract the dynamic field value from the web request 185 my $DynamicFieldValue = $DynamicFieldBackendObject->EditFieldValueGet( 186 DynamicFieldConfig => $DynamicFieldConfig, 187 ParamObject => $ParamObject, 188 LayoutObject => $LayoutObject, 189 ReturnTemplateStructure => 1, 190 ); 191 192 # set the complete value structure in GetParam to store it later in the Generic Agent Job 193 if ( IsHashRefWithData($DynamicFieldValue) ) { 194 %DynamicFieldValues = ( %DynamicFieldValues, %{$DynamicFieldValue} ); 195 } 196 } 197 198 # get array params 199 for my $Parameter ( 200 qw(LockIDs StateIDs StateTypeIDs QueueIDs PriorityIDs OwnerIDs ResponsibleIDs 201 TypeIDs ServiceIDs SLAIDs 202 ScheduleDays ScheduleMinutes ScheduleHours 203 EventValues 204 ) 205 ) 206 { 207 208 # get search array params (get submitted params) 209 if ( $ParamObject->GetArray( Param => $Parameter ) ) { 210 @{ $GetParam{$Parameter} } = $ParamObject->GetArray( Param => $Parameter ); 211 } 212 } 213 214 # get Dynamic fields for search from web request 215 # cycle trough the activated Dynamic Fields for this screen 216 DYNAMICFIELD: 217 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 218 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 219 220 # get search field preferences 221 my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( 222 DynamicFieldConfig => $DynamicFieldConfig, 223 ); 224 225 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 226 227 PREFERENCE: 228 for my $Preference ( @{$SearchFieldPreferences} ) { 229 230 # extract the dynamic field value from the web request 231 my $DynamicFieldValue = $DynamicFieldBackendObject->SearchFieldValueGet( 232 DynamicFieldConfig => $DynamicFieldConfig, 233 ParamObject => $ParamObject, 234 ReturnProfileStructure => 1, 235 LayoutObject => $LayoutObject, 236 Type => $Preference->{Type}, 237 ); 238 239 # set the complete value structure in %DynamicFieldValues to store it later in the 240 # Generic Agent Job 241 if ( IsHashRefWithData($DynamicFieldValue) ) { 242 %DynamicFieldValues = ( %DynamicFieldValues, %{$DynamicFieldValue} ); 243 } 244 } 245 } 246 247 # check needed data 248 if ( !$Self->{Profile} ) { 249 $Errors{ProfileInvalid} = 'ServerError'; 250 } 251 252 # Check length of fields from Add Note section. 253 if ( length $GetParam{NewNoteFrom} > 200 ) { 254 $Errors{NewNoteFromServerError} = 'ServerError'; 255 } 256 if ( length $GetParam{NewNoteSubject} > 200 ) { 257 $Errors{NewNoteSubjectServerError} = 'ServerError'; 258 } 259 if ( length $GetParam{NewNoteBody} > 200 ) { 260 $Errors{NewNoteBodyServerError} = 'ServerError'; 261 } 262 263 # Check if ticket selection contains stop words 264 my %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet( 265 MIMEBase_From => $GetParam{MIMEBase_From}, 266 MIMEBase_To => $GetParam{MIMEBase_To}, 267 MIMEBase_Cc => $GetParam{MIMEBase_Cc}, 268 MIMEBase_Subject => $GetParam{MIMEBase_Subject}, 269 MIMEBase_Body => $GetParam{MIMEBase_Body}, 270 ); 271 %Errors = ( %Errors, %StopWordsServerErrors ); 272 273 # if no errors occurred 274 if ( !%Errors ) { 275 276 if ( $Self->{OldProfile} ) { 277 278 # remove/clean up old profile stuff 279 $GenericAgentObject->JobDelete( 280 Name => $Self->{OldProfile}, 281 UserID => $Self->{UserID}, 282 ); 283 } 284 285 # insert new profile params 286 my $JobAddResult = $GenericAgentObject->JobAdd( 287 Name => $Self->{Profile}, 288 Data => { 289 %GetParam, 290 %DynamicFieldValues, 291 }, 292 UserID => $Self->{UserID}, 293 ); 294 295 if ($JobAddResult) { 296 297 # if the user would like to continue editing the generic agent job, just redirect to the edit screen 298 if ( 299 defined $ParamObject->GetParam( Param => 'ContinueAfterSave' ) 300 && ( $ParamObject->GetParam( Param => 'ContinueAfterSave' ) eq '1' ) 301 ) 302 { 303 my $Profile = $Self->{Profile} || ''; 304 return $LayoutObject->Redirect( OP => "Action=$Self->{Action};Subaction=Update;Profile=$Profile" ); 305 } 306 else { 307 308 # otherwise return to overview 309 return $LayoutObject->Redirect( OP => "Action=$Self->{Action}" ); 310 } 311 312 } 313 else { 314 $Errors{ProfileInvalid} = 'ServerError'; 315 $Errors{ProfileInvalidMsg} = 'AddError'; 316 } 317 } 318 319 # something went wrong 320 my $JobDataReference; 321 $JobDataReference = $Self->_MaskUpdate( 322 %Param, 323 %GetParam, 324 %DynamicFieldValues, 325 %Errors, 326 StopWordsAlreadyChecked => 1, 327 ); 328 329 # generate search mask 330 my $Output = $LayoutObject->Header( 331 Title => Translatable('Edit'), 332 ); 333 $Output .= $LayoutObject->NavigationBar(); 334 $Output .= $LayoutObject->Output( 335 TemplateFile => 'AdminGenericAgent', 336 Data => $JobDataReference, 337 ); 338 $Output .= $LayoutObject->Footer(); 339 return $Output; 340 } 341 342 # ---------------------------------------------------------- # 343 # edit generic agent job 344 # ---------------------------------------------------------- # 345 if ( $Self->{Subaction} eq 'Update' ) { 346 my $JobDataReference; 347 $JobDataReference = $Self->_MaskUpdate(%Param); 348 349 # generate search mask 350 my $Output = $LayoutObject->Header( 351 Title => Translatable('Edit'), 352 ); 353 354 $Output .= $LayoutObject->NavigationBar(); 355 $Output .= $LayoutObject->Output( 356 TemplateFile => 'AdminGenericAgent', 357 Data => $JobDataReference, 358 ); 359 $Output .= $LayoutObject->Footer(); 360 return $Output; 361 } 362 363 # ---------------------------------------------------------- # 364 # Update dynamic fields for generic agent job by AJAX 365 # ---------------------------------------------------------- # 366 if ( $Self->{Subaction} eq 'AddDynamicField' ) { 367 my $DynamicFieldID = $ParamObject->GetParam( Param => 'DynamicFieldID' ); 368 my $Type = $ParamObject->GetParam( Param => 'Type' ) || ''; 369 my $SelectedValue = $ParamObject->GetParam( Param => 'SelectedValue' ); 370 my $Widget = $ParamObject->GetParam( Param => 'Widget' ); 371 372 my $DynamicFieldConfig = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldGet( 373 ID => $DynamicFieldID, 374 ); 375 376 my %JobData; 377 if ( $Self->{Profile} ) { 378 %JobData = $Kernel::OM->Get('Kernel::System::GenericAgent')->JobGet( 379 Name => $Self->{Profile}, 380 ); 381 } 382 $JobData{Profile} = $Self->{Profile}; 383 $JobData{Subaction} = $Self->{Subaction}; 384 385 my $DynamicFieldHTML; 386 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 387 388 # Get field HTML. 389 if ( $Widget eq 'Select' ) { 390 $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( 391 DynamicFieldConfig => $DynamicFieldConfig, 392 Profile => \%JobData, 393 LayoutObject => $LayoutObject, 394 ConfirmationCheckboxes => 1, 395 Type => $Type, 396 ); 397 } 398 elsif ( $Widget eq 'Update' ) { 399 my $PossibleValuesFilter; 400 401 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 402 DynamicFieldConfig => $DynamicFieldConfig, 403 Behavior => 'IsACLReducible', 404 ); 405 406 if ($IsACLReducible) { 407 408 # Get PossibleValues. 409 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 410 DynamicFieldConfig => $DynamicFieldConfig, 411 ); 412 413 # Check if field has PossibleValues property in its configuration. 414 if ( IsHashRefWithData($PossibleValues) ) { 415 416 # Convert possible values key => value to key => key for ACLs using a Hash slice. 417 my %AclData = %{$PossibleValues}; 418 @AclData{ keys %AclData } = keys %AclData; 419 420 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 421 422 # Set possible values filter from ACLs. 423 my $ACL = $TicketObject->TicketAcl( 424 Action => $Self->{Action}, 425 Type => 'DynamicField_' . $DynamicFieldConfig->{Name}, 426 ReturnType => 'Ticket', 427 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 428 Data => \%AclData, 429 UserID => $Self->{UserID}, 430 ); 431 if ($ACL) { 432 my %Filter = $TicketObject->TicketAclData(); 433 434 # Convert Filer key => key back to key => value using map. 435 %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } 436 keys %Filter; 437 } 438 } 439 } 440 $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender( 441 DynamicFieldConfig => $DynamicFieldConfig, 442 PossibleValuesFilter => $PossibleValuesFilter, 443 LayoutObject => $LayoutObject, 444 ParamObject => $ParamObject, 445 UseDefaultValue => 0, 446 OverridePossibleNone => 1, 447 ConfirmationNeeded => 1, 448 NoIgnoreField => 1, 449 Template => \%JobData, 450 MaxLength => 200, 451 ); 452 } 453 454 $DynamicFieldHTML->{ID} = $SelectedValue; 455 456 my $Output = $LayoutObject->JSONEncode( 457 Data => $DynamicFieldHTML, 458 ); 459 460 # Send JSON response. 461 return $LayoutObject->Attachment( 462 ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, 463 Content => $Output, 464 Type => 'inline', 465 NoCache => 1, 466 ); 467 } 468 469 # ---------------------------------------------------------- # 470 # delete an generic agent job 471 # ---------------------------------------------------------- # 472 if ( $Self->{Subaction} eq 'Delete' && $Self->{Profile} ) { 473 474 # challenge token check for write action 475 $LayoutObject->ChallengeTokenCheck(); 476 477 if ( $Self->{Profile} ) { 478 $GenericAgentObject->JobDelete( 479 Name => $Self->{Profile}, 480 UserID => $Self->{UserID}, 481 ); 482 } 483 } 484 485 # ---------------------------------------------------------- # 486 # overview of all generic agent jobs 487 # ---------------------------------------------------------- # 488 $LayoutObject->Block( 489 Name => 'ActionList', 490 ); 491 $LayoutObject->Block( 492 Name => 'ActionAdd', 493 ); 494 $LayoutObject->Block( 495 Name => 'Filter', 496 ); 497 $LayoutObject->Block( 498 Name => 'Overview', 499 ); 500 501 my %Jobs = $GenericAgentObject->JobList(); 502 503 # if there are any data, it is shown 504 if (%Jobs) { 505 my $Counter = 1; 506 for my $JobKey ( sort keys %Jobs ) { 507 my %JobData = $GenericAgentObject->JobGet( Name => $JobKey ); 508 509 # css setting and text for valid or invalid jobs 510 $JobData{ShownValid} = $JobData{Valid} ? 'valid' : 'invalid'; 511 512 # separate each search result line by using several css 513 $LayoutObject->Block( 514 Name => 'Row', 515 Data => {%JobData}, 516 ); 517 } 518 } 519 520 # otherwise a no data found message is displayed 521 else { 522 $LayoutObject->Block( 523 Name => 'NoDataFoundMsg', 524 Data => {}, 525 ); 526 } 527 528 # generate search mask 529 my $Output = $LayoutObject->Header(); 530 $Output .= $LayoutObject->NavigationBar(); 531 $Output .= $LayoutObject->Output( 532 TemplateFile => 'AdminGenericAgent', 533 Data => \%Param, 534 ); 535 $Output .= $LayoutObject->Footer(); 536 return $Output; 537} 538 539sub _MaskUpdate { 540 my ( $Self, %Param ) = @_; 541 542 my %JobData; 543 544 if ( $Self->{Profile} ) { 545 546 # get db job data 547 %JobData = $Kernel::OM->Get('Kernel::System::GenericAgent')->JobGet( 548 Name => $Self->{Profile}, 549 ); 550 } 551 $JobData{Profile} = $Self->{Profile}; 552 $JobData{Subaction} = $Self->{Subaction}; 553 554 # get config object 555 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 556 557 # get list type 558 my $TreeView = 0; 559 if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { 560 $TreeView = 1; 561 } 562 563 my %ShownUsers = $Kernel::OM->Get('Kernel::System::User')->UserList( 564 Type => 'Long', 565 Valid => 1, 566 ); 567 568 # get layout object 569 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 570 571 $JobData{OwnerStrg} = $LayoutObject->BuildSelection( 572 Data => \%ShownUsers, 573 Name => 'OwnerIDs', 574 Multiple => 1, 575 Size => 5, 576 Translation => 0, 577 SelectedID => $JobData{OwnerIDs}, 578 Class => 'Modernize', 579 ); 580 $JobData{NewOwnerStrg} = $LayoutObject->BuildSelection( 581 Data => \%ShownUsers, 582 Name => 'NewOwnerID', 583 Size => 5, 584 Multiple => 0, 585 Translation => 0, 586 SelectedID => $JobData{NewOwnerID}, 587 Class => 'Modernize', 588 ); 589 my %Hours; 590 for my $Number ( 0 .. 23 ) { 591 $Hours{$Number} = sprintf( "%02d", $Number ); 592 } 593 $JobData{ScheduleHoursList} = $LayoutObject->BuildSelection( 594 Data => \%Hours, 595 Name => 'ScheduleHours', 596 Size => 6, 597 Multiple => 1, 598 Translation => 0, 599 SelectedID => $JobData{ScheduleHours}, 600 Class => 'Modernize', 601 ); 602 my %Minutes; 603 for my $Number ( 0 .. 59 ) { 604 $Minutes{$Number} = sprintf( "%02d", $Number ); 605 } 606 $JobData{ScheduleMinutesList} = $LayoutObject->BuildSelection( 607 Data => \%Minutes, 608 Name => 'ScheduleMinutes', 609 Size => 6, 610 Multiple => 1, 611 Translation => 0, 612 SelectedID => $JobData{ScheduleMinutes}, 613 Class => 'Modernize', 614 ); 615 $JobData{ScheduleDaysList} = $LayoutObject->BuildSelection( 616 Data => { 617 1 => Translatable('Mon'), 618 2 => Translatable('Tue'), 619 3 => Translatable('Wed'), 620 4 => Translatable('Thu'), 621 5 => Translatable('Fri'), 622 6 => Translatable('Sat'), 623 0 => Translatable('Sun'), 624 }, 625 Sort => 'NumericKey', 626 Name => 'ScheduleDays', 627 Size => 7, 628 Multiple => 1, 629 SelectedID => $JobData{ScheduleDays}, 630 Class => 'Modernize', 631 ); 632 633 # get state object 634 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 635 636 $JobData{StatesStrg} = $LayoutObject->BuildSelection( 637 Data => { 638 $StateObject->StateList( 639 UserID => 1, 640 Action => $Self->{Action}, 641 ), 642 }, 643 Name => 'StateIDs', 644 Multiple => 1, 645 Size => 5, 646 SelectedID => $JobData{StateIDs}, 647 Class => 'Modernize', 648 ); 649 $JobData{NewStatesStrg} = $LayoutObject->BuildSelection( 650 Data => { 651 $StateObject->StateList( 652 UserID => 1, 653 Action => $Self->{Action}, 654 ), 655 }, 656 Name => 'NewStateID', 657 Size => 5, 658 Multiple => 0, 659 SelectedID => $JobData{NewStateID}, 660 Class => 'Modernize', 661 ); 662 $JobData{NewPendingTimeTypeStrg} = $LayoutObject->BuildSelection( 663 Data => [ 664 { 665 Key => 60, 666 Value => Translatable('minute(s)'), 667 }, 668 { 669 Key => 3600, 670 Value => Translatable('hour(s)'), 671 }, 672 { 673 Key => 86400, 674 Value => Translatable('day(s)'), 675 }, 676 { 677 Key => 2592000, 678 Value => Translatable('month(s)'), 679 }, 680 { 681 Key => 31536000, 682 Value => Translatable('year(s)'), 683 }, 684 685 ], 686 Name => 'NewPendingTimeType', 687 Size => 1, 688 Multiple => 0, 689 SelectedID => $JobData{NewPendingTimeType}, 690 Translation => 1, 691 Title => $LayoutObject->{LanguageObject}->Translate('Time unit'), 692 Class => 'Modernize', 693 ); 694 695 # get queue object 696 my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue'); 697 698 $JobData{QueuesStrg} = $LayoutObject->AgentQueueListOption( 699 Data => { $QueueObject->GetAllQueues(), }, 700 Size => 5, 701 Multiple => 1, 702 Name => 'QueueIDs', 703 SelectedIDRefArray => $JobData{QueueIDs}, 704 TreeView => $TreeView, 705 OnChangeSubmit => 0, 706 Class => 'Modernize', 707 ); 708 $JobData{NewQueuesStrg} = $LayoutObject->AgentQueueListOption( 709 Data => { $QueueObject->GetAllQueues(), }, 710 Size => 5, 711 Multiple => 0, 712 Name => 'NewQueueID', 713 SelectedID => $JobData{NewQueueID}, 714 TreeView => $TreeView, 715 OnChangeSubmit => 0, 716 Class => 'Modernize', 717 ); 718 719 # get priority object 720 my $PriorityObject = $Kernel::OM->Get('Kernel::System::Priority'); 721 722 $JobData{PrioritiesStrg} = $LayoutObject->BuildSelection( 723 Data => { 724 $PriorityObject->PriorityList( 725 UserID => 1, 726 Action => $Self->{Action}, 727 ), 728 }, 729 Name => 'PriorityIDs', 730 Size => 5, 731 Multiple => 1, 732 SelectedID => $JobData{PriorityIDs}, 733 Class => 'Modernize', 734 ); 735 $JobData{NewPrioritiesStrg} = $LayoutObject->BuildSelection( 736 Data => { 737 $PriorityObject->PriorityList( 738 UserID => 1, 739 Action => $Self->{Action}, 740 ), 741 }, 742 Name => 'NewPriorityID', 743 Size => 5, 744 Multiple => 0, 745 SelectedID => $JobData{NewPriorityID}, 746 Class => 'Modernize', 747 ); 748 749 # get time option 750 my %Map = ( 751 TicketCreate => 'Time', 752 TicketChange => 'ChangeTime', 753 TicketClose => 'CloseTime', 754 TicketLastChange => 'LastChangeTime', 755 TicketLastClose => 'LastCloseTime', 756 TicketPending => 'TimePending', 757 TicketEscalation => 'EscalationTime', 758 TicketEscalationResponse => 'EscalationResponseTime', 759 TicketEscalationUpdate => 'EscalationUpdateTime', 760 TicketEscalationSolution => 'EscalationSolutionTime', 761 ); 762 for my $Type ( 763 qw( 764 TicketCreate TicketClose 765 TicketChange TicketLastChange 766 TicketLastClose 767 TicketPending 768 TicketEscalation TicketEscalationResponse 769 TicketEscalationUpdate TicketEscalationSolution 770 ) 771 ) 772 { 773 my $SearchType = $Map{$Type} . 'SearchType'; 774 if ( !$JobData{$SearchType} ) { 775 $JobData{ $SearchType . '::None' } = 'checked="checked"'; 776 } 777 elsif ( $JobData{$SearchType} eq 'TimePoint' ) { 778 $JobData{ $SearchType . '::TimePoint' } = 'checked="checked"'; 779 } 780 elsif ( $JobData{$SearchType} eq 'TimeSlot' ) { 781 $JobData{ $SearchType . '::TimeSlot' } = 'checked="checked"'; 782 } 783 784 my %Counter; 785 for my $Number ( 1 .. 60 ) { 786 $Counter{$Number} = sprintf( "%02d", $Number ); 787 } 788 789 # time 790 $JobData{ $Type . 'TimePoint' } = $LayoutObject->BuildSelection( 791 Data => \%Counter, 792 Name => $Type . 'TimePoint', 793 SelectedID => $JobData{ $Type . 'TimePoint' }, 794 Translation => 0, 795 ); 796 $JobData{ $Type . 'TimePointStart' } = $LayoutObject->BuildSelection( 797 Data => { 798 Last => Translatable('within the last ...'), 799 Next => Translatable('within the next ...'), 800 Before => Translatable('more than ... ago'), 801 }, 802 Name => $Type . 'TimePointStart', 803 SelectedID => $JobData{ $Type . 'TimePointStart' } || 'Last', 804 ); 805 $JobData{ $Type . 'TimePointFormat' } = $LayoutObject->BuildSelection( 806 Data => { 807 minute => Translatable('minute(s)'), 808 hour => Translatable('hour(s)'), 809 day => Translatable('day(s)'), 810 week => Translatable('week(s)'), 811 month => Translatable('month(s)'), 812 year => Translatable('year(s)'), 813 }, 814 Name => $Type . 'TimePointFormat', 815 SelectedID => $JobData{ $Type . 'TimePointFormat' }, 816 ); 817 $JobData{ $Type . 'TimeStart' } = $LayoutObject->BuildDateSelection( 818 %JobData, 819 Prefix => $Type . 'TimeStart', 820 Format => 'DateInputFormat', 821 DiffTime => -( 60 * 60 * 24 ) * 30, 822 Validate => 1, 823 ); 824 $JobData{ $Type . 'TimeStop' } = $LayoutObject->BuildDateSelection( 825 %JobData, 826 Prefix => $Type . 'TimeStop', 827 Format => 'DateInputFormat', 828 Validate => 1, 829 ); 830 } 831 832 $JobData{DeleteOption} = $LayoutObject->BuildSelection( 833 Data => $ConfigObject->Get('YesNoOptions'), 834 Name => 'NewDelete', 835 SelectedID => $JobData{NewDelete} || 0, 836 Class => 'Modernize', 837 ); 838 $JobData{ValidOption} = $LayoutObject->BuildSelection( 839 Data => $ConfigObject->Get('YesNoOptions'), 840 Name => 'Valid', 841 SelectedID => defined( $JobData{Valid} ) ? $JobData{Valid} : 1, 842 Class => 'Modernize', 843 ); 844 845 # get lock object 846 my $LockObject = $Kernel::OM->Get('Kernel::System::Lock'); 847 848 $JobData{LockOption} = $LayoutObject->BuildSelection( 849 Data => { 850 $LockObject->LockList( 851 UserID => 1, 852 Action => $Self->{Action}, 853 ), 854 }, 855 Name => 'LockIDs', 856 Multiple => 1, 857 Size => 3, 858 SelectedID => $JobData{LockIDs}, 859 Class => 'Modernize', 860 ); 861 $JobData{NewLockOption} = $LayoutObject->BuildSelection( 862 Data => { 863 $LockObject->LockList( 864 UserID => 1, 865 Action => $Self->{Action}, 866 ), 867 }, 868 Name => 'NewLockID', 869 Size => 3, 870 Multiple => 0, 871 SelectedID => $JobData{NewLockID}, 872 Class => 'Modernize', 873 ); 874 875 # Show server errors if ticket selection contains stop words 876 my %StopWordsServerErrors; 877 if ( !$Param{StopWordsAlreadyChecked} ) { 878 %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet( 879 MIMEBase_From => $JobData{MIMEBase_From}, 880 MIMEBase_To => $JobData{MIMEBase_To}, 881 MIMEBase_Cc => $JobData{MIMEBase_Cc}, 882 MIMEBase_Subject => $JobData{MIMEBase_Subject}, 883 MIMEBase_Body => $JobData{MIMEBase_Body}, 884 ); 885 } 886 887 # REMARK: we changed the wording "Send no notifications" to 888 # "Send agent/customer notifications on changes" in frontend. 889 # But the backend code is still the same (compatibility). 890 # Because of this case we changed 1=>'Yes' to 1=>'No' 891 $JobData{SendNoNotificationOption} = $LayoutObject->BuildSelection( 892 Data => { 893 '1' => Translatable('No'), 894 '0' => Translatable('Yes'), 895 }, 896 Name => 'NewSendNoNotification', 897 SelectedID => $JobData{NewSendNoNotification} || 0, 898 Class => 'Modernize', 899 ); 900 901 $JobData{AllowCustomScriptExecution} = $ConfigObject->Get('Ticket::GenericAgentAllowCustomScriptExecution') || 0; 902 $JobData{AllowCustomModuleExecution} = $ConfigObject->Get('Ticket::GenericAgentAllowCustomModuleExecution') || 0; 903 904 $LayoutObject->Block( 905 Name => 'ActionList', 906 ); 907 $LayoutObject->Block( 908 Name => 'ActionOverview', 909 ); 910 $LayoutObject->Block( 911 Name => 'Edit', 912 Data => { 913 %JobData, 914 %Param, 915 %StopWordsServerErrors, 916 }, 917 ); 918 919 # check for profile errors 920 if ( defined $Param{ProfileInvalid} ) { 921 $Param{ProfileInvalidMsg} //= ''; 922 $LayoutObject->Block( 923 Name => 'ProfileInvalidMsg' . $Param{ProfileInvalidMsg}, 924 ); 925 } 926 927 # check if the schedule options are selected 928 if ( 929 !defined $JobData{ScheduleDays}->[0] 930 || !defined $JobData{ScheduleHours}->[0] 931 || !defined $JobData{ScheduleMinutes}->[0] 932 ) 933 { 934 $LayoutObject->Block( 935 Name => 'JobScheduleWarning', 936 ); 937 } 938 939 # build type string 940 if ( $ConfigObject->Get('Ticket::Type') ) { 941 my %Type = $Kernel::OM->Get('Kernel::System::Type')->TypeList( 942 UserID => $Self->{UserID}, 943 ); 944 $JobData{TypesStrg} = $LayoutObject->BuildSelection( 945 Data => \%Type, 946 Name => 'TypeIDs', 947 SelectedID => $JobData{TypeIDs}, 948 Sort => 'AlphanumericValue', 949 Size => 3, 950 Multiple => 1, 951 Translation => 0, 952 Class => 'Modernize', 953 ); 954 $LayoutObject->Block( 955 Name => 'TicketType', 956 Data => \%JobData, 957 ); 958 $JobData{NewTypesStrg} = $LayoutObject->BuildSelection( 959 Data => \%Type, 960 Name => 'NewTypeID', 961 SelectedID => $JobData{NewTypeID}, 962 Sort => 'AlphanumericValue', 963 Size => 3, 964 Multiple => 0, 965 Translation => 0, 966 Class => 'Modernize', 967 ); 968 $LayoutObject->Block( 969 Name => 'NewTicketType', 970 Data => \%JobData, 971 ); 972 } 973 974 # build service string 975 if ( $ConfigObject->Get('Ticket::Service') ) { 976 977 # get list type 978 my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList( 979 Valid => 1, 980 KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren') // 0, 981 UserID => $Self->{UserID}, 982 ); 983 my %NewService = %Service; 984 $JobData{ServicesStrg} = $LayoutObject->BuildSelection( 985 Data => \%Service, 986 Name => 'ServiceIDs', 987 SelectedID => $JobData{ServiceIDs}, 988 Size => 5, 989 Multiple => 1, 990 TreeView => $TreeView, 991 Translation => 0, 992 Max => 200, 993 Class => 'Modernize', 994 ); 995 $JobData{NewServicesStrg} = $LayoutObject->BuildSelection( 996 Data => \%NewService, 997 Name => 'NewServiceID', 998 SelectedID => $JobData{NewServiceID}, 999 Size => 5, 1000 Multiple => 0, 1001 TreeView => $TreeView, 1002 Translation => 0, 1003 Max => 200, 1004 Class => 'Modernize', 1005 ); 1006 my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList( 1007 UserID => $Self->{UserID}, 1008 ); 1009 $JobData{SLAsStrg} = $LayoutObject->BuildSelection( 1010 Data => \%SLA, 1011 Name => 'SLAIDs', 1012 SelectedID => $JobData{SLAIDs}, 1013 Sort => 'AlphanumericValue', 1014 Size => 5, 1015 Multiple => 1, 1016 Translation => 0, 1017 Max => 200, 1018 Class => 'Modernize', 1019 ); 1020 $JobData{NewSLAsStrg} = $LayoutObject->BuildSelection( 1021 Data => \%SLA, 1022 Name => 'NewSLAID', 1023 SelectedID => $JobData{NewSLAID}, 1024 Sort => 'AlphanumericValue', 1025 Size => 5, 1026 Multiple => 0, 1027 Translation => 0, 1028 Max => 200, 1029 Class => 'Modernize', 1030 ); 1031 $LayoutObject->Block( 1032 Name => 'TicketService', 1033 Data => {%JobData}, 1034 ); 1035 $LayoutObject->Block( 1036 Name => 'NewTicketService', 1037 Data => {%JobData}, 1038 ); 1039 } 1040 1041 # ticket responsible string 1042 if ( $ConfigObject->Get('Ticket::Responsible') ) { 1043 $JobData{ResponsibleStrg} = $LayoutObject->BuildSelection( 1044 Data => \%ShownUsers, 1045 Name => 'ResponsibleIDs', 1046 Size => 5, 1047 Multiple => 1, 1048 Translation => 0, 1049 SelectedID => $JobData{ResponsibleIDs}, 1050 Class => 'Modernize', 1051 ); 1052 $JobData{NewResponsibleStrg} = $LayoutObject->BuildSelection( 1053 Data => \%ShownUsers, 1054 Name => 'NewResponsibleID', 1055 Size => 5, 1056 Multiple => 0, 1057 Translation => 0, 1058 SelectedID => $JobData{NewResponsibleID}, 1059 Class => 'Modernize', 1060 ); 1061 $LayoutObject->Block( 1062 Name => 'TicketResponsible', 1063 Data => {%JobData}, 1064 ); 1065 $LayoutObject->Block( 1066 Name => 'NewTicketResponsible', 1067 Data => {%JobData}, 1068 ); 1069 } 1070 1071 # prepare archive 1072 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 1073 1074 $JobData{'SearchInArchiveStrg'} = $LayoutObject->BuildSelection( 1075 Data => { 1076 ArchivedTickets => Translatable('Archived tickets'), 1077 NotArchivedTickets => Translatable('Unarchived tickets'), 1078 AllTickets => Translatable('All tickets'), 1079 }, 1080 Name => 'SearchInArchive', 1081 SelectedID => $JobData{SearchInArchive} || 'AllTickets', 1082 Class => 'Modernize', 1083 ); 1084 1085 $LayoutObject->Block( 1086 Name => 'SearchInArchive', 1087 Data => {%JobData}, 1088 ); 1089 1090 $JobData{'NewArchiveFlagStrg'} = $LayoutObject->BuildSelection( 1091 Data => { 1092 'y' => Translatable('archive tickets'), 1093 'n' => Translatable('restore tickets from archive'), 1094 }, 1095 Name => 'NewArchiveFlag', 1096 PossibleNone => 1, 1097 SelectedID => $JobData{NewArchiveFlag} || '', 1098 Class => 'Modernize', 1099 ); 1100 1101 $LayoutObject->Block( 1102 Name => 'NewArchiveFlag', 1103 Data => {%JobData}, 1104 ); 1105 } 1106 1107 # create dynamic field HTML for set with historical data options 1108 my $PrintDynamicFieldsSearchHeader = 1; 1109 1110 # get dynamic field backend object 1111 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 1112 1113 my @AddDynamicFields; 1114 my %DynamicFieldsJS; 1115 1116 # cycle through the activated Dynamic Fields for this screen 1117 DYNAMICFIELD: 1118 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1119 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1120 1121 # get search field preferences 1122 my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( 1123 DynamicFieldConfig => $DynamicFieldConfig, 1124 ); 1125 1126 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1127 1128 # Translate dynamic field label. 1129 my $TranslatedDynamicFieldLabel = $LayoutObject->{LanguageObject}->Translate( 1130 $DynamicFieldConfig->{Label}, 1131 ); 1132 1133 PREFERENCE: 1134 for my $Preference ( @{$SearchFieldPreferences} ) { 1135 1136 # Translate the suffix. 1137 my $TranslatedSuffix = $LayoutObject->{LanguageObject}->Translate( 1138 $Preference->{LabelSuffix}, 1139 ) || ''; 1140 1141 if ($TranslatedSuffix) { 1142 $TranslatedSuffix = ' (' . $TranslatedSuffix . ')'; 1143 } 1144 1145 my $Key = 'Search_DynamicField_' . $DynamicFieldConfig->{Name} . $Preference->{Type}; 1146 my $Text = $TranslatedDynamicFieldLabel . $TranslatedSuffix; 1147 1148 # Save all dynamic fields for JS. 1149 $DynamicFieldsJS{$Key} = { 1150 ID => $DynamicFieldConfig->{ID}, 1151 Type => $Preference->{Type}, 1152 Text => $Text, 1153 }; 1154 1155 # Decide if dynamic field go to add fields dropdown or selected fields area. 1156 if ( defined $JobData{$Key} ) { 1157 1158 # Get field HTML. 1159 my $DynamicFieldHTML = $DynamicFieldBackendObject->SearchFieldRender( 1160 DynamicFieldConfig => $DynamicFieldConfig, 1161 Profile => \%JobData, 1162 DefaultValue => 1163 $Self->{Config}->{Defaults}->{DynamicField}->{ $DynamicFieldConfig->{Name} }, 1164 LayoutObject => $LayoutObject, 1165 ConfirmationCheckboxes => 1, 1166 Type => $Preference->{Type}, 1167 ); 1168 1169 next PREFERENCE if !IsHashRefWithData($DynamicFieldHTML); 1170 1171 $LayoutObject->Block( 1172 Name => 'SelectedDynamicFields', 1173 Data => { 1174 Label => $DynamicFieldHTML->{Label}, 1175 Field => $DynamicFieldHTML->{Field}, 1176 ID => $Key, 1177 }, 1178 ); 1179 } 1180 else { 1181 push @AddDynamicFields, { 1182 Key => $Key, 1183 Value => $Text, 1184 }; 1185 } 1186 } 1187 } 1188 1189 my $DynamicFieldsStrg = $LayoutObject->BuildSelection( 1190 PossibleNone => 1, 1191 Data => \@AddDynamicFields, 1192 Name => 'AddDynamicFields', 1193 Multiple => 0, 1194 Class => 'Modernize', 1195 ); 1196 $LayoutObject->Block( 1197 Name => 'AddDynamicFields', 1198 Data => { 1199 DynamicFieldsStrg => $DynamicFieldsStrg, 1200 }, 1201 ); 1202 1203 # create dynamic field HTML for set with historical data options 1204 my $PrintDynamicFieldsEditHeader = 1; 1205 1206 # get param object 1207 my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 1208 1209 my @AddNewDynamicFields; 1210 1211 # cycle trough the activated Dynamic Fields for this screen 1212 DYNAMICFIELD: 1213 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1214 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1215 1216 # Check if field is Attachment type ( from OTRSDynamicFieldAttachment ) 1217 # this field is not updatable by Generic Agent 1218 my $IsAttachement = $DynamicFieldBackendObject->HasBehavior( 1219 DynamicFieldConfig => $DynamicFieldConfig, 1220 Behavior => 'IsAttachement', 1221 ); 1222 next DYNAMICFIELD if $IsAttachement; 1223 1224 my $PossibleValuesFilter; 1225 1226 my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior( 1227 DynamicFieldConfig => $DynamicFieldConfig, 1228 Behavior => 'IsACLReducible', 1229 ); 1230 1231 if ($IsACLReducible) { 1232 1233 # get PossibleValues 1234 my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet( 1235 DynamicFieldConfig => $DynamicFieldConfig, 1236 ); 1237 1238 # check if field has PossibleValues property in its configuration 1239 if ( IsHashRefWithData($PossibleValues) ) { 1240 1241 # convert possible values key => value to key => key for ACLs using a Hash slice 1242 my %AclData = %{$PossibleValues}; 1243 @AclData{ keys %AclData } = keys %AclData; 1244 1245 # get ticket object 1246 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 1247 1248 # set possible values filter from ACLs 1249 my $ACL = $TicketObject->TicketAcl( 1250 Action => $Self->{Action}, 1251 Type => 'DynamicField_' . $DynamicFieldConfig->{Name}, 1252 ReturnType => 'Ticket', 1253 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 1254 Data => \%AclData, 1255 UserID => $Self->{UserID}, 1256 ); 1257 if ($ACL) { 1258 my %Filter = $TicketObject->TicketAclData(); 1259 1260 # convert Filer key => key back to key => value using map 1261 %{$PossibleValuesFilter} = map { $_ => $PossibleValues->{$_} } 1262 keys %Filter; 1263 } 1264 } 1265 } 1266 1267 my $Key = 'DynamicField_' . $DynamicFieldConfig->{Name}; 1268 my $Used = 0; 1269 if ( defined $JobData{ $Key . 'Used' } ) { 1270 $Key .= 'Used'; 1271 $Used = 1; 1272 } 1273 1274 # Save all new dynamic fields for JS. 1275 $DynamicFieldsJS{$Key} = { 1276 ID => $DynamicFieldConfig->{ID}, 1277 Text => $DynamicFieldConfig->{Name}, 1278 }; 1279 1280 # Decide if dynamic field go to add fields dropdown or selected fields area. 1281 # 1282 # First statement part - if we have a defined value. 1283 # 1284 # Second statement part - if we have empty value which can be valid if 1285 # current dynamic field has empty value in its configuration (PossibleNone). 1286 # 1287 # Third statement part - it is for Date, DateTime and similar fields which has 1288 # a checkbox "flag" for including its value to GA config. It must return 1289 # false if checkbox is unchecked (in DB value is 0, not empty string or undef), 1290 # otherwise must be true (for other dynamic fields or checked checkbox). 1291 if ( 1292 defined $JobData{$Key} 1293 && ( 1294 $DynamicFieldConfig->{Config}->{PossibleNone} 1295 || $JobData{$Key} ne '' 1296 ) 1297 && ( !$Used || $JobData{$Key} ) 1298 ) 1299 { 1300 1301 # Get field HTML. 1302 my $DynamicFieldHTML = $DynamicFieldBackendObject->EditFieldRender( 1303 DynamicFieldConfig => $DynamicFieldConfig, 1304 PossibleValuesFilter => $PossibleValuesFilter, 1305 LayoutObject => $LayoutObject, 1306 ParamObject => $ParamObject, 1307 UseDefaultValue => 0, 1308 OverridePossibleNone => 1, 1309 ConfirmationNeeded => 1, 1310 NoIgnoreField => 1, 1311 Template => \%JobData, 1312 MaxLength => 200, 1313 ); 1314 1315 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldHTML); 1316 1317 $LayoutObject->Block( 1318 Name => 'SelectedNewDynamicFields', 1319 Data => { 1320 Label => $DynamicFieldHTML->{Label}, 1321 Field => $DynamicFieldHTML->{Field}, 1322 ID => $Key, 1323 }, 1324 ); 1325 } 1326 else { 1327 push @AddNewDynamicFields, { 1328 Key => $Key, 1329 Value => $DynamicFieldConfig->{Name}, 1330 }; 1331 } 1332 } 1333 1334 my $NewDynamicFieldsStrg = $LayoutObject->BuildSelection( 1335 PossibleNone => 1, 1336 Data => \@AddNewDynamicFields, 1337 Name => 'AddNewDynamicFields', 1338 Multiple => 0, 1339 Class => 'Modernize', 1340 ); 1341 $LayoutObject->Block( 1342 Name => 'AddNewDynamicFields', 1343 Data => { 1344 NewDynamicFieldsStrg => $NewDynamicFieldsStrg, 1345 }, 1346 ); 1347 $LayoutObject->AddJSData( 1348 Key => 'DynamicFieldsJS', 1349 Value => \%DynamicFieldsJS, 1350 ); 1351 1352 # get event object 1353 my $EventObject = $Kernel::OM->Get('Kernel::System::Event'); 1354 1355 # get registered event triggers from the config 1356 my %RegisteredEvents = $EventObject->EventList( 1357 ObjectTypes => [ 'Ticket', 'Article' ], 1358 ); 1359 1360 # create the event triggers table 1361 for my $Event ( @{ $JobData{EventValues} || [] } ) { 1362 1363 # set the event type ( event object like Article or Ticket) 1364 my $EventType; 1365 EVENTTYPE: 1366 for my $Type ( sort keys %RegisteredEvents ) { 1367 if ( grep { $_ eq $Event } @{ $RegisteredEvents{$Type} } ) { 1368 $EventType = $Type; 1369 last EVENTTYPE; 1370 } 1371 } 1372 1373 # paint each event row in event triggers table 1374 $LayoutObject->Block( 1375 Name => 'EventRow', 1376 Data => { 1377 Event => $Event, 1378 EventType => $EventType || '-', 1379 }, 1380 ); 1381 } 1382 1383 my @EventTypeList; 1384 my $SelectedEventType = $ParamObject->GetParam( Param => 'EventType' ) || 'Ticket'; 1385 1386 # create event trigger selectors (one for each type) 1387 TYPE: 1388 for my $Type ( sort keys %RegisteredEvents ) { 1389 1390 # refresh event list for each event type 1391 1392 # paint each selector 1393 my $EventStrg = $LayoutObject->BuildSelection( 1394 PossibleNone => 0, 1395 Data => $RegisteredEvents{$Type} || [], 1396 Name => $Type . 'Event', 1397 Sort => 'AlphanumericValue', 1398 PossibleNone => 0, 1399 Class => 'Modernize EventList GenericInterfaceSpacing', 1400 Title => $LayoutObject->{LanguageObject}->Translate('Event'), 1401 ); 1402 1403 $LayoutObject->Block( 1404 Name => 'EventAdd', 1405 Data => { 1406 EventStrg => $EventStrg, 1407 }, 1408 ); 1409 1410 push @EventTypeList, $Type; 1411 } 1412 1413 # create event type selector 1414 my $EventTypeStrg = $LayoutObject->BuildSelection( 1415 Data => \@EventTypeList, 1416 Name => 'EventType', 1417 Sort => 'AlphanumericValue', 1418 SelectedValue => $SelectedEventType, 1419 PossibleNone => 0, 1420 Class => 'Modernize', 1421 Title => $LayoutObject->{LanguageObject}->Translate('Type'), 1422 ); 1423 $LayoutObject->Block( 1424 Name => 'EventTypeStrg', 1425 Data => { 1426 EventTypeStrg => $EventTypeStrg, 1427 }, 1428 ); 1429 1430 return \%JobData; 1431} 1432 1433sub _MaskRun { 1434 my ( $Self, %Param ) = @_; 1435 1436 # get layout object 1437 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 1438 1439 my %JobData; 1440 1441 if ( $Self->{Profile} ) { 1442 %JobData = $Kernel::OM->Get('Kernel::System::GenericAgent')->JobGet( Name => $Self->{Profile} ); 1443 if ( exists $JobData{SearchInArchive} && $JobData{SearchInArchive} eq 'ArchivedTickets' ) { 1444 $JobData{ArchiveFlags} = ['y']; 1445 } 1446 if ( exists $JobData{SearchInArchive} && $JobData{SearchInArchive} eq 'AllTickets' ) { 1447 $JobData{ArchiveFlags} = [ 'y', 'n' ]; 1448 } 1449 } 1450 else { 1451 $LayoutObject->FatalError( 1452 Message => Translatable('Need Profile!'), 1453 ); 1454 } 1455 $JobData{Profile} = $Self->{Profile}; 1456 $Param{Subaction} = $Self->{Subaction}; 1457 $Param{Profile} = $Self->{Profile}; 1458 1459 # dynamic fields search parameters for ticket search 1460 my %DynamicFieldSearchParameters; 1461 1462 # get dynamic field backend object 1463 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 1464 1465 # cycle trough the activated Dynamic Fields for this screen 1466 DYNAMICFIELD: 1467 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1468 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1469 1470 # get search field preferences 1471 my $SearchFieldPreferences = $DynamicFieldBackendObject->SearchFieldPreferences( 1472 DynamicFieldConfig => $DynamicFieldConfig, 1473 ); 1474 1475 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1476 1477 PREFERENCE: 1478 for my $Preference ( @{$SearchFieldPreferences} ) { 1479 1480 if ( 1481 !$JobData{ 1482 'Search_DynamicField_' 1483 . $DynamicFieldConfig->{Name} 1484 . $Preference->{Type} 1485 } 1486 ) 1487 { 1488 next PREFERENCE; 1489 } 1490 1491 # extract the dynamic field value from the profile 1492 my $SearchParameter = $DynamicFieldBackendObject->SearchFieldParameterBuild( 1493 DynamicFieldConfig => $DynamicFieldConfig, 1494 Profile => \%JobData, 1495 LayoutObject => $LayoutObject, 1496 Type => $Preference->{Type}, 1497 ); 1498 1499 # set search parameter 1500 if ( defined $SearchParameter ) { 1501 $DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } 1502 = $SearchParameter->{Parameter}; 1503 } 1504 } 1505 } 1506 1507 # remove residual dynamic field data from job definition 1508 # they are passed through dedicated variable anyway 1509 PARAM_NAME: 1510 for my $ParamName ( sort keys %JobData ) { 1511 next PARAM_NAME if !( $ParamName =~ /^DynamicField_/ ); 1512 delete $JobData{$ParamName}; 1513 } 1514 1515 # get needed objects 1516 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 1517 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 1518 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 1519 1520 # perform ticket search 1521 my $GenericAgentTicketSearch = $ConfigObject->Get("Ticket::GenericAgentTicketSearch") || {}; 1522 my $Counter = $TicketObject->TicketSearch( 1523 Result => 'COUNT', 1524 SortBy => 'Age', 1525 OrderBy => 'Down', 1526 UserID => 1, 1527 Limit => 60_000, 1528 ConditionInline => $GenericAgentTicketSearch->{ExtendedSearchCondition}, 1529 %JobData, 1530 %DynamicFieldSearchParameters, 1531 ) || 0; 1532 1533 my @TicketIDs = $TicketObject->TicketSearch( 1534 Result => 'ARRAY', 1535 SortBy => 'Age', 1536 OrderBy => 'Down', 1537 UserID => 1, 1538 Limit => 30, 1539 ConditionInline => $GenericAgentTicketSearch->{ExtendedSearchCondition}, 1540 %JobData, 1541 %DynamicFieldSearchParameters, 1542 ); 1543 1544 $LayoutObject->Block( 1545 Name => 'ActionList', 1546 ); 1547 $LayoutObject->Block( 1548 Name => 'ActionOverview', 1549 ); 1550 $LayoutObject->Block( 1551 Name => 'Result', 1552 Data => { 1553 %Param, 1554 Name => $Self->{Profile}, 1555 AffectedIDs => $Counter, 1556 }, 1557 ); 1558 1559 my $RunLimit = $ConfigObject->Get('Ticket::GenericAgentRunLimit'); 1560 if ( $Counter > $RunLimit ) { 1561 $LayoutObject->Block( 1562 Name => 'RunLimit', 1563 Data => { 1564 Counter => $Counter, 1565 RunLimit => $RunLimit, 1566 }, 1567 ); 1568 } 1569 1570 if (@TicketIDs) { 1571 $LayoutObject->Block( 1572 Name => 'ResultBlock', 1573 ); 1574 for my $TicketID (@TicketIDs) { 1575 1576 # Get ticket data. 1577 my %Ticket = $TicketObject->TicketGet( 1578 TicketID => $TicketID, 1579 DynamicFields => 0, 1580 ); 1581 1582 # Get article data. 1583 my @Articles = $ArticleObject->ArticleList( 1584 TicketID => $TicketID, 1585 OnlyFirst => 1, 1586 ); 1587 my %Article; 1588 for my $Article (@Articles) { 1589 %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( %{$Article} ); 1590 } 1591 1592 my %Data = ( %Ticket, %Article ); 1593 1594 # Set missing information for tickets without articles. 1595 if ( !%Article ) { 1596 $Data{Subject} = $Data{Title}; 1597 } 1598 1599 $Data{Age} = $LayoutObject->CustomerAge( 1600 Age => $Data{Age}, 1601 Space => ' ', 1602 ); 1603 $Data{css} = "PriorityID-$Data{PriorityID}"; 1604 1605 # user info 1606 my %UserInfo = $Kernel::OM->Get('Kernel::System::User')->GetUserData( 1607 User => $Data{Owner}, 1608 ); 1609 $Data{UserLastname} = $UserInfo{UserLastname}; 1610 $Data{UserFirstname} = $UserInfo{UserFirstname}; 1611 $Data{UserFullname} = $UserInfo{UserFullname}; 1612 $LayoutObject->Block( 1613 Name => 'Ticket', 1614 Data => \%Data, 1615 ); 1616 } 1617 1618 if ( $JobData{NewDelete} ) { 1619 $LayoutObject->Block( 1620 Name => 'DeleteWarning', 1621 ); 1622 } 1623 } 1624 1625 # HTML search mask output 1626 my $Output = $LayoutObject->Header( 1627 Title => Translatable('Affected Tickets'), 1628 ); 1629 $Output .= $LayoutObject->NavigationBar(); 1630 $Output .= $LayoutObject->Output( 1631 TemplateFile => 'AdminGenericAgent', 1632 Data => \%Param, 1633 ); 1634 1635 # build footer 1636 $Output .= $LayoutObject->Footer(); 1637 return $Output; 1638} 1639 1640sub _StopWordsServerErrorsGet { 1641 my ( $Self, %Param ) = @_; 1642 1643 if ( !%Param ) { 1644 $Kernel::OM->Get('Kernel::Output::HTML::Layout')->FatalError( 1645 Message => Translatable('Got no values to check.'), 1646 ); 1647 } 1648 1649 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 1650 1651 my %StopWordsServerErrors; 1652 if ( !$ArticleObject->SearchStringStopWordsUsageWarningActive() ) { 1653 return %StopWordsServerErrors; 1654 } 1655 1656 my %SearchStrings; 1657 1658 FIELD: 1659 for my $Field ( sort keys %Param ) { 1660 next FIELD if !defined $Param{$Field}; 1661 next FIELD if !length $Param{$Field}; 1662 1663 $SearchStrings{$Field} = $Param{$Field}; 1664 } 1665 1666 if (%SearchStrings) { 1667 1668 my $StopWords = $ArticleObject->SearchStringStopWordsFind( 1669 SearchStrings => \%SearchStrings, 1670 ); 1671 1672 FIELD: 1673 for my $Field ( sort keys %{$StopWords} ) { 1674 next FIELD if !defined $StopWords->{$Field}; 1675 next FIELD if ref $StopWords->{$Field} ne 'ARRAY'; 1676 next FIELD if !@{ $StopWords->{$Field} }; 1677 1678 $StopWordsServerErrors{ $Field . 'Invalid' } = 'ServerError'; 1679 $StopWordsServerErrors{ $Field . 'InvalidTooltip' } 1680 = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{LanguageObject} 1681 ->Translate('Please remove the following words because they cannot be used for the ticket selection:') 1682 . ' ' 1683 . join( ',', sort @{ $StopWords->{$Field} } ); 1684 } 1685 } 1686 1687 return %StopWordsServerErrors; 1688} 1689 16901; 1691