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::CustomerTicketSearch; 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 return $Self; 27} 28 29sub Run { 30 my ( $Self, %Param ) = @_; 31 32 my $Output; 33 34 # get needed objects 35 my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request'); 36 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 37 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 38 39 my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}"); 40 41 # get dynamic field config for frontend module 42 my $DynamicFieldFilter = $Config->{DynamicField}; 43 44 # get dynamic field object 45 my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); 46 47 # get the dynamic fields for ticket object 48 my $DynamicField = $DynamicFieldObject->DynamicFieldListGet( 49 Valid => 1, 50 ObjectType => ['Ticket'], 51 FieldFilter => $DynamicFieldFilter || {}, 52 ); 53 54 # get backend object 55 my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 56 57 # reduce the dynamic fields to only the ones that are desinged for customer interface 58 my @CustomerDynamicFields; 59 DYNAMICFIELD: 60 for my $DynamicFieldConfig ( @{$DynamicField} ) { 61 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 62 63 my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( 64 DynamicFieldConfig => $DynamicFieldConfig, 65 Behavior => 'IsCustomerInterfaceCapable', 66 ); 67 next DYNAMICFIELD if !$IsCustomerInterfaceCapable; 68 69 push @CustomerDynamicFields, $DynamicFieldConfig; 70 } 71 $DynamicField = \@CustomerDynamicFields; 72 73 my $Profile = $ParamObject->GetParam( Param => 'Profile' ) || ''; 74 75 # check request 76 if ( $ParamObject->GetParam( Param => 'SearchTemplate' ) && $Profile ) { 77 return $LayoutObject->Redirect( 78 OP => 79 "Action=CustomerTicketSearch;Subaction=Search;TakeLastSearch=1;SaveProfile=1;Profile=$Profile", 80 ); 81 } 82 83 # remember exclude attributes 84 my @Excludes = $ParamObject->GetArray( Param => 'Exclude' ); 85 86 # get single params 87 my %GetParam; 88 89 # get config data 90 my $StartHit = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 ); 91 my $SearchLimit = $ConfigObject->Get('Ticket::CustomerTicketSearch::SearchLimit') 92 || 200; 93 my $SearchPageShown = $ConfigObject->Get('Ticket::CustomerTicketSearch::SearchPageShown') || 40; 94 my $SortBy = $ParamObject->GetParam( Param => 'SortBy' ) 95 || $ConfigObject->Get('Ticket::CustomerTicketSearch::SortBy::Default') 96 || 'Age'; 97 my $CurrentOrder = $ParamObject->GetParam( Param => 'Order' ) 98 || $ConfigObject->Get('Ticket::CustomerTicketSearch::Order::Default') 99 || 'Down'; 100 101 # get search profile object 102 my $SearchProfileObject = $Kernel::OM->Get('Kernel::System::SearchProfile'); 103 my $TakeLastSearch = $ParamObject->GetParam( Param => 'TakeLastSearch' ) || ''; 104 105 # collect all searchable article field definitions and add the fields to the attributes array 106 my %ArticleSearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList(); 107 108 # load profiles string params (press load profile) 109 if ( ( $Self->{Subaction} eq 'LoadProfile' && $Profile ) || $TakeLastSearch ) { 110 %GetParam = $SearchProfileObject->SearchProfileGet( 111 Base => 'CustomerTicketSearch', 112 Name => $Profile, 113 UserLogin => $Self->{UserLogin}, 114 ); 115 } 116 117 # get search string params (get submitted params) 118 else { 119 KEY: 120 for my $Key ( 121 sort keys %ArticleSearchableFields, 122 qw(TicketNumber ResultForm TimeSearchType StateType SearchInArchive 123 TicketCreateTimePointFormat TicketCreateTimePoint TicketCreateTimePointStart 124 TicketCreateTimeStart TicketCreateTimeStartDay TicketCreateTimeStartMonth 125 TicketCreateTimeStartYear TicketCreateTimeStop TicketCreateTimeStopDay 126 TicketCreateTimeStopMonth TicketCreateTimeStopYear 127 ) 128 ) 129 { 130 next KEY if $ArticleSearchableFields{$Key} && $ArticleSearchableFields{$Key}->{HideInCustomerInterface}; 131 132 # get search string params (get submitted params) 133 $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); 134 135 # remove white space on the start and end 136 if ( $GetParam{$Key} ) { 137 $GetParam{$Key} =~ s/\s+$//g; 138 $GetParam{$Key} =~ s/^\s+//g; 139 } 140 } 141 142 # get array params 143 for my $Key ( 144 qw(CustomerID StateIDs StateTypeIDs PriorityIDs OwnerIDs ResponsibleIDs ServiceIDs 145 TypeIDs 146 ) 147 ) 148 { 149 150 # get search array params (get submitted params) 151 my @Array = $ParamObject->GetArray( Param => $Key ); 152 if (@Array) { 153 $GetParam{$Key} = \@Array; 154 } 155 } 156 157 # get Dynamic fields form param object 158 # cycle through the activated Dynamic Fields for this screen 159 DYNAMICFIELD: 160 for my $DynamicFieldConfig ( @{$DynamicField} ) { 161 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 162 163 # get search field preferences 164 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 165 DynamicFieldConfig => $DynamicFieldConfig, 166 ); 167 168 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 169 170 PREFERENCE: 171 for my $Preference ( @{$SearchFieldPreferences} ) { 172 173 # extract the dynamic field value from the web request 174 my $DynamicFieldValue = $BackendObject->SearchFieldValueGet( 175 DynamicFieldConfig => $DynamicFieldConfig, 176 ParamObject => $ParamObject, 177 ReturnProfileStructure => 1, 178 LayoutObject => $LayoutObject, 179 Type => $Preference->{Type}, 180 ); 181 182 # set the complete value structure in GetParam to store it later in the search 183 # profile 184 if ( IsHashRefWithData($DynamicFieldValue) ) { 185 %GetParam = ( %GetParam, %{$DynamicFieldValue} ); 186 } 187 } 188 } 189 } 190 191 # check if item need to get excluded 192 for my $Exclude (@Excludes) { 193 if ( $GetParam{$Exclude} ) { 194 delete $GetParam{$Exclude}; 195 } 196 } 197 198 # get time option 199 if ( !$GetParam{TimeSearchType} ) { 200 $GetParam{'TimeSearchType::None'} = 'checked="checked"'; 201 } 202 elsif ( $GetParam{TimeSearchType} eq 'TimePoint' ) { 203 $GetParam{'TimeSearchType::TimePoint'} = 'checked="checked"'; 204 } 205 elsif ( $GetParam{TimeSearchType} eq 'TimeSlot' ) { 206 $GetParam{'TimeSearchType::TimeSlot'} = 'checked="checked"'; 207 } 208 209 # set result form env 210 if ( !$GetParam{ResultForm} ) { 211 $GetParam{ResultForm} = ''; 212 } 213 if ( $GetParam{ResultForm} eq 'Print' ) { 214 $SearchPageShown = $SearchLimit; 215 } 216 217 # check request 218 if ( $Self->{Subaction} eq 'OpenSearchDescription' ) { 219 my $Output = $LayoutObject->Output( 220 TemplateFile => 'CustomerTicketSearchOpenSearchDescription', 221 Data => {%Param}, 222 ); 223 return $LayoutObject->Attachment( 224 Filename => 'OpenSearchDescription.xml', 225 ContentType => 'text/xml', 226 Content => $Output, 227 Type => 'inline', 228 ); 229 } 230 231 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 232 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 233 234 # Get profile search and template data. 235 my $SaveProfile = $ParamObject->GetParam( Param => 'SaveProfile' ) || ''; 236 my $SelectTemplate = $ParamObject->GetParam( Param => 'SelectTemplate' ) || ''; 237 my $EraseTemplate = $ParamObject->GetParam( Param => 'EraseTemplate' ) || ''; 238 239 # check for server errors 240 my %ServerErrors; 241 if ( 242 $Self->{Subaction} eq 'Search' 243 && !$Self->{EraseTemplate} 244 ) 245 { 246 247 # check for stop word errors 248 my %StopWordsServerErrors = $Self->_StopWordsServerErrorsGet( 249 From => $GetParam{From}, 250 To => $GetParam{To}, 251 Cc => $GetParam{Cc}, 252 Subject => $GetParam{Subject}, 253 Body => $GetParam{Body}, 254 ); 255 256 %ServerErrors = ( %ServerErrors, %StopWordsServerErrors ); 257 } 258 259 # show result page 260 if ( !%ServerErrors && $Self->{Subaction} eq 'Search' && !$EraseTemplate ) { 261 262 my $ProfileName = ''; 263 if ($Profile) { 264 $ProfileName = "($Profile)"; 265 } 266 267 # fill up profile name (e.g. with last-search) 268 if ( !$Profile || !$SaveProfile ) { 269 $Profile = 'last-search'; 270 } 271 272 if ( !$ProfileName ) { 273 $ProfileName = "($Profile)"; 274 } 275 276 if ( $ProfileName eq '(last-search)' ) { 277 $ProfileName = '(' . $LayoutObject->{LanguageObject}->Translate('last-search') . ')'; 278 } 279 280 # store search URL in LastScreenOverview to make sure the 281 # customer can use the "back" link as expected 282 my $URL = "Action=CustomerTicketSearch;Subaction=Search;Profile=$Profile;" 283 . "SortBy=$SortBy;Order=$CurrentOrder;TakeLastSearch=1;" 284 . "StartHit=$StartHit"; 285 $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( 286 SessionID => $Self->{SessionID}, 287 Key => 'LastScreenOverview', 288 Value => $URL, 289 ); 290 291 # save search profile (under last-search or real profile name) 292 $SaveProfile = 1; 293 294 # remember last search values 295 if ( $SaveProfile && $Profile ) { 296 297 # remove old profile stuff 298 $SearchProfileObject->SearchProfileDelete( 299 Base => 'CustomerTicketSearch', 300 Name => $Profile, 301 UserLogin => $Self->{UserLogin}, 302 ); 303 304 # insert new profile params 305 for my $Key ( sort keys %GetParam ) { 306 if ( $GetParam{$Key} ) { 307 $SearchProfileObject->SearchProfileAdd( 308 Base => 'CustomerTicketSearch', 309 Name => $Profile, 310 Key => $Key, 311 Value => $GetParam{$Key}, 312 UserLogin => $Self->{UserLogin}, 313 ); 314 } 315 } 316 } 317 318 my %TimeMap = ( 319 TicketCreate => 'Time', 320 ); 321 322 for my $TimeType ( sort keys %TimeMap ) { 323 324 # get create time settings 325 if ( !$GetParam{ $TimeMap{$TimeType} . 'SearchType' } ) { 326 327 # do nothing with time stuff 328 } 329 elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimeSlot' ) { 330 for my $Key (qw(Month Day)) { 331 $GetParam{ $TimeType . 'TimeStart' . $Key } 332 = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStart' . $Key } ); 333 $GetParam{ $TimeType . 'TimeStop' . $Key } 334 = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStop' . $Key } ); 335 } 336 if ( 337 $GetParam{ $TimeType . 'TimeStartDay' } 338 && $GetParam{ $TimeType . 'TimeStartMonth' } 339 && $GetParam{ $TimeType . 'TimeStartYear' } 340 ) 341 { 342 my $DateTimeObject = $Kernel::OM->Create( 343 'Kernel::System::DateTime', 344 ObjectParams => { 345 Year => $GetParam{ $TimeType . 'TimeStartYear' }, 346 Month => $GetParam{ $TimeType . 'TimeStartMonth' }, 347 Day => $GetParam{ $TimeType . 'TimeStartDay' }, 348 Hour => 0, # midnight 349 Minute => 0, 350 Second => 0, 351 TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(), 352 }, 353 ); 354 355 # Convert start time to local system time zone. 356 $DateTimeObject->ToOTRSTimeZone(); 357 $GetParam{ $TimeType . 'TimeNewerDate' } = $DateTimeObject->ToString(); 358 } 359 if ( 360 $GetParam{ $TimeType . 'TimeStopDay' } 361 && $GetParam{ $TimeType . 'TimeStopMonth' } 362 && $GetParam{ $TimeType . 'TimeStopYear' } 363 ) 364 { 365 my $DateTimeObject = $Kernel::OM->Create( 366 'Kernel::System::DateTime', 367 ObjectParams => { 368 Year => $GetParam{ $TimeType . 'TimeStopYear' }, 369 Month => $GetParam{ $TimeType . 'TimeStopMonth' }, 370 Day => $GetParam{ $TimeType . 'TimeStopDay' }, 371 Hour => 23, # just before midnight 372 Minute => 59, 373 Second => 59, 374 TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(), 375 }, 376 ); 377 378 # Convert stop time to local system time zone. 379 $DateTimeObject->ToOTRSTimeZone(); 380 $GetParam{ $TimeType . 'TimeOlderDate' } = $DateTimeObject->ToString(); 381 } 382 } 383 elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimePoint' ) { 384 if ( 385 $GetParam{ $TimeType . 'TimePoint' } 386 && $GetParam{ $TimeType . 'TimePointStart' } 387 && $GetParam{ $TimeType . 'TimePointFormat' } 388 ) 389 { 390 my $Time = 0; 391 if ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'minute' ) { 392 $Time = $GetParam{ $TimeType . 'TimePoint' }; 393 } 394 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'hour' ) { 395 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60; 396 } 397 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'day' ) { 398 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24; 399 } 400 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'week' ) { 401 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 7; 402 } 403 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'month' ) { 404 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 30; 405 } 406 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'year' ) { 407 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 365; 408 } 409 if ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Before' ) { 410 411 # more than ... ago 412 $GetParam{ $TimeType . 'TimeOlderMinutes' } = $Time; 413 } 414 elsif ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Next' ) { 415 416 # within next 417 $GetParam{ $TimeType . 'TimeNewerMinutes' } = 0; 418 $GetParam{ $TimeType . 'TimeOlderMinutes' } = -$Time; 419 } 420 else { 421 422 # within last ... 423 $GetParam{ $TimeType . 'TimeOlderMinutes' } = 0; 424 $GetParam{ $TimeType . 'TimeNewerMinutes' } = $Time; 425 } 426 } 427 } 428 } 429 430 # prepare archive flag 431 if ( 432 $ConfigObject->Get('Ticket::ArchiveSystem') 433 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') 434 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 1 435 ) 436 { 437 438 $GetParam{SearchInArchive} ||= ''; 439 if ( $GetParam{SearchInArchive} eq 'AllTickets' ) { 440 $GetParam{ArchiveFlags} = [ 'y', 'n' ]; 441 } 442 elsif ( $GetParam{SearchInArchive} eq 'ArchivedTickets' ) { 443 $GetParam{ArchiveFlags} = ['y']; 444 } 445 else { 446 $GetParam{ArchiveFlags} = ['n']; 447 } 448 } 449 elsif ( 450 $ConfigObject->Get('Ticket::ArchiveSystem') 451 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') 452 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 2 453 ) 454 { 455 $GetParam{ArchiveFlags} = ['n']; 456 } 457 else { 458 $GetParam{ArchiveFlags} = [ 'y', 'n' ]; 459 } 460 461 # dynamic fields search parameters for ticket search 462 my %DynamicFieldSearchParameters; 463 my %DynamicFieldSearchDisplay; 464 465 # cycle through the activated Dynamic Fields for this screen 466 DYNAMICFIELD: 467 for my $DynamicFieldConfig ( @{$DynamicField} ) { 468 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 469 470 # get search field preferences 471 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 472 DynamicFieldConfig => $DynamicFieldConfig, 473 ); 474 475 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 476 477 PREFERENCE: 478 for my $Preference ( @{$SearchFieldPreferences} ) { 479 480 my $DynamicFieldValue = $BackendObject->SearchFieldValueGet( 481 DynamicFieldConfig => $DynamicFieldConfig, 482 ParamObject => $ParamObject, 483 Type => $Preference->{Type}, 484 ReturnProfileStructure => 1, 485 ); 486 487 # set the complete value structure in %DynamicFieldValues to discard those where the 488 # value will not be possible to get 489 next PREFERENCE if !IsHashRefWithData($DynamicFieldValue); 490 491 # extract the dynamic field value from the profile 492 my $SearchParameter = $BackendObject->SearchFieldParameterBuild( 493 DynamicFieldConfig => $DynamicFieldConfig, 494 Profile => \%GetParam, 495 LayoutObject => $LayoutObject, 496 Type => $Preference->{Type}, 497 ); 498 499 # set search parameter 500 if ( defined $SearchParameter ) { 501 $DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } 502 = $SearchParameter->{Parameter}; 503 504 # set value to display 505 $DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} } 506 = $SearchParameter->{Display}; 507 } 508 } 509 } 510 511 # disable output of customer company tickets 512 my $DisableCompanyTickets = $ConfigObject->Get('Ticket::Frontend::CustomerDisableCompanyTicketAccess'); 513 514 if ($DisableCompanyTickets) { 515 $GetParam{CustomerUserLoginRaw} = $Self->{UserID}; 516 } 517 518 # perform ticket search 519 my @ViewableTicketIDs = $TicketObject->TicketSearch( 520 Result => 'ARRAY', 521 SortBy => $SortBy, 522 OrderBy => $CurrentOrder, 523 Limit => $SearchLimit, 524 CustomerUserID => $Self->{UserID}, 525 ConditionInline => $Config->{ExtendedSearchCondition}, 526 ContentSearchPrefix => '*', 527 ContentSearchSuffix => '*', 528 FullTextIndex => 1, 529 %GetParam, 530 %DynamicFieldSearchParameters, 531 ); 532 533 # get needed objects 534 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 535 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 536 537 # CSV and Excel output 538 if ( 539 $GetParam{ResultForm} eq 'CSV' 540 || $GetParam{ResultForm} eq 'Excel' 541 ) 542 { 543 544 # create head (actual head and head for data fill) 545 my @TmpCSVHead = @{ $Config->{SearchCSVData} }; 546 my @CSVHead = @{ $Config->{SearchCSVData} }; 547 548 # get the ticket dynamic fields for CSV display 549 my $CSVDynamicField = $DynamicFieldObject->DynamicFieldListGet( 550 Valid => 1, 551 ObjectType => ['Ticket'], 552 FieldFilter => $Config->{SearchCSVDynamicField} || {}, 553 ); 554 555 # reduce the dynamic fields to only the ones that are desinged for customer interface 556 my @CSVCustomerDynamicFields; 557 DYNAMICFIELD: 558 for my $DynamicFieldConfig ( @{$CSVDynamicField} ) { 559 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 560 561 my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( 562 DynamicFieldConfig => $DynamicFieldConfig, 563 Behavior => 'IsCustomerInterfaceCapable', 564 ); 565 next DYNAMICFIELD if !$IsCustomerInterfaceCapable; 566 567 push @CSVCustomerDynamicFields, $DynamicFieldConfig; 568 } 569 $CSVDynamicField = \@CSVCustomerDynamicFields; 570 571 # include the selected dynamic fields in CVS results 572 DYNAMICFIELD: 573 for my $DynamicFieldConfig ( @{$CSVDynamicField} ) { 574 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 575 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 576 next DYNAMICFIELD if $DynamicFieldConfig->{Name} eq ''; 577 578 push @TmpCSVHead, 'DynamicField_' . $DynamicFieldConfig->{Name}; 579 push @CSVHead, $DynamicFieldConfig->{Label}; 580 } 581 582 my @CSVData; 583 for my $TicketID (@ViewableTicketIDs) { 584 585 # Get ticket data. 586 my %Ticket = $TicketObject->TicketGet( 587 TicketID => $TicketID, 588 DynamicFields => 1, 589 Extended => 1, 590 UserID => $Self->{UserID}, 591 ); 592 593 # Get first article data. 594 my @Articles = $ArticleObject->ArticleList( 595 TicketID => $TicketID, 596 IsVisibleForCustomer => 1, 597 OnlyFirst => 1, 598 ); 599 my %Article; 600 for my $Article (@Articles) { 601 %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 602 TicketID => $TicketID, 603 ArticleID => $Article->{ArticleID}, 604 DynamicFields => 1, 605 ); 606 } 607 608 my %Data; 609 610 # If no article was found, set some defaults. 611 if ( !%Article ) { 612 %Data = %Ticket; 613 $Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled'); 614 $Data{Body} = $LayoutObject->{LanguageObject}->Translate('This item has no articles yet.'); 615 $Data{From} = '--'; 616 } 617 else { 618 %Data = ( %Ticket, %Article ); 619 } 620 621 for my $Key (qw(State Lock)) { 622 $Data{$Key} = $LayoutObject->{LanguageObject}->Translate( $Data{$Key} ); 623 } 624 625 $Data{Age} = $LayoutObject->CustomerAge( 626 Age => $Data{Age}, 627 Space => ' ' 628 ); 629 630 # get whole article (if configured!) 631 if ( $Config->{SearchArticleCSVTree} && $GetParam{ResultForm} eq 'CSV' ) { 632 my @Articles = $ArticleObject->ArticleList( 633 TicketID => $TicketID, 634 IsVisibleForCustomer => 1, 635 ); 636 if (@Articles) { 637 for my $Article (@Articles) { 638 my %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 639 TicketID => $TicketID, 640 ArticleID => $Article->{ArticleID}, 641 DynamicFields => 0, 642 ); 643 if ( $Article{Body} ) { 644 $Data{ArticleTree} 645 .= "\n-->" 646 . "||$Article{SenderType}" 647 . "||$Article{From}" 648 . "||$Article{CreateTime}" 649 . "||<--------------\n" 650 . $Article{Body}; 651 } 652 } 653 } 654 else { 655 $Data{ArticleTree} .= $LayoutObject->{LanguageObject}->Translate( 656 'This item has no articles yet.' 657 ); 658 } 659 } 660 661 # customer info (customer name) 662 if ( $Data{CustomerUserID} ) { 663 $Data{CustomerName} = $CustomerUserObject->CustomerName( 664 UserLogin => $Data{CustomerUserID}, 665 ); 666 } 667 668 # user info 669 my %UserInfo = $UserObject->GetUserData( 670 User => $Data{Owner}, 671 ); 672 673 # merge row data 674 my %Info = ( 675 %Data, 676 %UserInfo, 677 AccountedTime => 678 $TicketObject->TicketAccountedTimeGet( TicketID => $TicketID ), 679 ); 680 681 my @Data; 682 for my $Header (@TmpCSVHead) { 683 684 # check if header is a dynamic field and get the value from dynamic field 685 # backend 686 if ( $Header =~ m{\A DynamicField_ ( [a-zA-Z\d]+ ) \z}xms ) { 687 688 # loop over the dynamic fields configured for CSV output 689 DYNAMICFIELD: 690 for my $DynamicFieldConfig ( @{$CSVDynamicField} ) { 691 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 692 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 693 694 # skip all fields that do not match the current field name ($1) 695 # with out the 'DynamicField_' prefix 696 next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $1; 697 698 # get the value for print 699 my $ValueStrg = $BackendObject->DisplayValueRender( 700 DynamicFieldConfig => $DynamicFieldConfig, 701 Value => $Info{$Header}, 702 HTMLOutput => 0, 703 LayoutObject => $LayoutObject, 704 ); 705 push @Data, $ValueStrg->{Value}; 706 707 # terminate the loop 708 last DYNAMICFIELD; 709 } 710 } 711 712 # otherwise retrieve data from article 713 else { 714 push @Data, $Info{$Header}; 715 } 716 } 717 push @CSVData, \@Data; 718 } 719 720 # get Separator from language file 721 my $UserCSVSeparator = $LayoutObject->{LanguageObject}->{Separator}; 722 723 if ( $ConfigObject->Get('PreferencesGroups')->{CSVSeparator}->{Active} ) { 724 my %UserData = $UserObject->GetUserData( UserID => $Self->{UserID} ); 725 $UserCSVSeparator = $UserData{UserCSVSeparator} if $UserData{UserCSVSeparator}; 726 } 727 728 my %HeaderMap = ( 729 TicketNumber => Translatable('Ticket Number'), 730 CustomerName => Translatable('Customer Realname'), 731 ); 732 733 my @CSVHeadTranslated = map { $LayoutObject->{LanguageObject}->Translate( $HeaderMap{$_} || $_ ); } 734 @CSVHead; 735 736 # return csv to download 737 my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 738 my $FileName = sprintf( 739 'ticket_search_%s', 740 $CurSystemDateTimeObject->Format( 741 Format => '%Y-%m-%d_%H-%M' 742 ) 743 ); 744 745 # get CSV object 746 my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV'); 747 748 if ( $GetParam{ResultForm} eq 'CSV' ) { 749 750 my $CSV = $CSVObject->Array2CSV( 751 Head => \@CSVHeadTranslated, 752 Data => \@CSVData, 753 ); 754 return $LayoutObject->Attachment( 755 Filename => $FileName . '.csv', 756 ContentType => "text/csv; charset=" . $LayoutObject->{UserCharset}, 757 Content => $CSV, 758 ); 759 760 } 761 762 # return Excel to download 763 elsif ( $GetParam{ResultForm} eq 'Excel' ) { 764 my $Excel = $CSVObject->Array2CSV( 765 Head => \@CSVHeadTranslated, 766 Data => \@CSVData, 767 Format => "Excel", 768 ); 769 770 return $LayoutObject->Attachment( 771 Filename => $FileName . '.xlsx', 772 ContentType => 773 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 774 Content => $Excel, 775 ); 776 } 777 } 778 779 elsif ( $GetParam{ResultForm} eq 'Print' ) { 780 781 # get PDF object 782 my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); 783 784 my @PDFData; 785 for my $TicketID (@ViewableTicketIDs) { 786 787 # Get ticket data. 788 my %Ticket = $TicketObject->TicketGet( 789 TicketID => $TicketID, 790 DynamicFields => 1, 791 Extended => 1, 792 UserID => $Self->{UserID}, 793 ); 794 795 # Get last customer article. 796 my @Articles = $ArticleObject->ArticleList( 797 TicketID => $TicketID, 798 SenderType => 'customer', 799 IsVisibleForCustomer => 1, 800 OnlyLast => 1, 801 ); 802 803 # If the ticket has no customer article, get the last agent article. 804 if ( !@Articles ) { 805 @Articles = $ArticleObject->ArticleList( 806 TicketID => $TicketID, 807 SenderType => 'agent', 808 IsVisibleForCustomer => 1, 809 OnlyLast => 1, 810 ); 811 } 812 813 # Finally, if everything failed, get latest article. 814 if ( !@Articles ) { 815 @Articles = $ArticleObject->ArticleList( 816 TicketID => $TicketID, 817 IsVisibleForCustomer => 1, 818 OnlyLast => 1, 819 ); 820 } 821 822 my %Article; 823 for my $Article (@Articles) { 824 %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 825 %{$Article}, 826 DynamicFields => 0, 827 ); 828 } 829 830 my %Data; 831 832 # If no article was found, set some defaults. 833 if ( !%Article ) { 834 %Data = %Ticket; 835 $Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled'); 836 $Data{From} = '--'; 837 } 838 else { 839 %Data = ( %Ticket, %Article ); 840 } 841 842 # customer info 843 my %CustomerData; 844 if ( $Data{CustomerUserID} ) { 845 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 846 User => $Data{CustomerUserID}, 847 ); 848 } 849 elsif ( $Data{CustomerID} ) { 850 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 851 CustomerID => $Data{CustomerID}, 852 ); 853 } 854 855 # customer info (customer name) 856 if ( $CustomerData{UserLogin} ) { 857 $Data{CustomerName} = $CustomerUserObject->CustomerName( 858 UserLogin => $CustomerData{UserLogin}, 859 ); 860 } 861 862 # user info 863 my %UserInfo = $UserObject->GetUserData( 864 User => $Data{Owner}, 865 ); 866 867 # customer info string 868 $UserInfo{CustomerName} = '(' . $UserInfo{CustomerName} . ')' 869 if ( $UserInfo{CustomerName} ); 870 871 my %Info = ( %Data, %UserInfo ); 872 my $Created = $LayoutObject->{LanguageObject}->FormatTimeString( 873 $Data{CreateTime} // $Data{Created}, 874 'DateFormat', 875 ); 876 877 my $Customer = "$Data{CustomerID} $Data{CustomerName}"; 878 879 my @PDFRow; 880 push @PDFRow, $Data{TicketNumber}; 881 push @PDFRow, $Created; 882 push @PDFRow, $Data{From}; 883 push @PDFRow, $Data{Subject}; 884 push @PDFRow, $Data{State}; 885 push @PDFRow, $Data{Queue}; 886 push @PDFRow, $Customer; 887 push @PDFData, \@PDFRow; 888 889 } 890 891 my $Title = $LayoutObject->{LanguageObject}->Translate('Ticket') . ' ' 892 . $LayoutObject->{LanguageObject}->Translate('Search'); 893 my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by'); 894 my $Page = $LayoutObject->{LanguageObject}->Translate('Page'); 895 my $DateTimeString = $Kernel::OM->Create('Kernel::System::DateTime')->ToString(); 896 my $Time = $LayoutObject->{LanguageObject}->FormatTimeString( 897 $DateTimeString, 898 'DateFormat', 899 ); 900 901 # get maximum number of pages 902 my $MaxPages = $ConfigObject->Get('PDF::MaxPages'); 903 if ( !$MaxPages || $MaxPages < 1 || $MaxPages > 1000 ) { 904 $MaxPages = 100; 905 } 906 907 my $CellData; 908 909 # verify if there are tickets to show 910 if (@PDFData) { 911 912 # create the header 913 $CellData->[0]->[0]->{Content} = $ConfigObject->Get('Ticket::Hook'); 914 $CellData->[0]->[0]->{Font} = 'ProportionalBold'; 915 $CellData->[0]->[1]->{Content} = $LayoutObject->{LanguageObject}->Translate('Created'); 916 $CellData->[0]->[1]->{Font} = 'ProportionalBold'; 917 $CellData->[0]->[2]->{Content} = $LayoutObject->{LanguageObject}->Translate('From'); 918 $CellData->[0]->[2]->{Font} = 'ProportionalBold'; 919 $CellData->[0]->[3]->{Content} = $LayoutObject->{LanguageObject}->Translate('Subject'); 920 $CellData->[0]->[3]->{Font} = 'ProportionalBold'; 921 $CellData->[0]->[4]->{Content} = $LayoutObject->{LanguageObject}->Translate('State'); 922 $CellData->[0]->[4]->{Font} = 'ProportionalBold'; 923 $CellData->[0]->[5]->{Content} = $LayoutObject->{LanguageObject}->Translate('Queue'); 924 $CellData->[0]->[5]->{Font} = 'ProportionalBold'; 925 $CellData->[0]->[6]->{Content} = $LayoutObject->{LanguageObject}->Translate('CustomerID'); 926 $CellData->[0]->[6]->{Font} = 'ProportionalBold'; 927 928 # create the content array 929 my $CounterRow = 1; 930 for my $Row (@PDFData) { 931 my $CounterColumn = 0; 932 for my $Content ( @{$Row} ) { 933 $CellData->[$CounterRow]->[$CounterColumn]->{Content} = $Content; 934 $CounterColumn++; 935 } 936 $CounterRow++; 937 } 938 } 939 940 # otherwise, show 'No ticket data found' message 941 else { 942 $CellData->[0]->[0]->{Content} = $LayoutObject->{LanguageObject}->Translate('No ticket data found.'); 943 } 944 945 # page params 946 my %PageParam; 947 $PageParam{PageOrientation} = 'landscape'; 948 $PageParam{MarginTop} = 30; 949 $PageParam{MarginRight} = 40; 950 $PageParam{MarginBottom} = 40; 951 $PageParam{MarginLeft} = 40; 952 $PageParam{HeaderRight} = $Title; 953 $PageParam{HeadlineLeft} = $Title; 954 955 # table params 956 my %TableParam; 957 $TableParam{CellData} = $CellData; 958 $TableParam{Type} = 'Cut'; 959 $TableParam{FontSize} = 6; 960 $TableParam{Border} = 0; 961 $TableParam{BackgroundColorEven} = '#DDDDDD'; 962 $TableParam{Padding} = 1; 963 $TableParam{PaddingTop} = 3; 964 $TableParam{PaddingBottom} = 3; 965 966 # create new pdf document 967 $PDFObject->DocumentNew( 968 Title => $ConfigObject->Get('Product') . ': ' . $Title, 969 Encode => $LayoutObject->{UserCharset}, 970 ); 971 972 # start table output 973 $PDFObject->PageNew( 974 %PageParam, 975 FooterRight => $Page . ' 1', 976 ); 977 978 $PDFObject->PositionSet( 979 Move => 'relativ', 980 Y => -6, 981 ); 982 983 # output title 984 $PDFObject->Text( 985 Text => $Title, 986 FontSize => 13, 987 ); 988 989 $PDFObject->PositionSet( 990 Move => 'relativ', 991 Y => -6, 992 ); 993 994 # output "printed by" 995 $PDFObject->Text( 996 Text => $PrintedBy . ' ' 997 . $Self->{UserFullname} . ' (' 998 . $Self->{UserEmail} . ')' 999 . ', ' . $Time, 1000 FontSize => 9, 1001 ); 1002 1003 $PDFObject->PositionSet( 1004 Move => 'relativ', 1005 Y => -14, 1006 ); 1007 1008 PAGE: 1009 for my $PageNumber ( 2 .. $MaxPages ) { 1010 1011 # output table (or a fragment of it) 1012 %TableParam = $PDFObject->Table(%TableParam); 1013 1014 # stop output or another page 1015 if ( $TableParam{State} ) { 1016 last PAGE; 1017 } 1018 else { 1019 $PDFObject->PageNew( 1020 %PageParam, 1021 FooterRight => $Page . ' ' . $PageNumber, 1022 ); 1023 } 1024 } 1025 1026 # return the pdf document 1027 my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 1028 my $Filename = sprintf( 1029 'ticket_search_%s.pdf', 1030 $CurSystemDateTimeObject->Format( Format => '%Y-%m-%d_%H-%M' ), 1031 ); 1032 1033 my $PDFString = $PDFObject->DocumentOutput(); 1034 return $LayoutObject->Attachment( 1035 Filename => $Filename, 1036 ContentType => "application/pdf", 1037 Content => $PDFString, 1038 Type => 'inline', 1039 ); 1040 1041 } 1042 1043 my $Counter = 0; 1044 1045 # get the ticket dynamic fields for overview display 1046 my $OverviewDynamicField = $DynamicFieldObject->DynamicFieldListGet( 1047 Valid => 1, 1048 ObjectType => ['Ticket'], 1049 FieldFilter => $Config->{SearchOverviewDynamicField} || {}, 1050 ); 1051 1052 # reduce the dynamic fields to only the ones that are desinged for customer interface 1053 my @OverviewCustomerDynamicFields; 1054 DYNAMICFIELD: 1055 for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) { 1056 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1057 1058 my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( 1059 DynamicFieldConfig => $DynamicFieldConfig, 1060 Behavior => 'IsCustomerInterfaceCapable', 1061 ); 1062 next DYNAMICFIELD if !$IsCustomerInterfaceCapable; 1063 1064 push @OverviewCustomerDynamicFields, $DynamicFieldConfig; 1065 } 1066 $OverviewDynamicField = \@OverviewCustomerDynamicFields; 1067 1068 # if there are results to show 1069 if (@ViewableTicketIDs) { 1070 1071 # Dynamic fields table headers 1072 # cycle through the activated Dynamic Fields for this screen 1073 DYNAMICFIELD: 1074 for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) { 1075 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1076 1077 my $Label = $DynamicFieldConfig->{Label}; 1078 1079 # get field sortable condition 1080 my $IsSortable = $BackendObject->HasBehavior( 1081 DynamicFieldConfig => $DynamicFieldConfig, 1082 Behavior => 'IsSortable', 1083 ); 1084 1085 if ($IsSortable) { 1086 my $CSS = ''; 1087 my $Order = 'Down'; 1088 if ( 1089 $SortBy 1090 && ( 1091 $SortBy eq 1092 ( 'DynamicField_' . $DynamicFieldConfig->{Name} ) 1093 ) 1094 ) 1095 { 1096 if ( $CurrentOrder && ( $CurrentOrder eq 'Up' ) ) { 1097 $Order = 'Down'; 1098 $CSS .= ' SortAscending'; 1099 } 1100 else { 1101 $Order = 'Up'; 1102 $CSS .= ' SortDescending'; 1103 } 1104 } 1105 1106 $LayoutObject->Block( 1107 Name => 'HeaderDynamicField', 1108 Data => { 1109 %Param, 1110 CSS => $CSS, 1111 }, 1112 ); 1113 1114 $LayoutObject->Block( 1115 Name => 'HeaderDynamicFieldSortable', 1116 Data => { 1117 %Param, 1118 Order => $Order, 1119 Label => $Label, 1120 DynamicFieldName => $DynamicFieldConfig->{Name}, 1121 }, 1122 ); 1123 } 1124 else { 1125 1126 $LayoutObject->Block( 1127 Name => 'HeaderDynamicField', 1128 Data => { 1129 %Param, 1130 }, 1131 ); 1132 1133 $LayoutObject->Block( 1134 Name => 'HeaderDynamicFieldNotSortable', 1135 Data => { 1136 %Param, 1137 Label => $Label, 1138 }, 1139 ); 1140 } 1141 } 1142 1143 for my $TicketID (@ViewableTicketIDs) { 1144 $Counter++; 1145 1146 # build search result 1147 if ( 1148 $Counter >= $StartHit 1149 && $Counter < ( $SearchPageShown + $StartHit ) 1150 ) 1151 { 1152 1153 # Get ticket data. 1154 my %Ticket = $TicketObject->TicketGet( 1155 TicketID => $TicketID, 1156 DynamicFields => 1, 1157 Extended => 1, 1158 UserID => $Self->{UserID}, 1159 ); 1160 1161 # Get last customer article. 1162 my @Articles = $ArticleObject->ArticleList( 1163 TicketID => $TicketID, 1164 SenderType => 'customer', 1165 IsVisibleForCustomer => 1, 1166 OnlyLast => 1, 1167 ); 1168 1169 # If the ticket has no customer article, get the last agent article. 1170 if ( !@Articles ) { 1171 @Articles = $ArticleObject->ArticleList( 1172 TicketID => $TicketID, 1173 SenderType => 'agent', 1174 IsVisibleForCustomer => 1, 1175 OnlyLast => 1, 1176 ); 1177 } 1178 1179 # Finally, if everything failed, get latest article. 1180 if ( !@Articles ) { 1181 @Articles = $ArticleObject->ArticleList( 1182 TicketID => $TicketID, 1183 IsVisibleForCustomer => 1, 1184 OnlyLast => 1, 1185 ); 1186 } 1187 1188 my %Article; 1189 for my $Article (@Articles) { 1190 %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 1191 %{$Article}, 1192 DynamicFields => 1, 1193 ); 1194 } 1195 1196 my %Data; 1197 1198 # If no article was found, set some defaults. 1199 if ( !%Article ) { 1200 %Data = %Ticket; 1201 $Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled'); 1202 $Data{Body} = $LayoutObject->{LanguageObject}->Translate( 1203 'This item has no articles yet.' 1204 ); 1205 } 1206 else { 1207 %Data = ( %Ticket, %Article ); 1208 } 1209 1210 # customer info 1211 my %CustomerData; 1212 if ( $Data{CustomerUserID} ) { 1213 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1214 User => $Data{CustomerUserID}, 1215 ); 1216 } 1217 elsif ( $Data{CustomerID} ) { 1218 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1219 User => $Data{CustomerID}, 1220 ); 1221 } 1222 1223 # customer info (customer name) 1224 if ( $CustomerData{UserLogin} ) { 1225 $Data{CustomerName} = $CustomerUserObject->CustomerName( 1226 UserLogin => $CustomerData{UserLogin}, 1227 ); 1228 } 1229 1230 # user info 1231 my %Owner = $UserObject->GetUserData( 1232 User => $Data{Owner}, 1233 ); 1234 1235 # Condense down the subject 1236 my $Subject = $TicketObject->TicketSubjectClean( 1237 TicketNumber => $Data{TicketNumber}, 1238 Subject => $Data{Subject} || '', 1239 ); 1240 $Data{CustomerAge} = $LayoutObject->CustomerAge( 1241 Age => $Data{Age}, 1242 Space => ' ', 1243 ); 1244 1245 # customer info string 1246 if ( $Data{CustomerName} ) { 1247 $Data{CustomerName} = '(' . $Data{CustomerName} . ')'; 1248 } 1249 1250 # add blocks to template 1251 $LayoutObject->Block( 1252 Name => 'Record', 1253 Data => { 1254 %Data, 1255 Subject => $Subject, 1256 %Owner, 1257 }, 1258 ); 1259 1260 # Dynamic fields 1261 # cycle through the activated Dynamic Fields for this screen 1262 DYNAMICFIELD: 1263 for my $DynamicFieldConfig ( @{$OverviewDynamicField} ) { 1264 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1265 1266 # get field value 1267 my $ValueStrg = $BackendObject->DisplayValueRender( 1268 DynamicFieldConfig => $DynamicFieldConfig, 1269 Value => $Data{ 'DynamicField_' . $DynamicFieldConfig->{Name} }, 1270 ValueMaxChars => 20, 1271 LayoutObject => $LayoutObject, 1272 ); 1273 1274 $LayoutObject->Block( 1275 Name => 'RecordDynamicField', 1276 Data => { 1277 Value => $ValueStrg->{Value}, 1278 Title => $ValueStrg->{Title}, 1279 }, 1280 ); 1281 } 1282 } 1283 } 1284 } 1285 1286 # otherwise show a no data found msg 1287 else { 1288 $LayoutObject->Block( Name => 'NoDataFoundMsg' ); 1289 } 1290 1291 # show attributes used for search 1292 my %IDMap = ( 1293 StateIDs => { 1294 Name => 'State', 1295 Object => 'State', 1296 Method => 'StateLookup', 1297 Key => 'StateID', 1298 Translation => 1, 1299 }, 1300 StateTypeIDs => { 1301 Name => 'StateType', 1302 Object => 'State', 1303 Method => 'StateTypeLookup', 1304 Key => 'StateTypeID', 1305 Translation => 1, 1306 }, 1307 PriorityIDs => { 1308 Name => 'Priority', 1309 Object => 'Priority', 1310 Method => 'PriorityLookup', 1311 Key => 'PriorityID', 1312 Translation => 1, 1313 }, 1314 QueueIDs => { 1315 Name => 'Queue', 1316 Object => 'Queue', 1317 Method => 'QueueLookup', 1318 Key => 'QueueID', 1319 Translation => 0, 1320 }, 1321 OwnerIDs => { 1322 Name => 'Owner', 1323 Object => 'User', 1324 Method => 'UserLookup', 1325 Key => 'UserID', 1326 Translation => 0, 1327 }, 1328 ResponsibleIDs => { 1329 Name => 'Responsible', 1330 Object => 'User', 1331 Method => 'UserLookup', 1332 Key => 'UserID', 1333 Translation => 0, 1334 }, 1335 1336 ResponsibleIDs => { 1337 Name => 'Responsible', 1338 Object => 'User', 1339 Method => 'UserLookup', 1340 Key => 'UserID', 1341 Translation => 0, 1342 }, 1343 ); 1344 1345 KEY: 1346 for my $Key ( 1347 qw(TicketNumber From To Cc Subject Body CustomerID TimeSearchType StateType 1348 StateIDs StateTypeIDs PriorityIDs OwnerIDs ResponsibleIDs 1349 ) 1350 ) 1351 { 1352 next KEY if !$GetParam{$Key}; 1353 my $Attribute = $IDMap{$Key}->{Name} || $Key; 1354 my $Object = $IDMap{$Key}->{Object} || ''; 1355 my $Method = $IDMap{$Key}->{Method}; 1356 my $MethodKey = $IDMap{$Key}->{Key}; 1357 my $Translation = $IDMap{$Key}->{Translation}; 1358 my $Value; 1359 1360 # get appropriate object 1361 my $LookupObject; 1362 if ( $IDMap{$Key}->{Name} ) { 1363 $LookupObject = $Kernel::OM->Get( 'Kernel::System::' . $Object ); 1364 } 1365 1366 if ( ref $GetParam{$Key} eq 'ARRAY' ) { 1367 for my $ItemRaw ( @{ $GetParam{$Key} } ) { 1368 my $Item = $ItemRaw; 1369 if ($Value) { 1370 $Value .= '+'; 1371 } 1372 if ($LookupObject) { 1373 $Item = $LookupObject->$Method( $MethodKey => $Item ); 1374 if ($Translation) { 1375 $Item = $LayoutObject->{LanguageObject}->Translate($Item); 1376 } 1377 } 1378 $Value .= $Item; 1379 } 1380 } 1381 else { 1382 my $Item = $GetParam{$Key}; 1383 if ($LookupObject) { 1384 $Item = $LookupObject->$Method( $MethodKey => $Item ); 1385 if ($Translation) { 1386 $Item = $LayoutObject->{LanguageObject}->Translate($Item); 1387 } 1388 } 1389 $Value = $Item; 1390 } 1391 1392 if ( $Key eq 'TimeSearchType' ) { 1393 1394 if ( $GetParam{TimeSearchType} eq 'TimeSlot' ) { 1395 1396 my $StartDate = $LayoutObject->{LanguageObject}->FormatTimeString( 1397 $GetParam{TicketCreateTimeStartYear} 1398 . '-' . $GetParam{TicketCreateTimeStartMonth} 1399 . '-' . $GetParam{TicketCreateTimeStartDay} 1400 . ' 00:00:00', 'DateFormatShort' 1401 ); 1402 1403 my $StopDate = $LayoutObject->{LanguageObject}->FormatTimeString( 1404 $GetParam{TicketCreateTimeStopYear} 1405 . '-' . $GetParam{TicketCreateTimeStopMonth} 1406 . '-' . $GetParam{TicketCreateTimeStopDay} 1407 . ' 00:00:00', 'DateFormatShort' 1408 ); 1409 1410 $Attribute = 'Created between'; 1411 $Value = $StartDate . ' ' 1412 . $LayoutObject->{LanguageObject}->Translate('and') . ' ' 1413 . $StopDate; 1414 } 1415 else { 1416 1417 my $Mapping = { 1418 'Last' => Translatable('Created within the last'), 1419 'Before' => Translatable('Created more than ... ago'), 1420 }; 1421 1422 $Attribute = $Mapping->{ $GetParam{TicketCreateTimePointStart} }; 1423 $Value = $GetParam{TicketCreateTimePoint} . ' ' 1424 . $LayoutObject->{LanguageObject}->Translate( $GetParam{TicketCreateTimePointFormat} . '(s)' ); 1425 } 1426 } 1427 1428 $LayoutObject->Block( 1429 Name => 'SearchTerms', 1430 Data => { 1431 %Param, 1432 Attribute => $Attribute, 1433 Key => $Key, 1434 Value => $Value, 1435 }, 1436 ); 1437 } 1438 1439 # cycle through the activated Dynamic Fields for this screen 1440 DYNAMICFIELD: 1441 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1442 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1443 next DYNAMICFIELD 1444 if !$DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} }; 1445 1446 $LayoutObject->Block( 1447 Name => 'SearchTerms', 1448 Data => { 1449 Attribute => $DynamicFieldConfig->{Label}, 1450 Value => 1451 $DynamicFieldSearchDisplay{ 'DynamicField_' . $DynamicFieldConfig->{Name} }, 1452 }, 1453 ); 1454 } 1455 1456 my $Link = 'Profile=' . $LayoutObject->LinkEncode($Profile) . ';'; 1457 $Link .= 'SortBy=' . $LayoutObject->LinkEncode($SortBy) . ';'; 1458 $Link .= 'Order=' . $LayoutObject->LinkEncode($CurrentOrder) . ';'; 1459 $Link .= 'TakeLastSearch=1;'; 1460 1461 # build search navigation bar 1462 my %PageNav = $LayoutObject->PageNavBar( 1463 Limit => $SearchLimit, 1464 StartHit => $StartHit, 1465 PageShown => $SearchPageShown, 1466 AllHits => $Counter, 1467 Action => "Action=CustomerTicketSearch;Subaction=Search", 1468 Link => $Link, 1469 IDPrefix => "CustomerTicketSearch", 1470 ); 1471 1472 # show footer filter - show only if more the one page is available 1473 if ( $SearchLimit && ( $SearchLimit > $SearchPageShown ) ) { 1474 $LayoutObject->Block( 1475 Name => 'Pagination', 1476 Data => { 1477 %Param, 1478 %PageNav, 1479 }, 1480 ); 1481 } 1482 1483 my $Order = 'Down'; 1484 if ( $CurrentOrder eq 'Down' ) { 1485 $Order = 'Up'; 1486 } 1487 my $Sort = ''; 1488 my $StateSort = ''; 1489 my $TicketSort = ''; 1490 my $AgeSort = ''; 1491 1492 # define sort order 1493 if ( $Order eq 'Down' ) { 1494 $Sort = 'SortAscending'; 1495 } 1496 if ( $Order eq 'Up' ) { 1497 $Sort = 'SortDescending'; 1498 } 1499 1500 if ( $SortBy eq 'State' ) { 1501 $StateSort = $Sort; 1502 } 1503 if ( $SortBy eq 'Ticket' ) { 1504 $TicketSort = $Sort; 1505 } 1506 if ( $SortBy eq 'Age' ) { 1507 $AgeSort = $Sort; 1508 } 1509 1510 # start html page 1511 my $Output = $LayoutObject->CustomerHeader(); 1512 $Output .= $LayoutObject->CustomerNavigationBar(); 1513 $Output .= $LayoutObject->Output( 1514 TemplateFile => 'CustomerTicketSearchResultShort', 1515 Data => { 1516 %Param, 1517 %PageNav, 1518 Order => $Order, 1519 StateSort => $StateSort, 1520 TicketSort => $TicketSort, 1521 AgeSort => $AgeSort, 1522 Profile => $Profile, 1523 ProfileName => $ProfileName, 1524 }, 1525 ); 1526 1527 # build footer 1528 $Output .= $LayoutObject->CustomerFooter(); 1529 return $Output; 1530 } 1531 1532 # empty search site 1533 else { 1534 1535 # delete profile 1536 if ( $EraseTemplate && $Profile ) { 1537 1538 # remove old profile stuff 1539 $SearchProfileObject->SearchProfileDelete( 1540 Base => 'CustomerTicketSearch', 1541 Name => $Profile, 1542 UserLogin => $Self->{UserLogin}, 1543 ); 1544 %GetParam = (); 1545 $Profile = ''; 1546 } 1547 1548 # create HTML strings for all dynamic fields 1549 my %DynamicFieldHTML; 1550 1551 # cycle through the activated Dynamic Fields for this screen 1552 DYNAMICFIELD: 1553 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1554 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1555 1556 my $PossibleValuesFilter; 1557 1558 my $IsACLReducible = $BackendObject->HasBehavior( 1559 DynamicFieldConfig => $DynamicFieldConfig, 1560 Behavior => 'IsACLReducible', 1561 ); 1562 1563 if ($IsACLReducible) { 1564 1565 # get PossibleValues 1566 my $PossibleValues = $BackendObject->PossibleValuesGet( 1567 DynamicFieldConfig => $DynamicFieldConfig, 1568 ); 1569 1570 # check if field has PossibleValues property in its configuration 1571 if ( IsHashRefWithData($PossibleValues) ) { 1572 1573 # get historical values from database 1574 my $HistoricalValues = $BackendObject->HistoricalValuesGet( 1575 DynamicFieldConfig => $DynamicFieldConfig, 1576 ); 1577 1578 my $Data = $PossibleValues; 1579 1580 # add historic values to current values (if they don't exist anymore) 1581 if ( IsHashRefWithData($HistoricalValues) ) { 1582 for my $Key ( sort keys %{$HistoricalValues} ) { 1583 if ( !$Data->{$Key} ) { 1584 $Data->{$Key} = $HistoricalValues->{$Key}; 1585 } 1586 } 1587 } 1588 1589 # convert possible values key => value to key => key for ACLs using a Hash slice 1590 my %AclData = %{$Data}; 1591 @AclData{ keys %AclData } = keys %AclData; 1592 1593 # set possible values filter from ACLs 1594 my $ACL = $TicketObject->TicketAcl( 1595 Action => $Self->{Action}, 1596 ReturnType => 'Ticket', 1597 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 1598 Data => \%AclData, 1599 CustomerUserID => $Self->{UserID}, 1600 ); 1601 if ($ACL) { 1602 my %Filter = $TicketObject->TicketAclData(); 1603 1604 # convert Filer key => key back to key => value using map 1605 %{$PossibleValuesFilter} = map { $_ => $Data->{$_} } keys %Filter; 1606 } 1607 } 1608 } 1609 1610 # get search field preferences 1611 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 1612 DynamicFieldConfig => $DynamicFieldConfig, 1613 ); 1614 1615 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1616 1617 PREFERENCE: 1618 for my $Preference ( @{$SearchFieldPreferences} ) { 1619 1620 # get field html 1621 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 1622 = $BackendObject->SearchFieldRender( 1623 DynamicFieldConfig => $DynamicFieldConfig, 1624 Profile => \%GetParam, 1625 PossibleValuesFilter => $PossibleValuesFilter, 1626 DefaultValue => 1627 $Config->{Defaults}->{DynamicField} 1628 ->{ $DynamicFieldConfig->{Name} }, 1629 LayoutObject => $LayoutObject, 1630 ConfirmationCheckboxes => 1, 1631 Type => $Preference->{Type}, 1632 ); 1633 } 1634 } 1635 1636 # generate search mask 1637 my $Output = $LayoutObject->CustomerHeader(); 1638 $Output .= $LayoutObject->CustomerNavigationBar(); 1639 $Output .= $Self->MaskForm( 1640 %GetParam, 1641 Profile => $Profile, 1642 Area => 'Customer', 1643 DynamicFieldHTML => \%DynamicFieldHTML, 1644 %ServerErrors, 1645 ); 1646 $Output .= $LayoutObject->CustomerFooter(); 1647 return $Output; 1648 } 1649} 1650 1651sub MaskForm { 1652 my ( $Self, %Param ) = @_; 1653 1654 # get config object 1655 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 1656 1657 # get list type 1658 my $TreeView = 0; 1659 if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { 1660 $TreeView = 1; 1661 } 1662 1663 # get layout object 1664 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 1665 1666 $Param{ResultFormStrg} = $LayoutObject->BuildSelection( 1667 Data => { 1668 Normal => 'Normal', 1669 Print => 'Print', 1670 CSV => 'CSV', 1671 Excel => 'Excel', 1672 }, 1673 Name => 'ResultForm', 1674 SelectedID => $Param{ResultForm} || 'Normal', 1675 Class => 'Modernize', 1676 ); 1677 1678 my %Profiles = $Kernel::OM->Get('Kernel::System::SearchProfile')->SearchProfileList( 1679 Base => 'CustomerTicketSearch', 1680 UserLogin => $Self->{UserLogin}, 1681 ); 1682 1683 if ( $Profiles{'last-search'} ) { 1684 $Profiles{'last-search'} = $LayoutObject->{LanguageObject}->Translate('last-search'); 1685 } 1686 1687 $Param{ProfilesStrg} = $LayoutObject->BuildSelection( 1688 Data => \%Profiles, 1689 Translation => 0, 1690 Name => 'Profile', 1691 SelectedID => $Param{Profile}, 1692 Class => 'Modernize', 1693 PossibleNone => 1, 1694 ); 1695 1696 my %Customers = $Kernel::OM->Get('Kernel::System::CustomerGroup')->GroupContextCustomers( 1697 CustomerUserID => $Self->{UserID}, 1698 ); 1699 1700 $Param{CustomerIDStrg} = $LayoutObject->BuildSelection( 1701 Data => \%Customers, 1702 Name => 'CustomerID', 1703 Multiple => 1, 1704 Size => 5, 1705 SelectedID => $Param{CustomerID}, 1706 Class => 'Modernize', 1707 ); 1708 1709 # get service object 1710 my $ServiceObject = $Kernel::OM->Get('Kernel::System::Service'); 1711 1712 my %ServiceList; 1713 if ( $ConfigObject->Get('Customer::TicketSearch::AllServices') ) { 1714 %ServiceList = $ServiceObject->ServiceList( 1715 UserID => $Self->{UserID}, 1716 ); 1717 } 1718 else { 1719 %ServiceList = $ServiceObject->CustomerUserServiceMemberList( 1720 CustomerUserLogin => $Self->{UserID}, 1721 Result => 'HASH', 1722 ); 1723 } 1724 1725 $Param{ServicesStrg} = $LayoutObject->BuildSelection( 1726 Data => \%ServiceList, 1727 Name => 'ServiceIDs', 1728 Multiple => 1, 1729 Size => 5, 1730 SelectedID => $Param{ServiceIDs}, 1731 TreeView => $TreeView, 1732 Class => 'Modernize', 1733 ); 1734 $Param{TypesStrg} = $LayoutObject->BuildSelection( 1735 Data => { 1736 $Kernel::OM->Get('Kernel::System::Type')->TypeList( 1737 UserID => $Self->{UserID}, 1738 ), 1739 }, 1740 Name => 'TypeIDs', 1741 SelectedID => $Param{TypeIDs}, 1742 Multiple => 1, 1743 Size => 5, 1744 SelectedID => $Param{TypeIDs}, 1745 Class => 'Modernize', 1746 ); 1747 $Param{StatesStrg} = $LayoutObject->BuildSelection( 1748 Data => { 1749 $Kernel::OM->Get('Kernel::System::State')->StateList( 1750 UserID => $Self->{UserID}, 1751 Action => $Self->{Action}, 1752 ), 1753 }, 1754 Name => 'StateIDs', 1755 Multiple => 1, 1756 Size => 5, 1757 SelectedID => $Param{StateIDs}, 1758 Class => 'Modernize', 1759 ); 1760 $Param{StateTypeStrg} = $LayoutObject->BuildSelection( 1761 Data => { 1762 Open => 'open', 1763 Closed => 'closed', 1764 }, 1765 Name => 'StateType', 1766 Size => 5, 1767 SelectedID => $Param{StateType}, 1768 Class => 'Modernize', 1769 ); 1770 $Param{PrioritiesStrg} = $LayoutObject->BuildSelection( 1771 Data => { 1772 $Kernel::OM->Get('Kernel::System::Priority')->PriorityList( 1773 UserID => $Self->{UserID}, 1774 Action => $Self->{Action}, 1775 ), 1776 }, 1777 Name => 'PriorityIDs', 1778 Multiple => 1, 1779 Size => 5, 1780 SelectedID => $Param{PriorityIDs}, 1781 Class => 'Modernize', 1782 ); 1783 $Param{TicketCreateTimePoint} = $LayoutObject->BuildSelection( 1784 Data => { 1785 1 => ' 1', 1786 2 => ' 2', 1787 3 => ' 3', 1788 4 => ' 4', 1789 5 => ' 5', 1790 6 => ' 6', 1791 7 => ' 7', 1792 8 => ' 8', 1793 9 => ' 9', 1794 10 => '10', 1795 11 => '11', 1796 12 => '12', 1797 13 => '13', 1798 14 => '14', 1799 15 => '15', 1800 16 => '16', 1801 17 => '17', 1802 18 => '18', 1803 19 => '19', 1804 20 => '20', 1805 21 => '21', 1806 22 => '22', 1807 23 => '23', 1808 24 => '24', 1809 25 => '25', 1810 26 => '26', 1811 27 => '27', 1812 28 => '28', 1813 29 => '29', 1814 30 => '30', 1815 31 => '31', 1816 32 => '32', 1817 33 => '33', 1818 34 => '34', 1819 35 => '35', 1820 36 => '36', 1821 37 => '37', 1822 38 => '38', 1823 39 => '39', 1824 40 => '40', 1825 41 => '41', 1826 42 => '42', 1827 43 => '43', 1828 44 => '44', 1829 45 => '45', 1830 46 => '46', 1831 47 => '47', 1832 48 => '48', 1833 49 => '49', 1834 50 => '50', 1835 51 => '51', 1836 52 => '52', 1837 53 => '53', 1838 54 => '54', 1839 55 => '55', 1840 56 => '56', 1841 57 => '57', 1842 58 => '58', 1843 59 => '59', 1844 }, 1845 Translation => 0, 1846 Name => 'TicketCreateTimePoint', 1847 SelectedID => $Param{TicketCreateTimePoint}, 1848 ); 1849 $Param{TicketCreateTimePointStart} = $LayoutObject->BuildSelection( 1850 Data => { 1851 Last => Translatable('within the last ...'), 1852 Before => Translatable('more than ... ago'), 1853 }, 1854 Translation => 1, 1855 Name => 'TicketCreateTimePointStart', 1856 SelectedID => $Param{TicketCreateTimePointStart} || 'Last', 1857 ); 1858 $Param{TicketCreateTimePointFormat} = $LayoutObject->BuildSelection( 1859 Data => { 1860 minute => Translatable('minute(s)'), 1861 hour => Translatable('hour(s)'), 1862 day => Translatable('day(s)'), 1863 week => Translatable('week(s)'), 1864 month => Translatable('month(s)'), 1865 year => Translatable('year(s)'), 1866 }, 1867 Translation => 1, 1868 Name => 'TicketCreateTimePointFormat', 1869 SelectedID => $Param{TicketCreateTimePointFormat}, 1870 ); 1871 $Param{TicketCreateTimeStart} = $LayoutObject->BuildDateSelection( 1872 %Param, 1873 Prefix => 'TicketCreateTimeStart', 1874 TicketCreateTimeStartClass => 'DateSelection', 1875 Format => 'DateInputFormat', 1876 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 1877 ); 1878 $Param{TicketCreateTimeStop} = $LayoutObject->BuildDateSelection( 1879 %Param, 1880 Prefix => 'TicketCreateTimeStop', 1881 TicketCreateTimeStopClass => 'DateSelection', 1882 Format => 'DateInputFormat', 1883 ); 1884 1885 # html search mask output 1886 $LayoutObject->Block( 1887 Name => 'Search', 1888 Data => { %Param, }, 1889 ); 1890 1891 # create the fulltext field entries to be displayed 1892 my %ArticleSearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList(); 1893 1894 FIELD: 1895 for my $ArticleFieldKey ( 1896 sort { $ArticleSearchableFields{$a}->{Label} cmp $ArticleSearchableFields{$b}->{Label} } 1897 keys %ArticleSearchableFields 1898 ) 1899 { 1900 next FIELD if $ArticleSearchableFields{$ArticleFieldKey}->{HideInCustomerInterface}; 1901 1902 $LayoutObject->Block( 1903 Name => 'SearchableArticleField', 1904 Data => { 1905 ArticleFieldLabel => $ArticleSearchableFields{$ArticleFieldKey}->{Label}, 1906 ArticleFieldKey => $ArticleSearchableFields{$ArticleFieldKey}->{Key}, 1907 ArticleFieldValue => $Param{$ArticleFieldKey} // '', 1908 }, 1909 ); 1910 } 1911 1912 # enable archive search 1913 if ( 1914 $ConfigObject->Get('Ticket::ArchiveSystem') 1915 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') 1916 && $ConfigObject->Get('Ticket::CustomerArchiveSystem') eq 1 1917 ) 1918 { 1919 1920 $Param{SearchInArchiveStrg} = $LayoutObject->BuildSelection( 1921 Data => { 1922 ArchivedTickets => Translatable('Archived tickets'), 1923 NotArchivedTickets => Translatable('Unarchived tickets'), 1924 AllTickets => Translatable('All tickets'), 1925 }, 1926 Name => 'SearchInArchive', 1927 SelectedID => $Param{SearchInArchive} || 'NotArchivedTickets', 1928 Class => 'Modernize', 1929 ); 1930 1931 $LayoutObject->Block( 1932 Name => 'SearchInArchive', 1933 Data => { 1934 SearchInArchiveStrg => $Param{SearchInArchiveStrg}, 1935 }, 1936 ); 1937 } 1938 1939 my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}"); 1940 1941 # get dynamic field config for frontend module 1942 my $DynamicFieldFilter = $Config->{DynamicField}; 1943 1944 # get the dynamic fields for ticket object 1945 my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 1946 Valid => 1, 1947 ObjectType => ['Ticket'], 1948 FieldFilter => $DynamicFieldFilter || {}, 1949 ); 1950 1951 # get backend object 1952 my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 1953 1954 # reduce the dynamic fields to only the ones that are desinged for customer interface 1955 my @CustomerDynamicFields; 1956 DYNAMICFIELD: 1957 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1958 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1959 1960 my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior( 1961 DynamicFieldConfig => $DynamicFieldConfig, 1962 Behavior => 'IsCustomerInterfaceCapable', 1963 ); 1964 next DYNAMICFIELD if !$IsCustomerInterfaceCapable; 1965 1966 push @CustomerDynamicFields, $DynamicFieldConfig; 1967 } 1968 $DynamicField = \@CustomerDynamicFields; 1969 1970 # output Dynamic fields blocks 1971 # cycle through the activated Dynamic Fields for this screen 1972 DYNAMICFIELD: 1973 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1974 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1975 1976 # get search field preferences 1977 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 1978 DynamicFieldConfig => $DynamicFieldConfig, 1979 ); 1980 1981 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1982 1983 PREFERENCE: 1984 for my $Preference ( @{$SearchFieldPreferences} ) { 1985 1986 # skip fields that HTML could not be retrieved 1987 next PREFERENCE if !IsHashRefWithData( 1988 $Param{DynamicFieldHTML}->{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 1989 ); 1990 1991 $LayoutObject->Block( 1992 Name => 'DynamicField', 1993 Data => { 1994 Label => $Param{DynamicFieldHTML} 1995 ->{ $DynamicFieldConfig->{Name} . $Preference->{Type} }->{Label}, 1996 Field => $Param{DynamicFieldHTML} 1997 ->{ $DynamicFieldConfig->{Name} . $Preference->{Type} }->{Field}, 1998 }, 1999 ); 2000 } 2001 } 2002 2003 # html search mask output 2004 return $LayoutObject->Output( 2005 TemplateFile => 'CustomerTicketSearch', 2006 Data => \%Param, 2007 ); 2008} 2009 2010sub _StopWordsServerErrorsGet { 2011 my ( $Self, %Param ) = @_; 2012 2013 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 2014 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 2015 2016 if ( !%Param ) { 2017 $LayoutObject->FatalError( 2018 Message => Translatable('Got no values to check.'), 2019 ); 2020 } 2021 2022 my %StopWordsServerErrors; 2023 if ( !$ArticleObject->SearchStringStopWordsUsageWarningActive() ) { 2024 return %StopWordsServerErrors; 2025 } 2026 2027 my %SearchStrings; 2028 2029 FIELD: 2030 for my $Field ( sort keys %Param ) { 2031 next FIELD if !defined $Param{$Field}; 2032 next FIELD if !length $Param{$Field}; 2033 2034 $SearchStrings{$Field} = $Param{$Field}; 2035 } 2036 2037 if (%SearchStrings) { 2038 2039 my $StopWords = $ArticleObject->SearchStringStopWordsFind( 2040 SearchStrings => \%SearchStrings, 2041 ); 2042 2043 FIELD: 2044 for my $Field ( sort keys %{$StopWords} ) { 2045 next FIELD if !defined $StopWords->{$Field}; 2046 next FIELD if ref $StopWords->{$Field} ne 'ARRAY'; 2047 next FIELD if !@{ $StopWords->{$Field} }; 2048 2049 $StopWordsServerErrors{ $Field . 'Invalid' } = 'ServerError'; 2050 $StopWordsServerErrors{ $Field . 'InvalidTooltip' } = $LayoutObject->{LanguageObject} 2051 ->Translate('Please remove the following words because they cannot be used for the search:') 2052 . ' ' 2053 . join( ',', sort @{ $StopWords->{$Field} } ); 2054 } 2055 } 2056 2057 return %StopWordsServerErrors; 2058} 2059 20601; 2061