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::AgentTicketSearch; 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 $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 38 39 my $Config = $ConfigObject->Get("Ticket::Frontend::$Self->{Action}"); 40 41 # get config data 42 $Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 ); 43 $Self->{SearchLimit} = $Config->{SearchLimit} || 500; 44 $Self->{SortBy} = $ParamObject->GetParam( Param => 'SortBy' ) 45 || $Config->{'SortBy::Default'} 46 || 'Age'; 47 $Self->{OrderBy} = $ParamObject->GetParam( Param => 'OrderBy' ) 48 || $Config->{'Order::Default'} 49 || 'Down'; 50 $Self->{Profile} = $ParamObject->GetParam( Param => 'Profile' ) || ''; 51 $Self->{SaveProfile} = $ParamObject->GetParam( Param => 'SaveProfile' ) || ''; 52 $Self->{TakeLastSearch} = $ParamObject->GetParam( Param => 'TakeLastSearch' ) || ''; 53 $Self->{SelectTemplate} = $ParamObject->GetParam( Param => 'SelectTemplate' ) || ''; 54 $Self->{EraseTemplate} = $ParamObject->GetParam( Param => 'EraseTemplate' ) || ''; 55 56 # get list type 57 my $TreeView = 0; 58 if ( $ConfigObject->Get('Ticket::Frontend::ListType') eq 'tree' ) { 59 $TreeView = 1; 60 } 61 62 # get layout object 63 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 64 65 # check request 66 if ( $Self->{Subaction} eq 'OpenSearchDescriptionTicketNumber' ) { 67 my $Output = $LayoutObject->Output( 68 TemplateFile => 'AgentTicketSearchOpenSearchDescriptionTicketNumber', 69 Data => \%Param, 70 ); 71 return $LayoutObject->Attachment( 72 Filename => 'OpenSearchDescriptionTicketNumber.xml', 73 ContentType => 'application/opensearchdescription+xml', 74 Content => $Output, 75 Type => 'inline', 76 ); 77 } 78 if ( $Self->{Subaction} eq 'OpenSearchDescriptionFulltext' ) { 79 my $Output = $LayoutObject->Output( 80 TemplateFile => 'AgentTicketSearchOpenSearchDescriptionFulltext', 81 Data => \%Param, 82 ); 83 return $LayoutObject->Attachment( 84 Filename => 'OpenSearchDescriptionFulltext.xml', 85 ContentType => 'application/opensearchdescription+xml', 86 Content => $Output, 87 Type => 'inline', 88 ); 89 } 90 91 # Autocomplete is executed via AJAX request. 92 if ( $Self->{Subaction} eq 'AJAXAutocomplete' ) { 93 $LayoutObject->ChallengeTokenCheck(); 94 95 my $Skip = $ParamObject->GetParam( Param => 'Skip' ) || ''; 96 my $Search = $ParamObject->GetParam( Param => 'Term' ) || ''; 97 my $Filter = $ParamObject->GetParam( Param => 'Filter' ) || '{}'; 98 my $MaxResults = int( $ParamObject->GetParam( Param => 'MaxResults' ) || 20 ); 99 100 # Remove leading and trailing spaces from search term. 101 $Search =~ s{ \A \s* ( [^\s]+ ) \s* \z }{$1}xms; 102 103 # Parse passed search filter. 104 my $SearchFilter = $Kernel::OM->Get('Kernel::System::JSON')->Decode( 105 Data => $Filter, 106 ); 107 108 # Workaround, all auto completion requests get posted by UTF8 anyway. 109 # Convert any to 8bit string if application is not running in UTF8. 110 $Search = $Kernel::OM->Get('Kernel::System::Encode')->Convert2CharsetInternal( 111 Text => $Search, 112 From => 'utf-8', 113 ); 114 115 my @TicketIDs; 116 117 # Search for tickets by: 118 # - Ticket Number 119 # - Ticket Title 120 if ($Search) { 121 122 @TicketIDs = $TicketObject->TicketSearch( 123 %{$SearchFilter}, 124 TicketNumber => '%' . $Search . '%', 125 Limit => $MaxResults, 126 Result => 'ARRAY', 127 ArchiveFlags => ['n'], 128 UserID => $Self->{UserID}, 129 ); 130 131 if ( !@TicketIDs ) { 132 @TicketIDs = $TicketObject->TicketSearch( 133 %{$SearchFilter}, 134 Title => '%' . $Search . '%', 135 Limit => $MaxResults, 136 Result => 'ARRAY', 137 ArchiveFlags => ['n'], 138 UserID => $Self->{UserID}, 139 ); 140 } 141 } 142 143 my @Results; 144 145 # Include additional ticket information in results. 146 TICKET: 147 for my $TicketID (@TicketIDs) { 148 next TICKET if !$TicketID; 149 next TICKET if $TicketID eq $Skip; 150 151 my %Ticket = $TicketObject->TicketGet( 152 TicketID => $TicketID, 153 DynamicFields => 0, 154 UserID => $Self->{UserID}, 155 ); 156 157 next TICKET if !%Ticket; 158 159 push @Results, { 160 Key => $Ticket{TicketNumber}, 161 Value => $Ticket{TicketNumber} . ' ' . $Ticket{Title}, 162 }; 163 } 164 165 my $JSON = $LayoutObject->JSONEncode( 166 Data => \@Results || [], 167 ); 168 169 # Send JSON response. 170 return $LayoutObject->Attachment( 171 ContentType => 'application/json; charset=' . $LayoutObject->{Charset}, 172 Content => $JSON || '', 173 Type => 'inline', 174 NoCache => 1, 175 ); 176 } 177 178 # check request 179 if ( $ParamObject->GetParam( Param => 'SearchTemplate' ) && $Self->{Profile} ) { 180 my $Profile = $LayoutObject->LinkEncode( $Self->{Profile} ); 181 return $LayoutObject->Redirect( 182 OP => 183 "Action=AgentTicketSearch;Subaction=Search;TakeLastSearch=1;SaveProfile=1;Profile=$Profile" 184 ); 185 } 186 187 # get single params 188 my %GetParam; 189 190 # get needed objects 191 my $SearchProfileObject = $Kernel::OM->Get('Kernel::System::SearchProfile'); 192 my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField'); 193 my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 194 195 # get dynamic field config for frontend module 196 my $DynamicFieldFilter = $Config->{DynamicField}; 197 198 # get the dynamic fields for ticket object 199 my $DynamicField = $DynamicFieldObject->DynamicFieldListGet( 200 Valid => 1, 201 ObjectType => [ 'Ticket', 'Article' ], 202 FieldFilter => $DynamicFieldFilter || {}, 203 ); 204 205 # collect all searchable article field definitions and add the fields to the attributes array 206 my %ArticleSearchableFields = $Kernel::OM->Get('Kernel::System::Ticket::Article')->ArticleSearchableFieldsList(); 207 208 # load profiles string params (press load profile) 209 if ( ( $Self->{Subaction} eq 'LoadProfile' && $Self->{Profile} ) || $Self->{TakeLastSearch} ) { 210 %GetParam = $SearchProfileObject->SearchProfileGet( 211 Base => 'TicketSearch', 212 Name => $Self->{Profile}, 213 UserLogin => $Self->{UserLogin}, 214 ); 215 216 # convert attributes 217 if ( $GetParam{ShownAttributes} && ref $GetParam{ShownAttributes} eq 'ARRAY' ) { 218 $GetParam{ShownAttributes} = join ';', @{ $GetParam{ShownAttributes} }; 219 } 220 } 221 222 # get search string params (get submitted params) 223 else { 224 for my $Key ( 225 sort keys %ArticleSearchableFields, 226 qw( 227 TicketNumber Title CustomerID CustomerIDRaw CustomerUserLogin CustomerUserLoginRaw 228 CustomerUserID StateType Agent ResultForm TimeSearchType ChangeTimeSearchType 229 CloseTimeSearchType LastChangeTimeSearchType EscalationTimeSearchType PendingTimeSearchType 230 UseSubQueues ArticleTimeSearchType SearchInArchive Fulltext ShownAttributes 231 ArticleCreateTimePointFormat ArticleCreateTimePoint ArticleCreateTimePointStart 232 ArticleCreateTimeStart ArticleCreateTimeStartDay ArticleCreateTimeStartMonth 233 ArticleCreateTimeStartYear ArticleCreateTimeStop ArticleCreateTimeStopDay 234 ArticleCreateTimeStopMonth ArticleCreateTimeStopYear 235 TicketCreateTimePointFormat TicketCreateTimePoint 236 TicketCreateTimePointStart 237 TicketCreateTimeStart TicketCreateTimeStartDay TicketCreateTimeStartMonth 238 TicketCreateTimeStartYear 239 TicketCreateTimeStop TicketCreateTimeStopDay TicketCreateTimeStopMonth 240 TicketCreateTimeStopYear 241 TicketChangeTimePointFormat TicketChangeTimePoint 242 TicketChangeTimePointStart 243 TicketChangeTimeStart TicketChangeTimeStartDay TicketChangeTimeStartMonth 244 TicketChangeTimeStartYear 245 TicketChangeTimeStop TicketChangeTimeStopDay TicketChangeTimeStopMonth 246 TicketChangeTimeStopYear 247 TicketLastChangeTimePointFormat TicketLastChangeTimePoint 248 TicketLastChangeTimePointStart 249 TicketLastChangeTimeStart TicketLastChangeTimeStartDay TicketLastChangeTimeStartMonth 250 TicketLastChangeTimeStartYear 251 TicketLastChangeTimeStop TicketLastChangeTimeStopDay TicketLastChangeTimeStopMonth 252 TicketLastChangeTimeStopYear 253 TicketCloseTimePointFormat TicketCloseTimePoint 254 TicketCloseTimePointStart 255 TicketCloseTimeStart TicketCloseTimeStartDay TicketCloseTimeStartMonth 256 TicketCloseTimeStartYear 257 TicketCloseTimeStop TicketCloseTimeStopDay TicketCloseTimeStopMonth 258 TicketCloseTimeStopYear 259 TicketPendingTimePointFormat TicketPendingTimePoint 260 TicketPendingTimePointStart 261 TicketPendingTimeStart TicketPendingTimeStartDay TicketPendingTimeStartMonth 262 TicketPendingTimeStartYear 263 TicketPendingTimeStop TicketPendingTimeStopDay TicketPendingTimeStopMonth 264 TicketPendingTimeStopYear 265 TicketEscalationTimePointFormat TicketEscalationTimePoint 266 TicketEscalationTimePointStart 267 TicketEscalationTimeStart TicketEscalationTimeStartDay TicketEscalationTimeStartMonth 268 TicketEscalationTimeStartYear 269 TicketEscalationTimeStop TicketEscalationTimeStopDay TicketEscalationTimeStopMonth 270 TicketEscalationTimeStopYear 271 TicketCloseTimeNewerDate TicketCloseTimeOlderDate 272 ) 273 ) 274 { 275 # get search string params (get submitted params) 276 $GetParam{$Key} = $ParamObject->GetParam( Param => $Key ); 277 278 # remove white space on the start and end 279 if ( $GetParam{$Key} ) { 280 $GetParam{$Key} =~ s/\s+$//g; 281 $GetParam{$Key} =~ s/^\s+//g; 282 } 283 } 284 285 # get array params 286 for my $Key ( 287 qw(StateIDs States StateTypeIDs QueueIDs Queues PriorityIDs Priorities OwnerIDs 288 CreatedQueueIDs CreatedUserIDs WatchUserIDs ResponsibleIDs 289 TypeIDs Types ServiceIDs Services SLAIDs SLAs LockIDs Locks) 290 ) 291 { 292 293 # get search array params (get submitted params) 294 my @Array = $ParamObject->GetArray( Param => $Key ); 295 if (@Array) { 296 $GetParam{$Key} = \@Array; 297 } 298 } 299 300 # get Dynamic fields from param object 301 # cycle trough the activated Dynamic Fields for this screen 302 DYNAMICFIELD: 303 for my $DynamicFieldConfig ( @{$DynamicField} ) { 304 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 305 306 # get search field preferences 307 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 308 DynamicFieldConfig => $DynamicFieldConfig, 309 ); 310 311 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 312 313 PREFERENCE: 314 for my $Preference ( @{$SearchFieldPreferences} ) { 315 316 # extract the dynamic field value from the web request 317 my $DynamicFieldValue = $BackendObject->SearchFieldValueGet( 318 DynamicFieldConfig => $DynamicFieldConfig, 319 ParamObject => $ParamObject, 320 ReturnProfileStructure => 1, 321 LayoutObject => $LayoutObject, 322 Type => $Preference->{Type}, 323 ); 324 325 # set the complete value structure in GetParam to store it later in the search profile 326 if ( IsHashRefWithData($DynamicFieldValue) ) { 327 %GetParam = ( %GetParam, %{$DynamicFieldValue} ); 328 } 329 } 330 } 331 } 332 333 # get article create time option 334 if ( !$GetParam{ArticleTimeSearchType} ) { 335 $GetParam{'ArticleTimeSearchType::None'} = 1; 336 } 337 elsif ( $GetParam{ArticleTimeSearchType} eq 'TimePoint' ) { 338 $GetParam{'ArticleTimeSearchType::TimePoint'} = 1; 339 } 340 elsif ( $GetParam{ArticleTimeSearchType} eq 'TimeSlot' ) { 341 $GetParam{'ArticleTimeSearchType::TimeSlot'} = 1; 342 } 343 344 # get create time option 345 if ( !$GetParam{TimeSearchType} ) { 346 $GetParam{'TimeSearchType::None'} = 1; 347 } 348 elsif ( $GetParam{TimeSearchType} eq 'TimePoint' ) { 349 $GetParam{'TimeSearchType::TimePoint'} = 1; 350 } 351 elsif ( $GetParam{TimeSearchType} eq 'TimeSlot' ) { 352 $GetParam{'TimeSearchType::TimeSlot'} = 1; 353 } 354 355 # get change time option 356 if ( !$GetParam{ChangeTimeSearchType} ) { 357 $GetParam{'ChangeTimeSearchType::None'} = 1; 358 } 359 elsif ( $GetParam{ChangeTimeSearchType} eq 'TimePoint' ) { 360 $GetParam{'ChangeTimeSearchType::TimePoint'} = 1; 361 } 362 elsif ( $GetParam{ChangeTimeSearchType} eq 'TimeSlot' ) { 363 $GetParam{'ChangeTimeSearchType::TimeSlot'} = 1; 364 } 365 366 # get last change time option 367 if ( !$GetParam{LastChangeTimeSearchType} ) { 368 $GetParam{'LastChangeTimeSearchType::None'} = 1; 369 } 370 elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimePoint' ) { 371 $GetParam{'LastChangeTimeSearchType::TimePoint'} = 1; 372 } 373 elsif ( $GetParam{LastChangeTimeSearchType} eq 'TimeSlot' ) { 374 $GetParam{'LastChangeTimeSearchType::TimeSlot'} = 1; 375 } 376 377 # get close time option 378 if ( !$GetParam{PendingTimeSearchType} ) { 379 $GetParam{'PendingTimeSearchType::None'} = 1; 380 } 381 elsif ( $GetParam{PendingTimeSearchType} eq 'TimePoint' ) { 382 $GetParam{'PendingTimeSearchType::TimePoint'} = 1; 383 } 384 elsif ( $GetParam{PendingTimeSearchType} eq 'TimeSlot' ) { 385 $GetParam{'PendingTimeSearchType::TimeSlot'} = 1; 386 } 387 388 # get close time option 389 if ( !$GetParam{CloseTimeSearchType} ) { 390 $GetParam{'CloseTimeSearchType::None'} = 1; 391 } 392 elsif ( $GetParam{CloseTimeSearchType} eq 'TimePoint' ) { 393 $GetParam{'CloseTimeSearchType::TimePoint'} = 1; 394 } 395 elsif ( $GetParam{CloseTimeSearchType} eq 'TimeSlot' ) { 396 $GetParam{'CloseTimeSearchType::TimeSlot'} = 1; 397 } 398 399 # get escalation time option 400 if ( !$GetParam{EscalationTimeSearchType} ) { 401 $GetParam{'EscalationTimeSearchType::None'} = 1; 402 } 403 elsif ( $GetParam{EscalationTimeSearchType} eq 'TimePoint' ) { 404 $GetParam{'EscalationTimeSearchType::TimePoint'} = 1; 405 } 406 elsif ( $GetParam{EscalationTimeSearchType} eq 'TimeSlot' ) { 407 $GetParam{'EscalationTimeSearchType::TimeSlot'} = 1; 408 } 409 410 # set result form env 411 if ( !$GetParam{ResultForm} ) { 412 $GetParam{ResultForm} = ''; 413 } 414 415 # get needed objects 416 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 417 my $StateObject = $Kernel::OM->Get('Kernel::System::State'); 418 419 # show result site 420 if ( $Self->{Subaction} eq 'Search' && !$Self->{EraseTemplate} ) { 421 422 $Self->{ProfileName} = ''; 423 if ( $Self->{Profile} ) { 424 $Self->{ProfileName} = "($Self->{Profile})"; 425 } 426 427 # fill up profile name (e.g. with last-search) 428 if ( !$Self->{Profile} || !$Self->{SaveProfile} ) { 429 $Self->{Profile} = Translatable('last-search'); 430 } 431 432 if ( !$Self->{ProfileName} ) { 433 $Self->{ProfileName} = "($Self->{Profile})"; 434 } 435 436 if ( $Self->{ProfileName} eq '(last-search)' ) { 437 $Self->{ProfileName} = '(' . $LayoutObject->{LanguageObject}->Translate('last-search') . ')'; 438 } 439 440 # save search profile (under last-search or real profile name) 441 $Self->{SaveProfile} = 1; 442 443 # remember last search values 444 if ( $Self->{SaveProfile} && $Self->{Profile} ) { 445 446 # remove old profile stuff 447 $SearchProfileObject->SearchProfileDelete( 448 Base => 'TicketSearch', 449 Name => $Self->{Profile}, 450 UserLogin => $Self->{UserLogin}, 451 ); 452 453 # convert attributes 454 if ( $GetParam{ShownAttributes} && ref $GetParam{ShownAttributes} eq '' ) { 455 $GetParam{ShownAttributes} = [ split /;/, $GetParam{ShownAttributes} ]; 456 } 457 458 # replace StateType to StateIDs 459 if ( $GetParam{StateType} ) { 460 my @StateIDs; 461 462 if ( $GetParam{StateType} eq 'Open' ) { 463 @StateIDs = $StateObject->StateGetStatesByType( 464 Type => 'Viewable', 465 Result => 'ID', 466 ); 467 } 468 elsif ( $GetParam{StateType} eq 'Closed' ) { 469 my %ViewableStateOpenLookup = $StateObject->StateGetStatesByType( 470 Type => 'Viewable', 471 Result => 'HASH', 472 ); 473 474 my %StateList = $StateObject->StateList( UserID => $Self->{UserID} ); 475 for my $Item ( sort keys %StateList ) { 476 if ( !$ViewableStateOpenLookup{$Item} ) { 477 push @StateIDs, $Item; 478 } 479 } 480 } 481 482 # current ticket state type 483 else { 484 @StateIDs = $StateObject->StateGetStatesByType( 485 StateType => $GetParam{StateType}, 486 Result => 'ID', 487 ); 488 } 489 490 # merge with StateIDs 491 if ( @StateIDs && IsArrayRefWithData( $GetParam{StateIDs} ) ) { 492 my %StateIDs = map { $_ => 1 } @StateIDs; 493 @StateIDs = grep { exists $StateIDs{$_} } @{ $GetParam{StateIDs} }; 494 } 495 496 if (@StateIDs) { 497 $GetParam{StateIDs} = \@StateIDs; 498 } 499 } 500 501 # insert new profile params 502 KEY: 503 for my $Key ( sort keys %GetParam ) { 504 next KEY if !defined $GetParam{$Key}; 505 next KEY if $Key eq 'StateType'; 506 $SearchProfileObject->SearchProfileAdd( 507 Base => 'TicketSearch', 508 Name => $Self->{Profile}, 509 Key => $Key, 510 Value => $GetParam{$Key}, 511 UserLogin => $Self->{UserLogin}, 512 ); 513 } 514 } 515 516 my %TimeMap = ( 517 ArticleCreate => 'ArticleTime', 518 TicketCreate => 'Time', 519 TicketChange => 'ChangeTime', 520 TicketLastChange => 'LastChangeTime', 521 TicketClose => 'CloseTime', 522 TicketPending => 'PendingTime', 523 TicketEscalation => 'EscalationTime', 524 ); 525 526 for my $TimeType ( sort keys %TimeMap ) { 527 528 # get create time settings 529 if ( !$GetParam{ $TimeMap{$TimeType} . 'SearchType' } ) { 530 531 # do nothing with time stuff 532 } 533 elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimeSlot' ) { 534 for my $Key (qw(Month Day)) { 535 $GetParam{ $TimeType . 'TimeStart' . $Key } 536 = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStart' . $Key } ); 537 $GetParam{ $TimeType . 'TimeStop' . $Key } 538 = sprintf( "%02d", $GetParam{ $TimeType . 'TimeStop' . $Key } ); 539 } 540 if ( 541 $GetParam{ $TimeType . 'TimeStartDay' } 542 && $GetParam{ $TimeType . 'TimeStartMonth' } 543 && $GetParam{ $TimeType . 'TimeStartYear' } 544 ) 545 { 546 my $DateTimeObject = $Kernel::OM->Create( 547 'Kernel::System::DateTime', 548 ObjectParams => { 549 Year => $GetParam{ $TimeType . 'TimeStartYear' }, 550 Month => $GetParam{ $TimeType . 'TimeStartMonth' }, 551 Day => $GetParam{ $TimeType . 'TimeStartDay' }, 552 Hour => 0, # midnight 553 Minute => 0, 554 Second => 0, 555 TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(), 556 }, 557 ); 558 559 # Convert start time to local system time zone. 560 $DateTimeObject->ToOTRSTimeZone(); 561 $GetParam{ $TimeType . 'TimeNewerDate' } = $DateTimeObject->ToString(); 562 } 563 if ( 564 $GetParam{ $TimeType . 'TimeStopDay' } 565 && $GetParam{ $TimeType . 'TimeStopMonth' } 566 && $GetParam{ $TimeType . 'TimeStopYear' } 567 ) 568 { 569 my $DateTimeObject = $Kernel::OM->Create( 570 'Kernel::System::DateTime', 571 ObjectParams => { 572 Year => $GetParam{ $TimeType . 'TimeStopYear' }, 573 Month => $GetParam{ $TimeType . 'TimeStopMonth' }, 574 Day => $GetParam{ $TimeType . 'TimeStopDay' }, 575 Hour => 23, # just before midnight 576 Minute => 59, 577 Second => 59, 578 TimeZone => $Self->{UserTimeZone} || Kernel::System::DateTime->UserDefaultTimeZoneGet(), 579 }, 580 ); 581 582 # Convert stop time to local system time zone. 583 $DateTimeObject->ToOTRSTimeZone(); 584 $GetParam{ $TimeType . 'TimeOlderDate' } = $DateTimeObject->ToString(); 585 } 586 } 587 elsif ( $GetParam{ $TimeMap{$TimeType} . 'SearchType' } eq 'TimePoint' ) { 588 if ( 589 $GetParam{ $TimeType . 'TimePoint' } 590 && $GetParam{ $TimeType . 'TimePointStart' } 591 && $GetParam{ $TimeType . 'TimePointFormat' } 592 ) 593 { 594 my $Time = 0; 595 if ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'minute' ) { 596 $Time = $GetParam{ $TimeType . 'TimePoint' }; 597 } 598 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'hour' ) { 599 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60; 600 } 601 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'day' ) { 602 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24; 603 } 604 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'week' ) { 605 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 7; 606 } 607 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'month' ) { 608 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 30; 609 } 610 elsif ( $GetParam{ $TimeType . 'TimePointFormat' } eq 'year' ) { 611 $Time = $GetParam{ $TimeType . 'TimePoint' } * 60 * 24 * 365; 612 } 613 if ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Before' ) { 614 615 # more than ... ago 616 $GetParam{ $TimeType . 'TimeOlderMinutes' } = $Time; 617 } 618 elsif ( $GetParam{ $TimeType . 'TimePointStart' } eq 'Next' ) { 619 620 # within next 621 $GetParam{ $TimeType . 'TimeNewerMinutes' } = 0; 622 $GetParam{ $TimeType . 'TimeOlderMinutes' } = -$Time; 623 } 624 elsif ( $GetParam{ $TimeType . 'TimePointStart' } eq 'After' ) { 625 626 # in more then ... 627 $GetParam{ $TimeType . 'TimeNewerMinutes' } = -$Time; 628 } 629 else { 630 631 # within last ... 632 $GetParam{ $TimeType . 'TimeOlderMinutes' } = 0; 633 $GetParam{ $TimeType . 'TimeNewerMinutes' } = $Time; 634 } 635 } 636 } 637 } 638 639 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 640 641 # Special behavior for the fulltext search toolbar module: 642 # - Check full text string to see if contents is a ticket number. 643 # - If exists and not in print or CSV mode, redirect to the ticket. 644 # See http://bugs.otrs.org/show_bug.cgi?id=4238 for details. 645 # The original problem was that tickets with customer reply will be 646 # found by a fulltext search (ticket number is in the subjects), but 647 # 'new' tickets will not be found. 648 if ( 649 $GetParam{Fulltext} 650 && $ParamObject->GetParam( Param => 'CheckTicketNumberAndRedirect' ) 651 && $GetParam{ResultForm} ne 'Normal' 652 && $GetParam{ResultForm} ne 'Print' 653 ) 654 { 655 my $TicketID = $TicketObject->TicketIDLookup( 656 TicketNumber => $GetParam{Fulltext}, 657 UserID => $Self->{UserID}, 658 ); 659 if ($TicketID) { 660 return $LayoutObject->Redirect( 661 OP => "Action=AgentTicketZoom;TicketID=$TicketID", 662 ); 663 } 664 } 665 666 # prepare archive flag 667 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 668 669 $GetParam{SearchInArchive} ||= ''; 670 if ( $GetParam{SearchInArchive} eq 'AllTickets' ) { 671 $GetParam{ArchiveFlags} = [ 'y', 'n' ]; 672 } 673 elsif ( $GetParam{SearchInArchive} eq 'ArchivedTickets' ) { 674 $GetParam{ArchiveFlags} = ['y']; 675 } 676 else { 677 $GetParam{ArchiveFlags} = ['n']; 678 } 679 } 680 681 my %AttributeLookup; 682 683 # create attribute lookup table 684 for my $Attribute ( @{ $GetParam{ShownAttributes} || [] } ) { 685 $AttributeLookup{$Attribute} = 1; 686 } 687 688 # dynamic fields search parameters for ticket search 689 my %DynamicFieldSearchParameters; 690 691 # cycle trough the activated Dynamic Fields for this screen 692 DYNAMICFIELD: 693 for my $DynamicFieldConfig ( @{$DynamicField} ) { 694 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 695 696 # get search field preferences 697 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 698 DynamicFieldConfig => $DynamicFieldConfig, 699 ); 700 701 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 702 703 PREFERENCE: 704 for my $Preference ( @{$SearchFieldPreferences} ) { 705 706 if ( 707 !$AttributeLookup{ 708 'LabelSearch_DynamicField_' 709 . $DynamicFieldConfig->{Name} 710 . $Preference->{Type} 711 } 712 ) 713 { 714 next PREFERENCE; 715 } 716 717 # extract the dynamic field value from the profile 718 my $SearchParameter = $BackendObject->SearchFieldParameterBuild( 719 DynamicFieldConfig => $DynamicFieldConfig, 720 Profile => \%GetParam, 721 LayoutObject => $LayoutObject, 722 Type => $Preference->{Type}, 723 ); 724 725 # set search parameter 726 if ( defined $SearchParameter ) { 727 $DynamicFieldSearchParameters{ 'DynamicField_' . $DynamicFieldConfig->{Name} } 728 = $SearchParameter->{Parameter}; 729 } 730 } 731 } 732 733 my @ViewableTicketIDs; 734 735 { 736 local $Kernel::System::DB::UseSlaveDB = 1; 737 738 # perform ticket search 739 @ViewableTicketIDs = $TicketObject->TicketSearch( 740 Result => 'ARRAY', 741 SortBy => $Self->{SortBy}, 742 OrderBy => $Self->{OrderBy}, 743 Limit => $Self->{SearchLimit}, 744 UserID => $Self->{UserID}, 745 ConditionInline => $Config->{ExtendedSearchCondition}, 746 ContentSearchPrefix => '*', 747 ContentSearchSuffix => '*', 748 FullTextIndex => 1, 749 %GetParam, 750 %DynamicFieldSearchParameters, 751 ); 752 } 753 754 # get needed objects 755 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 756 757 # get the ticket dynamic fields for CSV display 758 my $CSVDynamicField = $DynamicFieldObject->DynamicFieldListGet( 759 Valid => 1, 760 ObjectType => ['Ticket'], 761 FieldFilter => $Config->{SearchCSVDynamicField} || {}, 762 ); 763 764 # CSV and Excel output 765 if ( 766 $GetParam{ResultForm} eq 'CSV' 767 || 768 $GetParam{ResultForm} eq 'Excel' 769 ) 770 { 771 772 # create head (actual head and head for data fill) 773 my @TmpCSVHead = @{ $Config->{SearchCSVData} }; 774 my @CSVHead = @{ $Config->{SearchCSVData} }; 775 776 # include the selected dynamic fields in CVS results 777 DYNAMICFIELD: 778 for my $DynamicFieldConfig ( @{$CSVDynamicField} ) { 779 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 780 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 781 next DYNAMICFIELD if $DynamicFieldConfig->{Name} eq ''; 782 783 push @TmpCSVHead, 'DynamicField_' . $DynamicFieldConfig->{Name}; 784 push @CSVHead, $DynamicFieldConfig->{Label}; 785 } 786 787 my @CSVData; 788 for my $TicketID (@ViewableTicketIDs) { 789 790 # Get ticket data. 791 my %Ticket = $TicketObject->TicketGet( 792 TicketID => $TicketID, 793 DynamicFields => 1, 794 Extended => 1, 795 UserID => $Self->{UserID}, 796 ); 797 798 # Get last customer article. 799 my @Articles = $ArticleObject->ArticleList( 800 TicketID => $TicketID, 801 SenderType => 'customer', 802 OnlyLast => 1, 803 ); 804 805 # If the ticket has no customer article, get the last agent article. 806 if ( !@Articles ) { 807 @Articles = $ArticleObject->ArticleList( 808 TicketID => $TicketID, 809 SenderType => 'agent', 810 OnlyLast => 1, 811 ); 812 } 813 814 # Finally, if everything failed, get latest article. 815 if ( !@Articles ) { 816 @Articles = $ArticleObject->ArticleList( 817 TicketID => $TicketID, 818 OnlyLast => 1, 819 ); 820 } 821 822 my %Article = $ArticleObject->BackendForArticle( %{ $Articles[0] } )->ArticleGet( 823 %{ $Articles[0] }, 824 DynamicFields => 1, 825 ); 826 827 my %Data; 828 829 if ( !%Article ) { 830 831 %Data = %Ticket; 832 833 # Set missing information. 834 $Data{Subject} = $Ticket{Title} || $LayoutObject->{LanguageObject}->Translate('Untitled'); 835 $Data{Body} = $LayoutObject->{LanguageObject}->Translate('This item has no articles yet.'); 836 $Data{From} = '--'; 837 } 838 else { 839 %Data = ( %Ticket, %Article ); 840 } 841 842 for my $Key (qw(State Lock)) { 843 $Data{$Key} = $LayoutObject->{LanguageObject}->Translate( $Data{$Key} ); 844 } 845 846 $Data{Age} = $LayoutObject->CustomerAge( 847 Age => $Data{Age}, 848 Space => ' ', 849 ); 850 851 # get whole article (if configured!) 852 if ( $Config->{SearchArticleCSVTree} ) { 853 my @Articles = $ArticleObject->ArticleList( 854 TicketID => $TicketID, 855 ); 856 857 if (@Articles) { 858 for my $Article (@Articles) { 859 my %ArticleData = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 860 TicketID => $TicketID, 861 ArticleID => $Article->{ArticleID}, 862 DynamicFields => 0, 863 ); 864 if ( $ArticleData{Body} ) { 865 $Data{ArticleTree} 866 .= "\n-->" 867 . "||$ArticleData{SenderType}" 868 . "||$ArticleData{From}" 869 . "||$ArticleData{CreateTime}" 870 . "||<--------------\n" 871 . $Article{Body}; 872 } 873 } 874 } 875 else { 876 $Data{ArticleTree} .= $LayoutObject->{LanguageObject}->Translate( 877 'This item has no articles yet.' 878 ); 879 } 880 } 881 882 # customer info (customer name) 883 if ( $Data{CustomerUserID} ) { 884 $Data{CustomerName} = $CustomerUserObject->CustomerName( 885 UserLogin => $Data{CustomerUserID}, 886 ); 887 } 888 889 # user info 890 my %UserInfo = $UserObject->GetUserData( 891 User => $Data{Owner}, 892 ); 893 894 # merge row data 895 my %Info = ( 896 %Data, 897 %UserInfo, 898 AccountedTime => 899 $TicketObject->TicketAccountedTimeGet( TicketID => $TicketID ), 900 ); 901 902 # Transform EscalationTime and EscalationTimeWorkingTime to a human readable format. 903 # See bug#13088 (https://bugs.otrs.org/show_bug.cgi?id=13088). 904 $Info{EscalationTime} = $LayoutObject->CustomerAgeInHours( 905 Age => $Info{EscalationTime}, 906 Space => ' ', 907 ); 908 $Info{EscalationTimeWorkingTime} = $LayoutObject->CustomerAgeInHours( 909 Age => $Info{EscalationTimeWorkingTime}, 910 Space => ' ', 911 ); 912 913 my @Data; 914 for my $Header (@TmpCSVHead) { 915 916 # check if header is a dynamic field and get the value from dynamic field 917 # backend 918 if ( $Header =~ m{\A DynamicField_ ( [a-zA-Z\d]+ ) \z}xms ) { 919 920 # loop over the dynamic fields configured for CSV output 921 DYNAMICFIELD: 922 for my $DynamicFieldConfig ( @{$CSVDynamicField} ) { 923 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 924 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 925 926 # skip all fields that does not match with current field name ($1) 927 # with out the 'DynamicField_' prefix 928 next DYNAMICFIELD if $DynamicFieldConfig->{Name} ne $1; 929 930 # get the value as for print (to correctly display) 931 my $ValueStrg = $BackendObject->DisplayValueRender( 932 DynamicFieldConfig => $DynamicFieldConfig, 933 Value => $Info{$Header}, 934 HTMLOutput => 0, 935 LayoutObject => $LayoutObject, 936 ); 937 push @Data, $ValueStrg->{Value}; 938 939 # terminate the DYNAMICFIELD loop 940 last DYNAMICFIELD; 941 } 942 } 943 944 # otherwise retrieve data from article 945 else { 946 push @Data, $Info{$Header}; 947 } 948 } 949 push @CSVData, \@Data; 950 } 951 952 # get Separator from language file 953 my $UserCSVSeparator = $LayoutObject->{LanguageObject}->{Separator}; 954 955 if ( $ConfigObject->Get('PreferencesGroups')->{CSVSeparator}->{Active} ) { 956 my %UserData = $UserObject->GetUserData( UserID => $Self->{UserID} ); 957 $UserCSVSeparator = $UserData{UserCSVSeparator} if $UserData{UserCSVSeparator}; 958 } 959 960 my %HeaderMap = ( 961 TicketNumber => Translatable('Ticket Number'), 962 CustomerName => Translatable('Customer Name'), 963 ); 964 965 my @CSVHeadTranslated = map { $LayoutObject->{LanguageObject}->Translate( $HeaderMap{$_} || $_ ); } 966 @CSVHead; 967 968 my $CurDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 969 my $FileName = sprintf( 970 'ticket_search_%s', 971 $CurDateTimeObject->Format( 972 Format => '%Y-%m-%d_%H-%M' 973 ) 974 ); 975 976 # get CSV object 977 my $CSVObject = $Kernel::OM->Get('Kernel::System::CSV'); 978 979 # generate CSV output 980 if ( $GetParam{ResultForm} eq 'CSV' ) { 981 my $CSV = $CSVObject->Array2CSV( 982 Head => \@CSVHeadTranslated, 983 Data => \@CSVData, 984 Separator => $UserCSVSeparator, 985 ); 986 987 # return csv to download 988 return $LayoutObject->Attachment( 989 Filename => $FileName . '.csv', 990 ContentType => "text/csv; charset=" . $LayoutObject->{UserCharset}, 991 Content => $CSV, 992 ); 993 } 994 995 # generate Excel output 996 elsif ( $GetParam{ResultForm} eq 'Excel' ) { 997 my $Excel = $CSVObject->Array2CSV( 998 Head => \@CSVHeadTranslated, 999 Data => \@CSVData, 1000 Format => 'Excel', 1001 ); 1002 1003 # return Excel to download 1004 return $LayoutObject->Attachment( 1005 Filename => $FileName . '.xlsx', 1006 ContentType => 1007 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 1008 Content => $Excel, 1009 ); 1010 } 1011 } 1012 1013 # PDF output 1014 elsif ( $GetParam{ResultForm} eq 'Print' ) { 1015 1016 # get PDF object 1017 my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF'); 1018 1019 my @PDFData; 1020 for my $TicketID (@ViewableTicketIDs) { 1021 1022 # Get ticket data. 1023 my %Ticket = $TicketObject->TicketGet( 1024 TicketID => $TicketID, 1025 DynamicFields => 1, 1026 UserID => $Self->{UserID}, 1027 ); 1028 1029 # Get last customer article. 1030 my @Articles = $ArticleObject->ArticleList( 1031 TicketID => $TicketID, 1032 SenderType => 'customer', 1033 OnlyLast => 1, 1034 ); 1035 1036 # If the ticket has no customer article, get the last agent article. 1037 if ( !@Articles ) { 1038 @Articles = $ArticleObject->ArticleList( 1039 TicketID => $TicketID, 1040 SenderType => 'agent', 1041 OnlyLast => 1, 1042 ); 1043 } 1044 1045 # Finally, if everything failed, get latest article. 1046 if ( !@Articles ) { 1047 @Articles = $ArticleObject->ArticleList( 1048 TicketID => $TicketID, 1049 OnlyLast => 1, 1050 ); 1051 } 1052 1053 my %Article = $ArticleObject->BackendForArticle( %{ $Articles[0] } )->ArticleGet( 1054 %{ $Articles[0] }, 1055 DynamicFields => 1, 1056 ); 1057 1058 my %Data; 1059 1060 if ( !%Article ) { 1061 1062 %Data = %Ticket; 1063 1064 # Set missing information. 1065 $Data{Subject} = $Data{Title} || Translatable('Untitled'); 1066 $Data{From} = '--'; 1067 } 1068 else { 1069 %Data = ( %Ticket, %Article ); 1070 } 1071 1072 # customer info 1073 my %CustomerData; 1074 if ( $Data{CustomerUserID} ) { 1075 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1076 User => $Data{CustomerUserID}, 1077 ); 1078 } 1079 elsif ( $Data{CustomerID} ) { 1080 %CustomerData = $CustomerUserObject->CustomerUserDataGet( 1081 CustomerID => $Data{CustomerID}, 1082 ); 1083 } 1084 1085 # customer info (customer name) 1086 if ( $CustomerData{UserLogin} ) { 1087 $Data{CustomerName} = $CustomerUserObject->CustomerName( 1088 UserLogin => $CustomerData{UserLogin}, 1089 ); 1090 } 1091 1092 # user info 1093 my %UserInfo = $UserObject->GetUserData( 1094 User => $Data{Owner}, 1095 ); 1096 1097 # customer info string 1098 $UserInfo{CustomerName} = '(' . $UserInfo{CustomerName} . ')' 1099 if ( $UserInfo{CustomerName} ); 1100 1101 my %Info = ( %Data, %UserInfo ); 1102 my $Created = $LayoutObject->{LanguageObject}->FormatTimeString( 1103 $Data{CreateTime} // $Data{Created}, 1104 'DateFormat', 1105 ); 1106 my $Owner = "$Info{Owner} ($Info{UserFullname})"; 1107 my $Customer = "$Data{CustomerID} $Data{CustomerName}"; 1108 1109 my @PDFRow; 1110 push @PDFRow, $Data{TicketNumber}; 1111 push @PDFRow, $Created; 1112 push @PDFRow, $Data{From}; 1113 push @PDFRow, $Data{Subject}; 1114 push @PDFRow, $Data{State}; 1115 push @PDFRow, $Data{Queue}; 1116 push @PDFRow, $Owner; 1117 push @PDFRow, $Customer; 1118 push @PDFData, \@PDFRow; 1119 1120 } 1121 1122 my $Title = $LayoutObject->{LanguageObject}->Translate('Ticket') . ' ' 1123 . $LayoutObject->{LanguageObject}->Translate('Search'); 1124 my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by'); 1125 my $Page = $LayoutObject->{LanguageObject}->Translate('Page'); 1126 my $DateTimeString = $Kernel::OM->Create('Kernel::System::DateTime')->ToString(); 1127 my $Time = $LayoutObject->{LanguageObject}->FormatTimeString( 1128 $DateTimeString, 1129 'DateFormat', 1130 ); 1131 1132 # get maximum number of pages 1133 my $MaxPages = $ConfigObject->Get('PDF::MaxPages'); 1134 if ( !$MaxPages || $MaxPages < 1 || $MaxPages > 1000 ) { 1135 $MaxPages = 100; 1136 } 1137 1138 my $CellData; 1139 1140 # verify if there are tickets to show 1141 if (@PDFData) { 1142 1143 # create the header 1144 $CellData->[0]->[0]->{Content} = $ConfigObject->Get('Ticket::Hook'); 1145 $CellData->[0]->[0]->{Font} = 'ProportionalBold'; 1146 $CellData->[0]->[1]->{Content} = $LayoutObject->{LanguageObject}->Translate('Created'); 1147 $CellData->[0]->[1]->{Font} = 'ProportionalBold'; 1148 $CellData->[0]->[2]->{Content} = $LayoutObject->{LanguageObject}->Translate('From'); 1149 $CellData->[0]->[2]->{Font} = 'ProportionalBold'; 1150 $CellData->[0]->[3]->{Content} = $LayoutObject->{LanguageObject}->Translate('Subject'); 1151 $CellData->[0]->[3]->{Font} = 'ProportionalBold'; 1152 $CellData->[0]->[4]->{Content} = $LayoutObject->{LanguageObject}->Translate('State'); 1153 $CellData->[0]->[4]->{Font} = 'ProportionalBold'; 1154 $CellData->[0]->[5]->{Content} = $LayoutObject->{LanguageObject}->Translate('Queue'); 1155 $CellData->[0]->[5]->{Font} = 'ProportionalBold'; 1156 $CellData->[0]->[6]->{Content} = $LayoutObject->{LanguageObject}->Translate('Owner'); 1157 $CellData->[0]->[6]->{Font} = 'ProportionalBold'; 1158 $CellData->[0]->[7]->{Content} = $LayoutObject->{LanguageObject}->Translate('CustomerID'); 1159 $CellData->[0]->[7]->{Font} = 'ProportionalBold'; 1160 1161 # create the content array 1162 my $CounterRow = 1; 1163 for my $Row (@PDFData) { 1164 my $CounterColumn = 0; 1165 for my $Content ( @{$Row} ) { 1166 $CellData->[$CounterRow]->[$CounterColumn]->{Content} = $Content; 1167 $CounterColumn++; 1168 } 1169 $CounterRow++; 1170 } 1171 } 1172 1173 # otherwise, show 'No ticket data found' message 1174 else { 1175 $CellData->[0]->[0]->{Content} = $LayoutObject->{LanguageObject}->Translate('No ticket data found.'); 1176 } 1177 1178 # page params 1179 my %PageParam; 1180 $PageParam{PageOrientation} = 'landscape'; 1181 $PageParam{MarginTop} = 30; 1182 $PageParam{MarginRight} = 40; 1183 $PageParam{MarginBottom} = 40; 1184 $PageParam{MarginLeft} = 40; 1185 $PageParam{HeaderRight} = $Title; 1186 $PageParam{HeadlineLeft} = $Title; 1187 1188 # table params 1189 my %TableParam; 1190 $TableParam{CellData} = $CellData; 1191 $TableParam{Type} = 'Cut'; 1192 $TableParam{FontSize} = 6; 1193 $TableParam{Border} = 0; 1194 $TableParam{BackgroundColorEven} = '#DDDDDD'; 1195 $TableParam{Padding} = 1; 1196 $TableParam{PaddingTop} = 3; 1197 $TableParam{PaddingBottom} = 3; 1198 1199 # create new pdf document 1200 $PDFObject->DocumentNew( 1201 Title => $ConfigObject->Get('Product') . ': ' . $Title, 1202 Encode => $LayoutObject->{UserCharset}, 1203 ); 1204 1205 # start table output 1206 $PDFObject->PageNew( 1207 %PageParam, 1208 FooterRight => $Page . ' 1', 1209 ); 1210 1211 $PDFObject->PositionSet( 1212 Move => 'relativ', 1213 Y => -6, 1214 ); 1215 1216 # output title 1217 $PDFObject->Text( 1218 Text => $Title, 1219 FontSize => 13, 1220 ); 1221 1222 $PDFObject->PositionSet( 1223 Move => 'relativ', 1224 Y => -6, 1225 ); 1226 1227 # output "printed by" 1228 $PDFObject->Text( 1229 Text => $PrintedBy . ' ' 1230 . $Self->{UserFullname} . ' (' 1231 . $Self->{UserEmail} . ')' 1232 . ', ' . $Time, 1233 FontSize => 9, 1234 ); 1235 1236 $PDFObject->PositionSet( 1237 Move => 'relativ', 1238 Y => -14, 1239 ); 1240 1241 PAGE: 1242 for my $PageNumber ( 2 .. $MaxPages ) { 1243 1244 # output table (or a fragment of it) 1245 %TableParam = $PDFObject->Table(%TableParam); 1246 1247 # stop output or another page 1248 if ( $TableParam{State} ) { 1249 last PAGE; 1250 } 1251 else { 1252 $PDFObject->PageNew( 1253 %PageParam, 1254 FooterRight => $Page . ' ' . $PageNumber, 1255 ); 1256 } 1257 } 1258 1259 # return the pdf document 1260 my $CurDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime'); 1261 my $Filename = sprintf( 1262 'ticket_search_%s.pdf', 1263 $CurDateTimeObject->Format( 1264 Format => '%Y-%m-%d_%H-%M' 1265 ) 1266 ); 1267 1268 my $PDFString = $PDFObject->DocumentOutput(); 1269 return $LayoutObject->Attachment( 1270 Filename => $Filename, 1271 ContentType => "application/pdf", 1272 Content => $PDFString, 1273 Type => 'inline', 1274 ); 1275 } 1276 else { 1277 1278 # redirect to the ticketzoom if result of the search is only one 1279 if ( scalar @ViewableTicketIDs eq 1 && !$Self->{TakeLastSearch} ) { 1280 return $LayoutObject->Redirect( 1281 OP => "Action=AgentTicketZoom;TicketID=$ViewableTicketIDs[0]", 1282 ); 1283 } 1284 1285 # store last overview screen 1286 my $URL = "Action=AgentTicketSearch;Subaction=Search" 1287 . ";Profile=" . $LayoutObject->LinkEncode( $Self->{Profile} ) 1288 . ";SortBy=" . $LayoutObject->LinkEncode( $Self->{SortBy} ) 1289 . ";OrderBy=" . $LayoutObject->LinkEncode( $Self->{OrderBy} ) 1290 . ";TakeLastSearch=1;StartHit=" 1291 . $LayoutObject->LinkEncode( $Self->{StartHit} ); 1292 1293 $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID( 1294 SessionID => $Self->{SessionID}, 1295 Key => 'LastScreenOverview', 1296 Value => $URL, 1297 ); 1298 1299 # start HTML page 1300 my $Output = $LayoutObject->Header(); 1301 $Output .= $LayoutObject->NavigationBar(); 1302 1303 # Notify if there are tickets which are not updated. 1304 $Output .= $LayoutObject->NotifyNonUpdatedTickets() // ''; 1305 1306 $Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || ''; 1307 $Self->{View} = $ParamObject->GetParam( Param => 'View' ) || ''; 1308 1309 # show tickets 1310 my $LinkPage = 'Filter=' 1311 . $LayoutObject->LinkEncode( $Self->{Filter} ) 1312 . ';View=' . $LayoutObject->LinkEncode( $Self->{View} ) 1313 . ';SortBy=' . $LayoutObject->LinkEncode( $Self->{SortBy} ) 1314 . ';OrderBy=' . $LayoutObject->LinkEncode( $Self->{OrderBy} ) 1315 . ';Profile=' 1316 . $LayoutObject->LinkEncode( $Self->{Profile} ) 1317 . ';TakeLastSearch=1;Subaction=Search' 1318 . ';'; 1319 my $LinkSort = 'Filter=' 1320 . $LayoutObject->LinkEncode( $Self->{Filter} ) 1321 . ';View=' . $LayoutObject->LinkEncode( $Self->{View} ) 1322 . ';Profile=' 1323 . $LayoutObject->LinkEncode( $Self->{Profile} ) 1324 . ';TakeLastSearch=1;Subaction=Search' 1325 . ';'; 1326 my $LinkFilter = 'TakeLastSearch=1;Subaction=Search;Profile=' 1327 . $LayoutObject->LinkEncode( $Self->{Profile} ) 1328 . ';'; 1329 my $LinkBack = 'Subaction=LoadProfile;Profile=' 1330 . $LayoutObject->LinkEncode( $Self->{Profile} ) 1331 . ';TakeLastSearch=1&'; 1332 1333 my $FilterLink = 'SortBy=' . $LayoutObject->LinkEncode( $Self->{SortBy} ) 1334 . ';OrderBy=' . $LayoutObject->LinkEncode( $Self->{OrderBy} ) 1335 . ';View=' . $LayoutObject->LinkEncode( $Self->{View} ) 1336 . ';Profile=' 1337 . $LayoutObject->LinkEncode( $Self->{Profile} ) 1338 . ';TakeLastSearch=1;Subaction=Search' 1339 . ';'; 1340 $Output .= $LayoutObject->TicketListShow( 1341 TicketIDs => \@ViewableTicketIDs, 1342 Total => scalar @ViewableTicketIDs, 1343 1344 View => $Self->{View}, 1345 1346 Env => $Self, 1347 LinkPage => $LinkPage, 1348 LinkSort => $LinkSort, 1349 LinkFilter => $LinkFilter, 1350 LinkBack => $LinkBack, 1351 Profile => $Self->{Profile}, 1352 ProfileName => $Self->{ProfileName}, 1353 1354 TitleName => Translatable('Search Results'), 1355 Bulk => 1, 1356 Limit => $Self->{SearchLimit}, 1357 1358 Filter => $Self->{Filter}, 1359 1360 OrderBy => $Self->{OrderBy}, 1361 SortBy => $Self->{SortBy}, 1362 RequestedURL => 'Action=' . $Self->{Action} . ';' . $LinkPage, 1363 1364 # do not print the result earlier, but return complete content 1365 Output => 1, 1366 ); 1367 1368 # build footer 1369 $Output .= $LayoutObject->Footer(); 1370 return $Output; 1371 } 1372 } 1373 elsif ( $Self->{Subaction} eq 'AJAXProfileDelete' ) { 1374 my $Profile = $ParamObject->GetParam( Param => 'Profile' ); 1375 1376 # remove old profile stuff 1377 $SearchProfileObject->SearchProfileDelete( 1378 Base => 'TicketSearch', 1379 Name => $Profile, 1380 UserLogin => $Self->{UserLogin}, 1381 ); 1382 my $Output = $LayoutObject->JSONEncode( 1383 Data => 1, 1384 ); 1385 return $LayoutObject->Attachment( 1386 NoCache => 1, 1387 ContentType => 'text/html', 1388 Content => $Output, 1389 Type => 'inline' 1390 ); 1391 } 1392 elsif ( $Self->{Subaction} eq 'AJAXStopWordCheck' ) { 1393 1394 my $StopWordCheckResult = { 1395 FoundStopWords => [], 1396 }; 1397 1398 if ( $Kernel::OM->Get('Kernel::System::Ticket::Article')->SearchStringStopWordsUsageWarningActive() ) { 1399 my @ParamNames = $ParamObject->GetParamNames(); 1400 my %SearchStrings; 1401 SEARCHSTRINGPARAMNAME: 1402 for my $SearchStringParamName ( sort @ParamNames ) { 1403 next SEARCHSTRINGPARAMNAME if $SearchStringParamName !~ m{\ASearchStrings\[(.*)\]\z}sm; 1404 $SearchStrings{$1} = $ParamObject->GetParam( Param => $SearchStringParamName ); 1405 } 1406 1407 $StopWordCheckResult->{FoundStopWords} 1408 = $Kernel::OM->Get('Kernel::System::Ticket::Article')->SearchStringStopWordsFind( 1409 SearchStrings => \%SearchStrings, 1410 ); 1411 } 1412 1413 my $Output = $LayoutObject->JSONEncode( 1414 Data => $StopWordCheckResult, 1415 ); 1416 return $LayoutObject->Attachment( 1417 NoCache => 1, 1418 ContentType => 'text/html', 1419 Content => $Output, 1420 Type => 'inline' 1421 ); 1422 } 1423 elsif ( $Self->{Subaction} eq 'AJAX' ) { 1424 my $Profile = $ParamObject->GetParam( Param => 'Profile' ) || ''; 1425 my $EmptySearch = $ParamObject->GetParam( Param => 'EmptySearch' ); 1426 if ( !$Profile ) { 1427 $EmptySearch = 1; 1428 } 1429 my %GetParam = $SearchProfileObject->SearchProfileGet( 1430 Base => 'TicketSearch', 1431 Name => $Profile, 1432 UserLogin => $Self->{UserLogin}, 1433 ); 1434 1435 # convert attributes 1436 if ( IsArrayRefWithData( $GetParam{ShownAttributes} ) ) { 1437 my @ShowAttributes = grep {defined} @{ $GetParam{ShownAttributes} }; 1438 $GetParam{ShownAttributes} = join ';', @ShowAttributes; 1439 } 1440 1441 # if no profile is used, set default params of default attributes 1442 if ( !$Profile ) { 1443 if ( $Config->{Defaults} ) { 1444 KEY: 1445 for my $Key ( sort keys %{ $Config->{Defaults} } ) { 1446 next KEY if !$Config->{Defaults}->{$Key}; 1447 next KEY if $Key eq 'DynamicField'; 1448 1449 if ( $Key =~ /^(Ticket|Article)(Create|Change|Close|Escalation)/ ) { 1450 my @Items = split /;/, $Config->{Defaults}->{$Key}; 1451 for my $Item (@Items) { 1452 my ( $Key, $Value ) = split /=/, $Item; 1453 $GetParam{$Key} = $Value; 1454 } 1455 } 1456 else { 1457 $GetParam{$Key} = $Config->{Defaults}->{$Key}; 1458 } 1459 } 1460 } 1461 } 1462 my @Attributes = ( 1463 1464 # Main fields 1465 { 1466 Key => 'TicketNumber', 1467 Value => Translatable('Ticket Number'), 1468 }, 1469 { 1470 Key => 'Fulltext', 1471 Value => Translatable('Fulltext'), 1472 }, 1473 { 1474 Key => 'Title', 1475 Value => Translatable('Title'), 1476 }, 1477 { 1478 Key => '', 1479 Value => '-', 1480 Disabled => 1, 1481 }, 1482 ); 1483 1484 for my $ArticleFieldKey ( sort keys %ArticleSearchableFields ) { 1485 push @Attributes, ( 1486 { 1487 Key => $ArticleSearchableFields{$ArticleFieldKey}->{Key}, 1488 Value => Translatable( $ArticleSearchableFields{$ArticleFieldKey}->{Label} ), 1489 }, 1490 ); 1491 } 1492 1493 # Ticket fields 1494 push @Attributes, ( 1495 { 1496 Key => '', 1497 Value => '-', 1498 Disabled => 1, 1499 }, 1500 { 1501 Key => 'CustomerID', 1502 Value => Translatable('CustomerID (complex search)'), 1503 }, 1504 { 1505 Key => 'CustomerIDRaw', 1506 Value => Translatable('CustomerID (exact match)'), 1507 }, 1508 { 1509 Key => 'CustomerUserLogin', 1510 Value => Translatable('Assigned to Customer User Login (complex search)'), 1511 }, 1512 { 1513 Key => 'CustomerUserLoginRaw', 1514 Value => Translatable('Assigned to Customer User Login (exact match)'), 1515 }, 1516 { 1517 Key => 'CustomerUserID', 1518 Value => Translatable('Accessible to Customer User Login (exact match)'), 1519 }, 1520 { 1521 Key => 'StateIDs', 1522 Value => Translatable('State'), 1523 }, 1524 { 1525 Key => 'PriorityIDs', 1526 Value => Translatable('Priority'), 1527 }, 1528 { 1529 Key => 'LockIDs', 1530 Value => Translatable('Lock'), 1531 }, 1532 { 1533 Key => 'QueueIDs', 1534 Value => Translatable('Queue'), 1535 }, 1536 { 1537 Key => 'CreatedQueueIDs', 1538 Value => Translatable('Created in Queue'), 1539 }, 1540 ); 1541 1542 if ( $ConfigObject->Get('Ticket::Type') ) { 1543 push @Attributes, ( 1544 { 1545 Key => 'TypeIDs', 1546 Value => Translatable('Type'), 1547 }, 1548 ); 1549 } 1550 1551 if ( $ConfigObject->Get('Ticket::Service') ) { 1552 push @Attributes, ( 1553 { 1554 Key => 'ServiceIDs', 1555 Value => Translatable('Service'), 1556 }, 1557 { 1558 Key => 'SLAIDs', 1559 Value => Translatable('SLA'), 1560 }, 1561 ); 1562 } 1563 1564 push @Attributes, ( 1565 { 1566 Key => 'OwnerIDs', 1567 Value => Translatable('Owner'), 1568 }, 1569 { 1570 Key => 'CreatedUserIDs', 1571 Value => Translatable('Created by'), 1572 }, 1573 ); 1574 if ( $ConfigObject->Get('Ticket::Watcher') ) { 1575 push @Attributes, ( 1576 { 1577 Key => 'WatchUserIDs', 1578 Value => Translatable('Watcher'), 1579 }, 1580 ); 1581 } 1582 if ( $ConfigObject->Get('Ticket::Responsible') ) { 1583 push @Attributes, ( 1584 { 1585 Key => 'ResponsibleIDs', 1586 Value => Translatable('Responsible'), 1587 }, 1588 ); 1589 } 1590 1591 # Time fields 1592 push @Attributes, ( 1593 { 1594 Key => '', 1595 Value => '-', 1596 Disabled => 1, 1597 }, 1598 { 1599 Key => 'TicketLastChangeTimePoint', 1600 Value => Translatable('Ticket Last Change Time (before/after)'), 1601 }, 1602 { 1603 Key => 'TicketLastChangeTimeSlot', 1604 Value => Translatable('Ticket Last Change Time (between)'), 1605 }, 1606 { 1607 Key => 'TicketChangeTimePoint', 1608 Value => Translatable('Ticket Change Time (before/after)'), 1609 }, 1610 { 1611 Key => 'TicketChangeTimeSlot', 1612 Value => Translatable('Ticket Change Time (between)'), 1613 }, 1614 { 1615 Key => 'TicketCloseTimePoint', 1616 Value => Translatable('Ticket Close Time (before/after)'), 1617 }, 1618 { 1619 Key => 'TicketCloseTimeSlot', 1620 Value => Translatable('Ticket Close Time (between)'), 1621 }, 1622 { 1623 Key => 'TicketCreateTimePoint', 1624 Value => Translatable('Ticket Create Time (before/after)'), 1625 }, 1626 { 1627 Key => 'TicketCreateTimeSlot', 1628 Value => Translatable('Ticket Create Time (between)'), 1629 }, 1630 { 1631 Key => 'TicketPendingTimePoint', 1632 Value => Translatable('Ticket Pending Until Time (before/after)'), 1633 }, 1634 { 1635 Key => 'TicketPendingTimeSlot', 1636 Value => Translatable('Ticket Pending Until Time (between)'), 1637 }, 1638 { 1639 Key => 'TicketEscalationTimePoint', 1640 Value => Translatable('Ticket Escalation Time (before/after)'), 1641 }, 1642 { 1643 Key => 'TicketEscalationTimeSlot', 1644 Value => Translatable('Ticket Escalation Time (between)'), 1645 }, 1646 { 1647 Key => 'ArticleCreateTimePoint', 1648 Value => Translatable('Article Create Time (before/after)'), 1649 }, 1650 { 1651 Key => 'ArticleCreateTimeSlot', 1652 Value => Translatable('Article Create Time (between)'), 1653 }, 1654 ); 1655 1656 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 1657 push @Attributes, ( 1658 { 1659 Key => 'SearchInArchive', 1660 Value => Translatable('Archive Search'), 1661 }, 1662 ); 1663 } 1664 1665 # Dynamic fields 1666 my $DynamicFieldSeparator = 1; 1667 1668 # create dynamic fields search options for attribute select 1669 # cycle trough the activated Dynamic Fields for this screen 1670 DYNAMICFIELD: 1671 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1672 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1673 next DYNAMICFIELD if !$DynamicFieldConfig->{Name}; 1674 next DYNAMICFIELD if $DynamicFieldConfig->{Name} eq ''; 1675 1676 # create a separator for dynamic fields attributes 1677 if ($DynamicFieldSeparator) { 1678 push @Attributes, ( 1679 { 1680 Key => '', 1681 Value => '-', 1682 Disabled => 1, 1683 }, 1684 ); 1685 $DynamicFieldSeparator = 0; 1686 } 1687 1688 # get search field preferences 1689 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 1690 DynamicFieldConfig => $DynamicFieldConfig, 1691 ); 1692 1693 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1694 1695 # translate the dynamic field label 1696 my $TranslatedDynamicFieldLabel = $LayoutObject->{LanguageObject}->Translate( 1697 $DynamicFieldConfig->{Label}, 1698 ); 1699 1700 PREFERENCE: 1701 for my $Preference ( @{$SearchFieldPreferences} ) { 1702 1703 # translate the suffix 1704 my $TranslatedSuffix = $LayoutObject->{LanguageObject}->Translate( 1705 $Preference->{LabelSuffix}, 1706 ) || ''; 1707 1708 if ($TranslatedSuffix) { 1709 $TranslatedSuffix = ' (' . $TranslatedSuffix . ')'; 1710 } 1711 1712 push @Attributes, ( 1713 { 1714 Key => 'Search_DynamicField_' 1715 . $DynamicFieldConfig->{Name} 1716 . $Preference->{Type}, 1717 Value => $TranslatedDynamicFieldLabel . $TranslatedSuffix, 1718 }, 1719 ); 1720 } 1721 } 1722 1723 # create HTML strings for all dynamic fields 1724 my %DynamicFieldHTML; 1725 1726 # cycle trough the activated Dynamic Fields for this screen 1727 DYNAMICFIELD: 1728 for my $DynamicFieldConfig ( @{$DynamicField} ) { 1729 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1730 1731 my $PossibleValuesFilter; 1732 1733 my $IsACLReducible = $BackendObject->HasBehavior( 1734 DynamicFieldConfig => $DynamicFieldConfig, 1735 Behavior => 'IsACLReducible', 1736 ); 1737 1738 if ($IsACLReducible) { 1739 1740 # get PossibleValues 1741 my $PossibleValues = $BackendObject->PossibleValuesGet( 1742 DynamicFieldConfig => $DynamicFieldConfig, 1743 ); 1744 1745 # check if field has PossibleValues property in its configuration 1746 if ( IsHashRefWithData($PossibleValues) ) { 1747 1748 # get historical values from database 1749 my $HistoricalValues = $BackendObject->HistoricalValuesGet( 1750 DynamicFieldConfig => $DynamicFieldConfig, 1751 ); 1752 1753 my $Data = $PossibleValues; 1754 1755 # add historic values to current values (if they don't exist anymore) 1756 if ( IsHashRefWithData($HistoricalValues) ) { 1757 for my $Key ( sort keys %{$HistoricalValues} ) { 1758 if ( !$Data->{$Key} ) { 1759 $Data->{$Key} = $HistoricalValues->{$Key}; 1760 } 1761 } 1762 } 1763 1764 # convert possible values key => value to key => key for ACLs using a Hash slice 1765 my %AclData = %{$Data}; 1766 @AclData{ keys %AclData } = keys %AclData; 1767 1768 # set possible values filter from ACLs 1769 my $ACL = $TicketObject->TicketAcl( 1770 Action => $Self->{Action}, 1771 ReturnType => 'Ticket', 1772 ReturnSubType => 'DynamicField_' . $DynamicFieldConfig->{Name}, 1773 Data => \%AclData, 1774 UserID => $Self->{UserID}, 1775 ); 1776 if ($ACL) { 1777 my %Filter = $TicketObject->TicketAclData(); 1778 1779 # convert Filer key => key back to key => value using map 1780 %{$PossibleValuesFilter} = map { $_ => $Data->{$_} } keys %Filter; 1781 } 1782 } 1783 } 1784 1785 # get search field preferences 1786 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 1787 DynamicFieldConfig => $DynamicFieldConfig, 1788 ); 1789 1790 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 1791 1792 PREFERENCE: 1793 for my $Preference ( @{$SearchFieldPreferences} ) { 1794 1795 # get field HTML 1796 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 1797 = $BackendObject->SearchFieldRender( 1798 DynamicFieldConfig => $DynamicFieldConfig, 1799 Profile => \%GetParam, 1800 PossibleValuesFilter => $PossibleValuesFilter, 1801 DefaultValue => 1802 $Config->{Defaults}->{DynamicField} 1803 ->{ $DynamicFieldConfig->{Name} }, 1804 LayoutObject => $LayoutObject, 1805 Type => $Preference->{Type}, 1806 ); 1807 } 1808 } 1809 1810 $Param{AttributesStrg} = $LayoutObject->BuildSelection( 1811 PossibleNone => 1, 1812 Data => \@Attributes, 1813 Name => 'Attribute', 1814 Multiple => 0, 1815 Class => 'Modernize', 1816 ); 1817 $Param{AttributesOrigStrg} = $LayoutObject->BuildSelection( 1818 PossibleNone => 1, 1819 Data => \@Attributes, 1820 Name => 'AttributeOrig', 1821 Multiple => 0, 1822 Class => 'Modernize', 1823 ); 1824 1825 # get all users of own groups 1826 my %AllUsers = $UserObject->UserList( 1827 Type => 'Long', 1828 Valid => 0, 1829 ); 1830 if ( !$ConfigObject->Get('Ticket::ChangeOwnerToEveryone') ) { 1831 my %Involved = $Kernel::OM->Get('Kernel::System::Group')->PermissionUserInvolvedGet( 1832 UserID => $Self->{UserID}, 1833 Type => 'ro', 1834 ); 1835 for my $UserID ( sort keys %AllUsers ) { 1836 if ( !$Involved{$UserID} ) { 1837 delete $AllUsers{$UserID}; 1838 } 1839 } 1840 } 1841 1842 my @ShownUsers; 1843 my %UsersInvalid; 1844 1845 # get valid users of own groups 1846 my %ValidUsers = $UserObject->UserList( 1847 Type => 'Long', 1848 Valid => 1, 1849 ); 1850 1851 USERID: 1852 for my $UserID ( sort { $AllUsers{$a} cmp $AllUsers{$b} } keys %AllUsers ) { 1853 1854 if ( !$ValidUsers{$UserID} ) { 1855 $UsersInvalid{$UserID} = $AllUsers{$UserID}; 1856 next USERID; 1857 } 1858 1859 push @ShownUsers, { 1860 Key => $UserID, 1861 Value => $AllUsers{$UserID}, 1862 }; 1863 } 1864 1865 # also show invalid agents (if any) 1866 if ( scalar %UsersInvalid ) { 1867 push @ShownUsers, { 1868 Key => '-', 1869 Value => '_____________________', 1870 Disabled => 1, 1871 }; 1872 push @ShownUsers, { 1873 Key => '-', 1874 Value => $LayoutObject->{LanguageObject}->Translate('Invalid Users'), 1875 Disabled => 1, 1876 }; 1877 push @ShownUsers, { 1878 Key => '-', 1879 Value => '', 1880 Disabled => 1, 1881 }; 1882 for my $UserID ( sort { $UsersInvalid{$a} cmp $UsersInvalid{$b} } keys %UsersInvalid ) { 1883 push @ShownUsers, { 1884 Key => $UserID, 1885 Value => $UsersInvalid{$UserID}, 1886 }; 1887 } 1888 } 1889 1890 $Param{UserStrg} = $LayoutObject->BuildSelection( 1891 Data => \@ShownUsers, 1892 Name => 'OwnerIDs', 1893 Multiple => 1, 1894 Size => 5, 1895 SelectedID => $GetParam{OwnerIDs}, 1896 Class => 'Modernize', 1897 ); 1898 $Param{CreatedUserStrg} = $LayoutObject->BuildSelection( 1899 Data => \@ShownUsers, 1900 Name => 'CreatedUserIDs', 1901 Multiple => 1, 1902 Size => 5, 1903 SelectedID => $GetParam{CreatedUserIDs}, 1904 Class => 'Modernize', 1905 ); 1906 if ( $ConfigObject->Get('Ticket::Watcher') ) { 1907 $Param{WatchUserStrg} = $LayoutObject->BuildSelection( 1908 Data => \@ShownUsers, 1909 Name => 'WatchUserIDs', 1910 Multiple => 1, 1911 Size => 5, 1912 SelectedID => $GetParam{WatchUserIDs}, 1913 Class => 'Modernize', 1914 ); 1915 } 1916 if ( $ConfigObject->Get('Ticket::Responsible') ) { 1917 $Param{ResponsibleStrg} = $LayoutObject->BuildSelection( 1918 Data => \@ShownUsers, 1919 Name => 'ResponsibleIDs', 1920 Multiple => 1, 1921 Size => 5, 1922 SelectedID => $GetParam{ResponsibleIDs}, 1923 Class => 'Modernize', 1924 ); 1925 } 1926 1927 # build service string 1928 if ( $ConfigObject->Get('Ticket::Service') ) { 1929 1930 my %Service = $Kernel::OM->Get('Kernel::System::Service')->ServiceList( 1931 UserID => $Self->{UserID}, 1932 KeepChildren => $ConfigObject->Get('Ticket::Service::KeepChildren'), 1933 ); 1934 $Param{ServicesStrg} = $LayoutObject->BuildSelection( 1935 Data => \%Service, 1936 Name => 'ServiceIDs', 1937 SelectedID => $GetParam{ServiceIDs}, 1938 TreeView => $TreeView, 1939 Sort => 'TreeView', 1940 Size => 5, 1941 Multiple => 1, 1942 Translation => 0, 1943 Max => 200, 1944 Class => 'Modernize', 1945 ); 1946 my %SLA = $Kernel::OM->Get('Kernel::System::SLA')->SLAList( 1947 UserID => $Self->{UserID}, 1948 ); 1949 $Param{SLAsStrg} = $LayoutObject->BuildSelection( 1950 Data => \%SLA, 1951 Name => 'SLAIDs', 1952 SelectedID => $GetParam{SLAIDs}, 1953 Sort => 'AlphanumericValue', 1954 Size => 5, 1955 Multiple => 1, 1956 Translation => 0, 1957 Max => 200, 1958 Class => 'Modernize', 1959 ); 1960 } 1961 1962 $Param{ResultFormStrg} = $LayoutObject->BuildSelection( 1963 Data => { 1964 Normal => Translatable('Normal'), 1965 Print => Translatable('Print'), 1966 CSV => Translatable('CSV'), 1967 Excel => Translatable('Excel'), 1968 }, 1969 Name => 'ResultForm', 1970 SelectedID => $GetParam{ResultForm} || 'Normal', 1971 Class => 'Modernize', 1972 ); 1973 1974 if ( $ConfigObject->Get('Ticket::ArchiveSystem') ) { 1975 1976 $Param{SearchInArchiveStrg} = $LayoutObject->BuildSelection( 1977 Data => { 1978 ArchivedTickets => Translatable('Archived tickets'), 1979 NotArchivedTickets => Translatable('Unarchived tickets'), 1980 AllTickets => Translatable('All tickets'), 1981 }, 1982 Name => 'SearchInArchive', 1983 SelectedID => $GetParam{SearchInArchive} || 'NotArchivedTickets', 1984 Class => 'Modernize', 1985 ); 1986 } 1987 1988 my %Profiles = $SearchProfileObject->SearchProfileList( 1989 Base => 'TicketSearch', 1990 UserLogin => $Self->{UserLogin}, 1991 ); 1992 1993 if ( $Profiles{'last-search'} ) { 1994 $Profiles{'last-search'} = $LayoutObject->{LanguageObject}->Translate('last-search'); 1995 } 1996 1997 $Param{ProfilesStrg} = $LayoutObject->BuildSelection( 1998 Data => \%Profiles, 1999 Name => 'Profile', 2000 ID => 'SearchProfile', 2001 SelectedID => $Profile, 2002 Class => 'Modernize', 2003 Translation => 0, 2004 PossibleNone => 1, 2005 ); 2006 2007 $Param{StatesStrg} = $LayoutObject->BuildSelection( 2008 Data => { 2009 $StateObject->StateList( 2010 UserID => $Self->{UserID}, 2011 Action => $Self->{Action}, 2012 ), 2013 }, 2014 Name => 'StateIDs', 2015 Multiple => 1, 2016 Size => 5, 2017 SelectedID => $GetParam{StateIDs}, 2018 Class => 'Modernize', 2019 ); 2020 my %AllQueues = $Kernel::OM->Get('Kernel::System::Queue')->GetAllQueues( 2021 UserID => $Self->{UserID}, 2022 Type => 'ro', 2023 ); 2024 $Param{QueuesStrg} = $LayoutObject->AgentQueueListOption( 2025 Data => \%AllQueues, 2026 Size => 5, 2027 Multiple => 1, 2028 Name => 'QueueIDs', 2029 TreeView => $TreeView, 2030 SelectedIDRefArray => $GetParam{QueueIDs}, 2031 OnChangeSubmit => 0, 2032 Class => 'Modernize', 2033 ); 2034 $Param{CreatedQueuesStrg} = $LayoutObject->AgentQueueListOption( 2035 Data => \%AllQueues, 2036 Size => 5, 2037 Multiple => 1, 2038 Name => 'CreatedQueueIDs', 2039 TreeView => $TreeView, 2040 SelectedIDRefArray => $GetParam{CreatedQueueIDs}, 2041 OnChangeSubmit => 0, 2042 Class => 'Modernize', 2043 ); 2044 $Param{PrioritiesStrg} = $LayoutObject->BuildSelection( 2045 Data => { 2046 $TicketObject->TicketPriorityList( 2047 UserID => $Self->{UserID}, 2048 Action => $Self->{Action}, 2049 ), 2050 }, 2051 Name => 'PriorityIDs', 2052 Multiple => 1, 2053 Size => 5, 2054 SelectedID => $GetParam{PriorityIDs}, 2055 Class => 'Modernize', 2056 ); 2057 $Param{LocksStrg} = $LayoutObject->BuildSelection( 2058 Data => { 2059 $Kernel::OM->Get('Kernel::System::Lock')->LockList( 2060 UserID => $Self->{UserID}, 2061 Action => $Self->{Action}, 2062 ), 2063 }, 2064 Name => 'LockIDs', 2065 Multiple => 1, 2066 Size => 5, 2067 SelectedID => $GetParam{LockIDs}, 2068 Class => 'Modernize', 2069 ); 2070 2071 $Param{ArticleCreateTimePoint} = $LayoutObject->BuildSelection( 2072 Data => [ 1 .. 59 ], 2073 Name => 'ArticleCreateTimePoint', 2074 SelectedID => $GetParam{ArticleCreateTimePoint}, 2075 ); 2076 $Param{ArticleCreateTimePointStart} = $LayoutObject->BuildSelection( 2077 Data => { 2078 'Last' => Translatable('within the last ...'), 2079 'Before' => Translatable('more than ... ago'), 2080 }, 2081 Name => 'ArticleCreateTimePointStart', 2082 SelectedID => $GetParam{ArticleCreateTimePointStart} || 'Last', 2083 ); 2084 $Param{ArticleCreateTimePointFormat} = $LayoutObject->BuildSelection( 2085 Data => { 2086 minute => Translatable('minute(s)'), 2087 hour => Translatable('hour(s)'), 2088 day => Translatable('day(s)'), 2089 week => Translatable('week(s)'), 2090 month => Translatable('month(s)'), 2091 year => Translatable('year(s)'), 2092 }, 2093 Name => 'ArticleCreateTimePointFormat', 2094 SelectedID => $GetParam{ArticleCreateTimePointFormat}, 2095 ); 2096 $Param{ArticleCreateTimeStart} = $LayoutObject->BuildDateSelection( 2097 %GetParam, 2098 Prefix => 'ArticleCreateTimeStart', 2099 Format => 'DateInputFormat', 2100 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2101 Validate => 1, 2102 ValidateDateBeforePrefix => 'ArticleCreateTimeStop', 2103 ); 2104 $Param{ArticleCreateTimeStop} = $LayoutObject->BuildDateSelection( 2105 %GetParam, 2106 Prefix => 'ArticleCreateTimeStop', 2107 Format => 'DateInputFormat', 2108 Validate => 1, 2109 ValidateDateAfterPrefix => 'ArticleCreateTimeStart', 2110 ); 2111 $Param{TicketCreateTimePoint} = $LayoutObject->BuildSelection( 2112 Data => [ 1 .. 59 ], 2113 Name => 'TicketCreateTimePoint', 2114 SelectedID => $GetParam{TicketCreateTimePoint}, 2115 ); 2116 $Param{TicketCreateTimePointStart} = $LayoutObject->BuildSelection( 2117 Data => { 2118 'Last' => Translatable('within the last ...'), 2119 'Before' => Translatable('more than ... ago'), 2120 }, 2121 Name => 'TicketCreateTimePointStart', 2122 SelectedID => $GetParam{TicketCreateTimePointStart} || 'Last', 2123 ); 2124 $Param{TicketCreateTimePointFormat} = $LayoutObject->BuildSelection( 2125 Data => { 2126 minute => Translatable('minute(s)'), 2127 hour => Translatable('hour(s)'), 2128 day => Translatable('day(s)'), 2129 week => Translatable('week(s)'), 2130 month => Translatable('month(s)'), 2131 year => Translatable('year(s)'), 2132 }, 2133 Name => 'TicketCreateTimePointFormat', 2134 SelectedID => $GetParam{TicketCreateTimePointFormat}, 2135 ); 2136 $Param{TicketCreateTimeStart} = $LayoutObject->BuildDateSelection( 2137 %GetParam, 2138 Prefix => 'TicketCreateTimeStart', 2139 Format => 'DateInputFormat', 2140 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2141 Validate => 1, 2142 ValidateDateBeforePrefix => 'TicketCreateTimeStop', 2143 ); 2144 $Param{TicketCreateTimeStop} = $LayoutObject->BuildDateSelection( 2145 %GetParam, 2146 Prefix => 'TicketCreateTimeStop', 2147 Format => 'DateInputFormat', 2148 Validate => 1, 2149 ValidateDateAfterPrefix => 'TicketCreateTimeStart', 2150 ); 2151 2152 $Param{TicketPendingTimePoint} = $LayoutObject->BuildSelection( 2153 Data => [ 1 .. 59 ], 2154 Name => 'TicketPendingTimePoint', 2155 SelectedID => $GetParam{TicketPendingTimePoint}, 2156 ); 2157 $Param{TicketPendingTimePointStart} = $LayoutObject->BuildSelection( 2158 Data => { 2159 'Last' => Translatable('within the last ...'), 2160 'Next' => Translatable('within the next ...'), 2161 'Before' => Translatable('more than ... ago'), 2162 'After' => Translatable('in more than ...'), 2163 }, 2164 Name => 'TicketPendingTimePointStart', 2165 SelectedID => $GetParam{TicketPendingTimePointStart} || 'Next', 2166 ); 2167 $Param{TicketPendingTimePointFormat} = $LayoutObject->BuildSelection( 2168 Data => { 2169 minute => Translatable('minute(s)'), 2170 hour => Translatable('hour(s)'), 2171 day => Translatable('day(s)'), 2172 week => Translatable('week(s)'), 2173 month => Translatable('month(s)'), 2174 year => Translatable('year(s)'), 2175 }, 2176 Name => 'TicketPendingTimePointFormat', 2177 SelectedID => $GetParam{TicketPendingTimePointFormat}, 2178 ); 2179 $Param{TicketPendingTimeStart} = $LayoutObject->BuildDateSelection( 2180 %GetParam, 2181 Prefix => 'TicketPendingTimeStart', 2182 Format => 'DateInputFormat', 2183 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2184 Validate => 1, 2185 ValidateDateBeforePrefix => 'TicketPendingTimeStop', 2186 ); 2187 $Param{TicketPendingTimeStop} = $LayoutObject->BuildDateSelection( 2188 %GetParam, 2189 Prefix => 'TicketPendingTimeStop', 2190 Format => 'DateInputFormat', 2191 Validate => 1, 2192 ValidateDateAfterPrefix => 'TicketPendingTimeStart', 2193 ); 2194 2195 $Param{TicketChangeTimePoint} = $LayoutObject->BuildSelection( 2196 Data => [ 1 .. 59 ], 2197 Name => 'TicketChangeTimePoint', 2198 SelectedID => $GetParam{TicketChangeTimePoint}, 2199 ); 2200 $Param{TicketChangeTimePointStart} = $LayoutObject->BuildSelection( 2201 Data => { 2202 'Last' => 'within the last ...', 2203 'Before' => 'more than ... ago', 2204 }, 2205 Name => 'TicketChangeTimePointStart', 2206 SelectedID => $GetParam{TicketChangeTimePointStart} || 'Last', 2207 ); 2208 $Param{TicketChangeTimePointFormat} = $LayoutObject->BuildSelection( 2209 Data => { 2210 minute => Translatable('minute(s)'), 2211 hour => Translatable('hour(s)'), 2212 day => Translatable('day(s)'), 2213 week => Translatable('week(s)'), 2214 month => Translatable('month(s)'), 2215 year => Translatable('year(s)'), 2216 }, 2217 Name => 'TicketChangeTimePointFormat', 2218 SelectedID => $GetParam{TicketChangeTimePointFormat}, 2219 ); 2220 $Param{TicketChangeTimeStart} = $LayoutObject->BuildDateSelection( 2221 %GetParam, 2222 Prefix => 'TicketChangeTimeStart', 2223 Format => 'DateInputFormat', 2224 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2225 Validate => 1, 2226 ValidateDateBeforePrefix => 'TicketChangeTimeStop', 2227 ); 2228 $Param{TicketChangeTimeStop} = $LayoutObject->BuildDateSelection( 2229 %GetParam, 2230 Prefix => 'TicketChangeTimeStop', 2231 Format => 'DateInputFormat', 2232 Validate => 1, 2233 ValidateDateAfterPrefix => 'TicketChangeTimeStart', 2234 ); 2235 2236 $Param{TicketCloseTimePoint} = $LayoutObject->BuildSelection( 2237 Data => [ 1 .. 59 ], 2238 Name => 'TicketCloseTimePoint', 2239 SelectedID => $GetParam{TicketCloseTimePoint}, 2240 ); 2241 $Param{TicketCloseTimePointStart} = $LayoutObject->BuildSelection( 2242 Data => { 2243 'Last' => Translatable('within the last ...'), 2244 'Before' => Translatable('more than ... ago'), 2245 }, 2246 Name => 'TicketCloseTimePointStart', 2247 SelectedID => $GetParam{TicketCloseTimePointStart} || 'Last', 2248 ); 2249 $Param{TicketCloseTimePointFormat} = $LayoutObject->BuildSelection( 2250 Data => { 2251 minute => Translatable('minute(s)'), 2252 hour => Translatable('hour(s)'), 2253 day => Translatable('day(s)'), 2254 week => Translatable('week(s)'), 2255 month => Translatable('month(s)'), 2256 year => Translatable('year(s)'), 2257 }, 2258 Name => 'TicketCloseTimePointFormat', 2259 SelectedID => $GetParam{TicketCloseTimePointFormat}, 2260 ); 2261 $Param{TicketCloseTimeStart} = $LayoutObject->BuildDateSelection( 2262 %GetParam, 2263 Prefix => 'TicketCloseTimeStart', 2264 Format => 'DateInputFormat', 2265 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2266 Validate => 1, 2267 ValidateDateBeforePrefix => 'TicketCloseTimeStop', 2268 ); 2269 $Param{TicketCloseTimeStop} = $LayoutObject->BuildDateSelection( 2270 %GetParam, 2271 Prefix => 'TicketCloseTimeStop', 2272 Format => 'DateInputFormat', 2273 Validate => 1, 2274 ValidateDateAfterPrefix => 'TicketCloseTimeStart', 2275 ); 2276 2277 $Param{TicketLastChangeTimePoint} = $LayoutObject->BuildSelection( 2278 Data => [ 1 .. 59 ], 2279 Name => 'TicketLastChangeTimePoint', 2280 SelectedID => $GetParam{TicketLastChangeTimePoint}, 2281 ); 2282 $Param{TicketLastChangeTimePointStart} = $LayoutObject->BuildSelection( 2283 Data => { 2284 'Last' => Translatable('within the last ...'), 2285 'Before' => Translatable('more than ... ago'), 2286 }, 2287 Name => 'TicketLastChangeTimePointStart', 2288 SelectedID => $GetParam{TicketLastChangeTimePointStart} || 'Last', 2289 ); 2290 $Param{TicketLastChangeTimePointFormat} = $LayoutObject->BuildSelection( 2291 Data => { 2292 minute => Translatable('minute(s)'), 2293 hour => Translatable('hour(s)'), 2294 day => Translatable('day(s)'), 2295 week => Translatable('week(s)'), 2296 month => Translatable('month(s)'), 2297 year => Translatable('year(s)'), 2298 }, 2299 Name => 'TicketLastChangeTimePointFormat', 2300 SelectedID => $GetParam{TicketLastChangeTimePointFormat}, 2301 ); 2302 $Param{TicketLastChangeTimeStart} = $LayoutObject->BuildDateSelection( 2303 %GetParam, 2304 Prefix => 'TicketLastChangeTimeStart', 2305 Format => 'DateInputFormat', 2306 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2307 Validate => 1, 2308 ValidateDateBeforePrefix => 'TicketLastChangeTimeStop', 2309 ); 2310 $Param{TicketLastChangeTimeStop} = $LayoutObject->BuildDateSelection( 2311 %GetParam, 2312 Prefix => 'TicketLastChangeTimeStop', 2313 Format => 'DateInputFormat', 2314 Validate => 1, 2315 ValidateDateAfterPrefix => 'TicketLastChangeTimeStart', 2316 ); 2317 2318 $Param{TicketEscalationTimePoint} = $LayoutObject->BuildSelection( 2319 Data => [ 1 .. 59 ], 2320 Name => 'TicketEscalationTimePoint', 2321 SelectedID => $GetParam{TicketEscalationTimePoint}, 2322 ); 2323 $Param{TicketEscalationTimePointStart} = $LayoutObject->BuildSelection( 2324 Data => { 2325 'Last' => Translatable('within the last ...'), 2326 'Next' => Translatable('within the next ...'), 2327 'Before' => Translatable('more than ... ago'), 2328 }, 2329 Name => 'TicketEscalationTimePointStart', 2330 SelectedID => $GetParam{TicketEscalationTimePointStart} || 'Last', 2331 ); 2332 $Param{TicketEscalationTimePointFormat} = $LayoutObject->BuildSelection( 2333 Data => { 2334 minute => Translatable('minute(s)'), 2335 hour => Translatable('hour(s)'), 2336 day => Translatable('day(s)'), 2337 week => Translatable('week(s)'), 2338 month => Translatable('month(s)'), 2339 year => Translatable('year(s)'), 2340 }, 2341 Name => 'TicketEscalationTimePointFormat', 2342 SelectedID => $GetParam{TicketEscalationTimePointFormat}, 2343 ); 2344 $Param{TicketEscalationTimeStart} = $LayoutObject->BuildDateSelection( 2345 %GetParam, 2346 Prefix => 'TicketEscalationTimeStart', 2347 Format => 'DateInputFormat', 2348 DiffTime => -( ( 60 * 60 * 24 ) * 30 ), 2349 Validate => 1, 2350 ValidateDateBeforePrefix => 'TicketEscalationTimeStop', 2351 ); 2352 $Param{TicketEscalationTimeStop} = $LayoutObject->BuildDateSelection( 2353 %GetParam, 2354 Prefix => 'TicketEscalationTimeStop', 2355 Format => 'DateInputFormat', 2356 Validate => 1, 2357 ValidateDateAfterPrefix => 'TicketEscalationTimeStart', 2358 ); 2359 2360 my %GetParamBackup = %GetParam; 2361 for my $Key ( 2362 qw(TicketEscalation TicketClose TicketChange TicketLastChange TicketPending TicketCreate ArticleCreate) 2363 ) 2364 { 2365 for my $SubKey (qw(TimeStart TimeStop TimePoint TimePointStart TimePointFormat)) { 2366 delete $GetParam{ $Key . $SubKey }; 2367 delete $GetParamBackup{ $Key . $SubKey }; 2368 } 2369 } 2370 2371 # build type string 2372 if ( $ConfigObject->Get('Ticket::Type') ) { 2373 2374 # get ticket object 2375 my %Type = $TicketObject->TicketTypeList( 2376 UserID => $Self->{UserID}, 2377 Action => $Self->{Action}, 2378 ); 2379 $Param{TypesStrg} = $LayoutObject->BuildSelection( 2380 Data => \%Type, 2381 Name => 'TypeIDs', 2382 SelectedID => $GetParam{TypeIDs}, 2383 Sort => 'AlphanumericValue', 2384 Size => 3, 2385 Multiple => 1, 2386 Translation => 0, 2387 Class => 'Modernize', 2388 ); 2389 } 2390 2391 # html search mask output 2392 $LayoutObject->Block( 2393 Name => 'SearchAJAX', 2394 Data => { 2395 %Param, 2396 %GetParam, 2397 EmptySearch => $EmptySearch, 2398 }, 2399 ); 2400 2401 # create the field entries to be displayed in the modal dialog 2402 for my $ArticleFieldKey ( sort keys %ArticleSearchableFields ) { 2403 $LayoutObject->Block( 2404 Name => 'SearchableArticleField', 2405 Data => { 2406 ArticleFieldLabel => $ArticleSearchableFields{$ArticleFieldKey}->{Label}, 2407 ArticleFieldKey => $ArticleSearchableFields{$ArticleFieldKey}->{Key}, 2408 ArticleFieldValue => $GetParam{$ArticleFieldKey} // '', 2409 }, 2410 ); 2411 } 2412 2413 # output Dynamic fields blocks 2414 # cycle trough the activated Dynamic Fields for this screen 2415 DYNAMICFIELD: 2416 for my $DynamicFieldConfig ( @{$DynamicField} ) { 2417 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 2418 2419 # get search field preferences 2420 my $SearchFieldPreferences = $BackendObject->SearchFieldPreferences( 2421 DynamicFieldConfig => $DynamicFieldConfig, 2422 ); 2423 2424 next DYNAMICFIELD if !IsArrayRefWithData($SearchFieldPreferences); 2425 2426 PREFERENCE: 2427 for my $Preference ( @{$SearchFieldPreferences} ) { 2428 2429 # skip fields that HTML could not be retrieved 2430 next PREFERENCE if !IsHashRefWithData( 2431 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 2432 ); 2433 2434 $LayoutObject->Block( 2435 Name => 'DynamicField', 2436 Data => { 2437 Label => 2438 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 2439 ->{Label}, 2440 Field => 2441 $DynamicFieldHTML{ $DynamicFieldConfig->{Name} . $Preference->{Type} } 2442 ->{Field}, 2443 }, 2444 ); 2445 } 2446 } 2447 2448 # compat. map for attributes 2449 my %Map = ( 2450 TimeSearchType => 'TicketCreate', 2451 ChangeTimeSearchType => 'TicketChange', 2452 CloseTimeSearchType => 'TicketClose', 2453 LastChangeTimeSearchType => 'TicketLastChange', 2454 PendingTimeSearchType => 'TicketPending', 2455 EscalationTimeSearchType => 'TicketEscalation', 2456 ArticleTimeSearchType => 'ArticleCreate', 2457 ); 2458 KEY: 2459 for my $Key ( sort keys %Map ) { 2460 next KEY if !defined $GetParamBackup{$Key}; 2461 if ( $GetParamBackup{$Key} eq 'TimePoint' ) { 2462 $GetParamBackup{ $Map{$Key} . 'TimePoint' } = 1; 2463 } 2464 elsif ( $GetParamBackup{$Key} eq 'TimeSlot' ) { 2465 $GetParamBackup{ $Map{$Key} . 'TimeSlot' } = 1; 2466 } 2467 } 2468 2469 # attributes for search 2470 my @SearchAttributes; 2471 2472 # show attributes 2473 my @ShownAttributes; 2474 if ( $GetParamBackup{ShownAttributes} ) { 2475 @ShownAttributes = split /;/, $GetParamBackup{ShownAttributes}; 2476 } 2477 my %AlreadyShown; 2478 2479 if ($Profile) { 2480 ITEM: 2481 for my $Item (@Attributes) { 2482 my $Key = $Item->{Key}; 2483 next ITEM if !$Key; 2484 2485 # check if shown 2486 if (@ShownAttributes) { 2487 my $Show = 0; 2488 SHOWN_ATTRIBUTE: 2489 for my $ShownAttribute (@ShownAttributes) { 2490 if ( 'Label' . $Key eq $ShownAttribute ) { 2491 $Show = 1; 2492 last SHOWN_ATTRIBUTE; 2493 } 2494 } 2495 next ITEM if !$Show; 2496 } 2497 else { 2498 # Skip undefined 2499 next ITEM if !defined $GetParamBackup{$Key}; 2500 2501 # Skip empty strings 2502 next ITEM if $GetParamBackup{$Key} eq ''; 2503 2504 # Skip empty arrays 2505 if ( ref $GetParamBackup{$Key} eq 'ARRAY' && !@{ $GetParamBackup{$Key} } ) { 2506 next ITEM; 2507 } 2508 } 2509 2510 # show attribute 2511 next ITEM if $AlreadyShown{$Key}; 2512 $AlreadyShown{$Key} = 1; 2513 2514 push @SearchAttributes, $Key; 2515 } 2516 } 2517 2518 # No profile, show default screen 2519 else { 2520 2521 # Merge regular show/hide settings and the settings for the dynamic fields 2522 my %Defaults = %{ $Config->{Defaults} || {} }; 2523 for my $DynamicFields ( sort keys %{ $Config->{DynamicField} || {} } ) { 2524 if ( $Config->{DynamicField}->{$DynamicFields} == 2 ) { 2525 $Defaults{"Search_DynamicField_$DynamicFields"} = 1; 2526 } 2527 } 2528 2529 my @OrderedDefaults; 2530 if (%Defaults) { 2531 2532 # ordering attributes on the same order like in Attributes 2533 for my $Item (@Attributes) { 2534 my $KeyAtr = $Item->{Key}; 2535 for my $Key ( sort keys %Defaults ) { 2536 if ( $Key eq $KeyAtr ) { 2537 push @OrderedDefaults, $Key; 2538 } 2539 } 2540 } 2541 2542 KEY: 2543 for my $Key (@OrderedDefaults) { 2544 next KEY if $Key eq 'DynamicField'; # Ignore entry for DF config 2545 next KEY if $AlreadyShown{$Key}; 2546 $AlreadyShown{$Key} = 1; 2547 2548 push @SearchAttributes, $Key; 2549 } 2550 } 2551 2552 # If no attribute is shown, show fulltext search. 2553 if ( !keys %AlreadyShown ) { 2554 push @SearchAttributes, 'Fulltext'; 2555 } 2556 } 2557 2558 $LayoutObject->AddJSData( 2559 Key => 'SearchAttributes', 2560 Value => \@SearchAttributes, 2561 ); 2562 2563 my $Output = $LayoutObject->Output( 2564 TemplateFile => 'AgentTicketSearch', 2565 Data => \%Param, 2566 AJAX => 1, 2567 ); 2568 return $LayoutObject->Attachment( 2569 NoCache => 1, 2570 ContentType => 'text/html', 2571 Charset => $LayoutObject->{UserCharset}, 2572 Content => $Output, 2573 Type => 'inline', 2574 ); 2575 } 2576 2577 # show default search screen 2578 $Output = $LayoutObject->Header(); 2579 $Output .= $LayoutObject->NavigationBar(); 2580 2581 # Notify if there are tickets which are not updated. 2582 $Output .= $LayoutObject->NotifyNonUpdatedTickets() // ''; 2583 2584 $LayoutObject->AddJSData( 2585 Key => 'NonAJAXSearch', 2586 Value => 1, 2587 ); 2588 if ( $Self->{Profile} ) { 2589 $LayoutObject->AddJSData( 2590 Key => 'Profile', 2591 Value => $Self->{Profile}, 2592 ); 2593 } 2594 $Output .= $LayoutObject->Output( 2595 TemplateFile => 'AgentTicketSearch', 2596 Data => \%Param, 2597 ); 2598 $Output .= $LayoutObject->Footer(); 2599 return $Output; 2600} 2601 26021; 2603