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