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