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::System::DynamicField::Driver::BaseDateTime;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15use Kernel::Language qw(Translatable);
16
17use parent qw(Kernel::System::DynamicField::Driver::Base);
18
19our @ObjectDependencies = (
20    'Kernel::System::DateTime',
21    'Kernel::System::DB',
22    'Kernel::System::DynamicFieldValue',
23    'Kernel::System::Log',
24);
25
26=head1 NAME
27
28Kernel::System::DynamicField::Driver::BaseDateTime - sub module of
29Kernel::System::DynamicField::Driver::Date and
30Kernel::System::DynamicField::Driver::DateTime
31
32=head1 DESCRIPTION
33
34Date common functions.
35
36=head1 PUBLIC INTERFACE
37
38=cut
39
40sub ValueGet {
41    my ( $Self, %Param ) = @_;
42
43    my $DFValue = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueGet(
44        FieldID  => $Param{DynamicFieldConfig}->{ID},
45        ObjectID => $Param{ObjectID},
46    );
47
48    return if !$DFValue;
49    return if !IsArrayRefWithData($DFValue);
50    return if !IsHashRefWithData( $DFValue->[0] );
51
52    return $DFValue->[0]->{ValueDateTime};
53}
54
55sub ValueSet {
56    my ( $Self, %Param ) = @_;
57
58    my $Success = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueSet(
59        FieldID  => $Param{DynamicFieldConfig}->{ID},
60        ObjectID => $Param{ObjectID},
61        Value    => [
62            {
63                ValueDateTime => $Param{Value},
64            },
65        ],
66        UserID => $Param{UserID},
67    );
68
69    return $Success;
70}
71
72sub ValueValidate {
73    my ( $Self, %Param ) = @_;
74
75    my $Prefix          = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
76    my $DateRestriction = $Param{DynamicFieldConfig}->{Config}->{DateRestriction};
77
78    my $Success = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueValidate(
79        Value => {
80            ValueDateTime => $Param{Value},
81        },
82        UserID => $Param{UserID}
83    );
84
85    if ($DateRestriction) {
86
87        # get time object
88        my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
89
90        my $SystemTime = $DateTimeObject->ToEpoch();
91
92        my $ValueSystemTime = $DateTimeObject->Set(
93            String => $Param{Value},
94        );
95        $ValueSystemTime = $ValueSystemTime ? $DateTimeObject->ToEpoch() : undef;
96
97        if ( $DateRestriction eq 'DisableFutureDates' && $ValueSystemTime > $SystemTime ) {
98            $Kernel::OM->Get('Kernel::System::Log')->Log(
99                Priority => 'error',
100                Message =>
101                    "The value for the Data field ($Param{DynamicFieldConfig}->{Name}) is in the future! The date needs to be in the past!",
102            );
103            return;
104        }
105        elsif ( $DateRestriction eq 'DisablePastDates' && $ValueSystemTime < $SystemTime ) {
106            $Kernel::OM->Get('Kernel::System::Log')->Log(
107                Priority => 'error',
108                Message =>
109                    "The value for the Date field ($Param{DynamicFieldConfig}->{Name}) is in the past! The date needs to be in the future!",
110            );
111            return;
112        }
113    }
114
115    return $Success;
116}
117
118sub SearchSQLGet {
119    my ( $Self, %Param ) = @_;
120
121    my %Operators = (
122        Equals            => '=',
123        GreaterThan       => '>',
124        GreaterThanEquals => '>=',
125        SmallerThan       => '<',
126        SmallerThanEquals => '<=',
127    );
128
129    if ( $Param{Operator} eq 'Empty' ) {
130        if ( $Param{SearchTerm} ) {
131            return " $Param{TableAlias}.value_date IS NULL ";
132        }
133        else {
134            return " $Param{TableAlias}.value_date IS NOT NULL ";
135        }
136    }
137    elsif ( !$Operators{ $Param{Operator} } ) {
138        $Kernel::OM->Get('Kernel::System::Log')->Log(
139            'Priority' => 'error',
140            'Message'  => "Unsupported Operator $Param{Operator}",
141        );
142        return;
143    }
144
145    my $SQL = " $Param{TableAlias}.value_date $Operators{ $Param{Operator} } '";
146    $SQL .= $Kernel::OM->Get('Kernel::System::DB')->Quote( $Param{SearchTerm} ) . "' ";
147    return $SQL;
148}
149
150sub SearchSQLOrderFieldGet {
151    my ( $Self, %Param ) = @_;
152
153    return "$Param{TableAlias}.value_date";
154}
155
156sub EditFieldRender {
157    my ( $Self, %Param ) = @_;
158
159    # take config from field config
160    my $FieldConfig = $Param{DynamicFieldConfig}->{Config};
161    my $FieldName   = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
162    my $FieldLabel  = $Param{DynamicFieldConfig}->{Label};
163
164    my $Value;
165
166    # set the field value or default
167    if ( $Param{UseDefaultValue} ) {
168        $Value = $FieldConfig->{DefaultValue} || '';
169    }
170
171    if ( defined $Param{Value} ) {
172        $Value = $Param{Value};
173    }
174
175    if ($Value) {
176        my ( $Year, $Month, $Day, $Hour, $Minute, $Second ) = $Value =~
177            m{ \A ( \d{4} ) - ( \d{2} ) - ( \d{2} ) \s ( \d{2} ) : ( \d{2} ) : ( \d{2} ) \z }xms;
178
179        # If a value is sent this value must be active, then the Used part needs to be set to 1
180        #   otherwise user can easily forget to mark the checkbox and this could lead into data
181        #   lost (Bug#8258).
182        $FieldConfig->{ $FieldName . 'Used' }   = 1;
183        $FieldConfig->{ $FieldName . 'Year' }   = $Year;
184        $FieldConfig->{ $FieldName . 'Month' }  = $Month;
185        $FieldConfig->{ $FieldName . 'Day' }    = $Day;
186        $FieldConfig->{ $FieldName . 'Hour' }   = $Hour;
187        $FieldConfig->{ $FieldName . 'Minute' } = $Minute;
188    }
189
190    # extract the dynamic field value from the web request
191    # TransformDates is always needed from EditFieldRender Bug#8452
192    my $FieldValues = $Self->EditFieldValueGet(
193        TransformDates       => 1,
194        ReturnValueStructure => 1,
195        %Param,
196    );
197
198    # set values from ParamObject if present
199    if ( defined $FieldValues && IsHashRefWithData($FieldValues) ) {
200        for my $Type (qw(Used Year Month Day Hour Minute)) {
201            $FieldConfig->{ $FieldName . $Type } = $FieldValues->{ $FieldName . $Type };
202        }
203    }
204
205    # check and set class if necessary
206    # Bug#9358: Class 'DateSelection' is needed for CustomerInterface
207    my $FieldClass = 'DynamicFieldText DateSelection';
208    if ( defined $Param{Class} && $Param{Class} ne '' ) {
209        $FieldClass .= ' ' . $Param{Class};
210    }
211
212    # set field as mandatory
213    if ( $Param{Mandatory} ) {
214        $FieldClass .= ' Validate_Required';
215    }
216
217    # set error css class
218    if ( $Param{ServerError} ) {
219        $FieldClass .= ' ServerError';
220    }
221
222    # to set the predefined based on a time difference
223    my $DiffTime = $FieldConfig->{DefaultValue};
224    if ( !defined $DiffTime || $DiffTime !~ m/^ \s* -? \d+ \s* $/smx ) {
225        $DiffTime = 0;
226    }
227
228    # to set the years range
229    my %YearsPeriodRange;
230    if ( defined $FieldConfig->{YearsPeriod} && $FieldConfig->{YearsPeriod} eq '1' ) {
231        %YearsPeriodRange = (
232            YearPeriodPast   => $FieldConfig->{YearsInPast}   || 0,
233            YearPeriodFuture => $FieldConfig->{YearsInFuture} || 0,
234        );
235    }
236
237    # date restrictions
238    if ( $FieldConfig->{DateRestriction} ) {
239        if ( $FieldConfig->{DateRestriction} eq 'DisablePastDates' ) {
240            $FieldConfig->{ValidateDateInFuture} = 1;
241        }
242        elsif ( $FieldConfig->{DateRestriction} eq 'DisableFutureDates' ) {
243            $FieldConfig->{ValidateDateNotInFuture} = 1;
244        }
245    }
246
247    my $HTMLString = $Param{LayoutObject}->BuildDateSelection(
248        %Param,
249        Prefix                => $FieldName,
250        Format                => 'DateInputFormatLong',
251        $FieldName . 'Class'  => $FieldClass,
252        DiffTime              => $DiffTime,
253        $FieldName . Required => $Param{Mandatory} || 0,
254        $FieldName . Optional => 1,
255        Validate              => 1,
256        %{$FieldConfig},
257        %YearsPeriodRange,
258    );
259
260    if ( $Param{Mandatory} ) {
261        my $DivID = $FieldName . 'UsedError';
262
263        my $FieldRequiredMessage = $Param{LayoutObject}->{LanguageObject}->Translate("This field is required.");
264
265        # for client side validation
266        $HTMLString .= <<"EOF";
267
268<div id="$DivID" class="TooltipErrorMessage">
269    <p>
270        $FieldRequiredMessage
271    </p>
272</div>
273EOF
274    }
275
276    if ( $Param{ServerError} ) {
277
278        my $ErrorMessage = $Param{ErrorMessage} || 'This field is required.';
279        $ErrorMessage = $Param{LayoutObject}->{LanguageObject}->Translate($ErrorMessage);
280        my $DivID = $FieldName . 'UsedServerError';
281
282        # for server side validation
283        $HTMLString .= <<"EOF";
284
285<div id="$DivID" class="TooltipErrorMessage">
286    <p>
287        $ErrorMessage
288    </p>
289</div>
290EOF
291    }
292
293    # call EditLabelRender on the common Driver
294    my $LabelString = $Self->EditLabelRender(
295        %Param,
296        Mandatory => $Param{Mandatory} || '0',
297        FieldName => $FieldName . 'Used',
298    );
299
300    my $Data = {
301        Field => $HTMLString,
302        Label => $LabelString,
303    };
304
305    return $Data;
306}
307
308sub EditFieldValueGet {
309    my ( $Self, %Param ) = @_;
310
311    # set the Prefix as the dynamic field name
312    my $Prefix = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
313
314    my %DynamicFieldValues;
315
316    # check if there is a Template and retrieve the dynamic field value from there
317    if ( IsHashRefWithData( $Param{Template} ) && defined $Param{Template}->{ $Prefix . 'Used' } ) {
318        for my $Type (qw(Used Year Month Day Hour Minute)) {
319            $DynamicFieldValues{ $Prefix . $Type } = $Param{Template}->{ $Prefix . $Type } || 0;
320        }
321    }
322
323    # otherwise get dynamic field value from the web request
324    elsif (
325        defined $Param{ParamObject}
326        && ref $Param{ParamObject} eq 'Kernel::System::Web::Request'
327        )
328    {
329        for my $Type (qw(Used Year Month Day Hour Minute)) {
330            $DynamicFieldValues{ $Prefix . $Type } = $Param{ParamObject}->GetParam(
331                Param => $Prefix . $Type,
332            ) || 0;
333        }
334    }
335
336    # return if the field is empty (e.g. initial screen)
337    return if !$DynamicFieldValues{ $Prefix . 'Used' }
338        && !$DynamicFieldValues{ $Prefix . 'Year' }
339        && !$DynamicFieldValues{ $Prefix . 'Month' }
340        && !$DynamicFieldValues{ $Prefix . 'Day' }
341        && !$DynamicFieldValues{ $Prefix . 'Hour' }
342        && !$DynamicFieldValues{ $Prefix . 'Minute' };
343
344    # check if need and can transform dates
345    # transform the dates early for ReturnValueStructure or ManualTimeStamp Bug#8452
346    if ( $Param{TransformDates} && $Param{LayoutObject} ) {
347
348        # transform time stamp based on user time zone
349        %DynamicFieldValues = $Param{LayoutObject}->TransformDateSelection(
350            %DynamicFieldValues,
351            Prefix => $Prefix,
352        );
353    }
354
355    # check if return value structure is needed
356    if ( defined $Param{ReturnValueStructure} && $Param{ReturnValueStructure} eq '1' ) {
357        return \%DynamicFieldValues;
358    }
359
360    # check if return template structure is needed
361    if ( defined $Param{ReturnTemplateStructure} && $Param{ReturnTemplateStructure} eq '1' ) {
362        return \%DynamicFieldValues;
363    }
364
365    # add seconds as 0 to the DynamicFieldValues hash
366    $DynamicFieldValues{ 'DynamicField_' . $Param{DynamicFieldConfig}->{Name} . 'Second' } = 0;
367
368    my $ManualTimeStamp = '';
369
370    if ( $DynamicFieldValues{ $Prefix . 'Used' } ) {
371
372        # add a leading zero for date parts that could be less than ten to generate a correct
373        # time stamp
374        for my $Type (qw(Month Day Hour Minute Second)) {
375            $DynamicFieldValues{ $Prefix . $Type } = sprintf "%02d",
376                $DynamicFieldValues{ $Prefix . $Type };
377        }
378
379        my $Year   = $DynamicFieldValues{ $Prefix . 'Year' }   || '0000';
380        my $Month  = $DynamicFieldValues{ $Prefix . 'Month' }  || '00';
381        my $Day    = $DynamicFieldValues{ $Prefix . 'Day' }    || '00';
382        my $Hour   = $DynamicFieldValues{ $Prefix . 'Hour' }   || '00';
383        my $Minute = $DynamicFieldValues{ $Prefix . 'Minute' } || '00';
384        my $Second = $DynamicFieldValues{ $Prefix . 'Second' } || '00';
385
386        $ManualTimeStamp =
387            $Year . '-' . $Month . '-' . $Day . ' '
388            . $Hour . ':' . $Minute . ':' . $Second;
389    }
390
391    return $ManualTimeStamp;
392}
393
394sub EditFieldValueValidate {
395    my ( $Self, %Param ) = @_;
396
397    # get the field value from the http request
398    my $Value = $Self->EditFieldValueGet(
399        DynamicFieldConfig   => $Param{DynamicFieldConfig},
400        ParamObject          => $Param{ParamObject},
401        ReturnValueStructure => 1,
402    );
403
404    # on normal basis Used field could be empty but if there was no value from EditFieldValueGet()
405    # it must be an error
406    if ( !defined $Value ) {
407        return {
408            ServerError  => 1,
409            ErrorMessage => 'Invalid Date!'
410        };
411    }
412
413    my $ServerError;
414    my $ErrorMessage;
415
416    # set the date time prefix as field name
417    my $Prefix = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
418
419    # date restriction
420    my $DateRestriction = $Param{DynamicFieldConfig}->{Config}->{DateRestriction};
421
422    # perform necessary validations
423    if ( $Param{Mandatory} && !$Value->{ $Prefix . 'Used' } ) {
424        $ServerError = 1;
425    }
426
427    if ( $Value->{ $Prefix . 'Used' } && $DateRestriction ) {
428
429        my $Year   = $Value->{ $Prefix . 'Year' }   || '0000';
430        my $Month  = $Value->{ $Prefix . 'Month' }  || '00';
431        my $Day    = $Value->{ $Prefix . 'Day' }    || '00';
432        my $Hour   = $Value->{ $Prefix . 'Hour' }   || '00';
433        my $Minute = $Value->{ $Prefix . 'Minute' } || '00';
434        my $Second = $Value->{ $Prefix . 'Second' } || '00';
435
436        my $ManualTimeStamp =
437            $Year . '-' . $Month . '-' . $Day . ' '
438            . $Hour . ':' . $Minute . ':' . $Second;
439
440        # get time object
441        my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
442
443        my $SystemTime = $DateTimeObject->ToEpoch();
444
445        my $ValueSystemTime = $DateTimeObject->Set(
446            String => $ManualTimeStamp,
447        );
448        $ValueSystemTime = $ValueSystemTime ? $DateTimeObject->ToEpoch() : undef;
449
450        if ( $DateRestriction eq 'DisableFutureDates' && $ValueSystemTime > $SystemTime ) {
451            $ServerError  = 1;
452            $ErrorMessage = "Invalid date (need a past date)!";
453        }
454        elsif ( $DateRestriction eq 'DisablePastDates' && $ValueSystemTime < $SystemTime ) {
455            $ServerError  = 1;
456            $ErrorMessage = "Invalid date (need a future date)!";
457        }
458    }
459
460    # create resulting structure
461    my $Result = {
462        ServerError  => $ServerError,
463        ErrorMessage => $ErrorMessage,
464    };
465
466    return $Result;
467}
468
469sub DisplayValueRender {
470    my ( $Self, %Param ) = @_;
471
472    my $Value = '';
473
474    # convert date to localized string
475    if ( defined $Param{Value} ) {
476        $Value = $Param{LayoutObject}->{LanguageObject}->FormatTimeString(
477            $Param{Value},
478            'DateFormat',
479            'NoSeconds',
480        );
481    }
482
483    # in this Driver there is no need for HTMLOutput
484    # Title is always equal to Value
485    my $Title = $Value;
486
487    # set field link form config
488    my $Link        = $Param{DynamicFieldConfig}->{Config}->{Link}        || '';
489    my $LinkPreview = $Param{DynamicFieldConfig}->{Config}->{LinkPreview} || '';
490
491    my $Data = {
492        Value       => $Value,
493        Title       => $Title,
494        Link        => $Link,
495        LinkPreview => $LinkPreview,
496    };
497
498    return $Data;
499}
500
501sub SearchFieldRender {
502    my ( $Self, %Param ) = @_;
503
504    # take config from field config
505    my $FieldConfig = $Param{DynamicFieldConfig}->{Config};
506    my $FieldName   = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name};
507
508    # set the default type
509    $Param{Type} ||= 'TimeSlot';
510
511    # add type to FieldName
512    $FieldName .= $Param{Type};
513
514    my $FieldLabel = $Param{DynamicFieldConfig}->{Label};
515
516    my $Value;
517
518    my %DefaultValue;
519
520    if ( defined $Param{DefaultValue} ) {
521        my @Items = split /;/, $Param{DefaultValue};
522
523# format example of the key name for TimePoint:
524#
525# Search_DynamicField_DateTest1TimePointFormat=week;Search_DynamicField_DateTest1TimePointStart=Before;Search_DynamicField_DateTest1TimePointValue=7;
526
527# format example of the key name for TimeSlot:
528#
529# Search_DynamicField_DateTest1TimeSlotStartYear=1974;Search_DynamicField_DateTest1TimeSlotStartMonth=01;Search_DynamicField_DateTest1TimeSlotStartDay=26;
530# Search_DynamicField_DateTest1TimeSlotStartHour=00;Search_DynamicField_DateTest1TimeSlotStartMinute=00;Search_DynamicField_DateTest1TimeSlotStartSecond=00;
531# Search_DynamicField_DateTest1TimeSlotStopYear=2013;Search_DynamicField_DateTest1TimeSlotStopMonth=01;Search_DynamicField_DateTest1TimeSlotStopDay=26;
532# Search_DynamicField_DateTest1TimeSlotStopHour=23;Search_DynamicField_DateTest1TimeSlotStopMinute=59;Search_DynamicField_DateTest1TimeSlotStopSecond=59;
533
534        my $KeyName = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name} . $Param{Type};
535
536        ITEM:
537        for my $Item (@Items) {
538            my ( $Key, $Value ) = split /=/, $Item;
539
540            # only handle keys that match the current type
541            next ITEM if $Key !~ m{ $Param{Type} }xms;
542
543            if ( $Param{Type} eq 'TimePoint' ) {
544
545                if ( $Key eq $KeyName . 'Format' ) {
546                    $DefaultValue{Format}->{$Key} = $Value;
547                }
548                elsif ( $Key eq $KeyName . 'Start' ) {
549                    $DefaultValue{Start}->{$Key} = $Value;
550                }
551                elsif ( $Key eq $KeyName . 'Value' ) {
552                    $DefaultValue{Value}->{$Key} = $Value;
553                }
554
555                next ITEM;
556            }
557
558            if ( $Key =~ m{Start} ) {
559                $DefaultValue{ValueStart}->{$Key} = $Value;
560            }
561            elsif ( $Key =~ m{Stop} ) {
562                $DefaultValue{ValueStop}->{$Key} = $Value;
563            }
564        }
565    }
566
567    # set the field value
568    if (%DefaultValue) {
569        $Value = \%DefaultValue;
570    }
571
572    # get the field value, this function is always called after the profile is loaded
573    my $FieldValues = $Self->SearchFieldValueGet(
574        %Param,
575    );
576
577    if (
578        defined $FieldValues
579        && $Param{Type} eq 'TimeSlot'
580        && defined $FieldValues->{ValueStart}
581        && defined $FieldValues->{ValueStop}
582        )
583    {
584        $Value = $FieldValues;
585    }
586
587    elsif (
588        defined $FieldValues
589        && $Param{Type} eq 'TimePoint'
590        && defined $FieldValues->{Format}
591        && defined $FieldValues->{Start}
592        && defined $FieldValues->{Value}
593        )
594    {
595        $Value = $FieldValues;
596    }
597
598    # check and set class if necessary
599    my $FieldClass = 'DynamicFieldDateTime';
600
601    # set as checked if necessary
602    my $FieldChecked = ( defined $Value->{$FieldName} && $Value->{$FieldName} == 1 ? 'checked="checked"' : '' );
603
604    my $HTMLString = <<"EOF";
605    <input type="hidden" id="$FieldName" name="$FieldName" value="1"/>
606EOF
607
608    if ( $Param{ConfirmationCheckboxes} ) {
609        $HTMLString = <<"EOF";
610    <input type="checkbox" id="$FieldName" name="$FieldName" value="1" $FieldChecked/>
611EOF
612    }
613
614    # build HTML for TimePoint
615    if ( $Param{Type} eq 'TimePoint' ) {
616
617        $HTMLString .= $Param{LayoutObject}->BuildSelection(
618            Data => {
619                'Before' => Translatable('more than ... ago'),
620                'Last'   => Translatable('within the last ...'),
621                'Next'   => Translatable('within the next ...'),
622                'After'  => Translatable('in more than ...'),
623            },
624            Sort           => 'IndividualKey',
625            SortIndividual => [ 'Before', 'Last', 'Next', 'After' ],
626            Name           => $FieldName . 'Start',
627            SelectedID     => $Value->{Start}->{ $FieldName . 'Start' } || 'Last',
628        );
629        $HTMLString .= ' ' . $Param{LayoutObject}->BuildSelection(
630            Data       => [ 1 .. 59 ],
631            Name       => $FieldName . 'Value',
632            SelectedID => $Value->{Value}->{ $FieldName . 'Value' } || 1,
633        );
634        $HTMLString .= ' ' . $Param{LayoutObject}->BuildSelection(
635            Data => {
636                minute => Translatable('minute(s)'),
637                hour   => Translatable('hour(s)'),
638                day    => Translatable('day(s)'),
639                week   => Translatable('week(s)'),
640                month  => Translatable('month(s)'),
641                year   => Translatable('year(s)'),
642            },
643            Name       => $FieldName . 'Format',
644            SelectedID => $Value->{Format}->{ $FieldName . 'Format' } || 'day',
645        );
646
647        my $AdditionalText;
648        if ( $Param{UseLabelHints} ) {
649            $AdditionalText = Translatable('before/after');
650        }
651
652        # call EditLabelRender on the common backend
653        my $LabelString = $Self->EditLabelRender(
654            %Param,
655            FieldName      => $FieldName,
656            AdditionalText => $AdditionalText,
657        );
658
659        my $Data = {
660            Field => $HTMLString,
661            Label => $LabelString,
662        };
663
664        return $Data;
665    }
666
667    # to set the years range
668    my %YearsPeriodRange;
669    if ( defined $FieldConfig->{YearsPeriod} && $FieldConfig->{YearsPeriod} eq '1' ) {
670        %YearsPeriodRange = (
671            YearPeriodPast   => $FieldConfig->{YearsInPast}   || 0,
672            YearPeriodFuture => $FieldConfig->{YearsInFuture} || 0,
673        );
674    }
675
676    # build HTML for start value set
677    $HTMLString .= $Param{LayoutObject}->BuildDateSelection(
678        %Param,
679        Prefix               => $FieldName . 'Start',
680        Format               => 'DateInputFormatLong',
681        $FieldName . 'Class' => $FieldClass,
682        DiffTime             => -( ( 60 * 60 * 24 ) * 30 ),
683        Validate             => 1,
684        %{ $Value->{ValueStart} },
685        %YearsPeriodRange,
686        OverrideTimeZone => 1,
687    );
688
689    # to put a line break between the two search dates
690    my $LineBreak = ' <br/>';
691
692    # in screens where the confirmation checkboxes is set, there is no need to render the filed in
693    # two lines (e.g. AdminGenericAgentn CustomerTicketSearch)
694    if ( $Param{ConfirmationCheckboxes} ) {
695        $LineBreak = '';
696    }
697
698    $HTMLString .= ' ' . $Param{LayoutObject}->{LanguageObject}->Translate("and") . "$LineBreak\n";
699
700    # build HTML for stop value set
701    $HTMLString .= $Param{LayoutObject}->BuildDateSelection(
702        %Param,
703        Prefix               => $FieldName . 'Stop',
704        Format               => 'DateInputFormatLong',
705        $FieldName . 'Class' => $FieldClass,
706        DiffTime             => +( ( 60 * 60 * 24 ) * 30 ),
707        Validate             => 1,
708        %{ $Value->{ValueStop} },
709        %YearsPeriodRange,
710        OverrideTimeZone => 1,
711    );
712
713    my $AdditionalText;
714    if ( $Param{UseLabelHints} ) {
715        $AdditionalText = Translatable('between');
716    }
717
718    # call EditLabelRender on the common Driver
719    my $LabelString = $Self->EditLabelRender(
720        %Param,
721        FieldName      => $FieldName,
722        AdditionalText => $AdditionalText,
723    );
724
725    my $Data = {
726        Field => $HTMLString,
727        Label => $LabelString,
728    };
729
730    return $Data;
731}
732
733sub SearchFieldValueGet {
734    my ( $Self, %Param ) = @_;
735
736    # set the Prefix as the dynamic field name
737    my $Prefix = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name};
738
739    # set the default type
740    $Param{Type} ||= 'TimeSlot';
741
742    # add type to prefix
743    $Prefix .= $Param{Type};
744
745    if ( $Param{Type} eq 'TimePoint' ) {
746
747        # get dynamic field value
748        my %DynamicFieldValues;
749        for my $Type (qw(Start Value Format)) {
750
751            # get dynamic field value form param object
752            if ( defined $Param{ParamObject} ) {
753
754                # return if value was not checked (useful in customer interface)
755                return if !$Param{ParamObject}->GetParam( Param => $Prefix );
756
757                $DynamicFieldValues{ $Prefix . $Type } = $Param{ParamObject}->GetParam(
758                    Param => $Prefix . $Type,
759                );
760            }
761
762            # otherwise get the value from the profile
763            elsif ( defined $Param{Profile} ) {
764
765                # return if value was not checked (useful in customer interface)
766                return if !$Param{Profile}->{$Prefix};
767
768                $DynamicFieldValues{ $Prefix . $Type } = $Param{Profile}->{ $Prefix . $Type };
769            }
770            else {
771                return;
772            }
773        }
774
775        # return if the field is empty (e.g. initial screen)
776        return if !$DynamicFieldValues{ $Prefix . 'Start' }
777            && !$DynamicFieldValues{ $Prefix . 'Value' }
778            && !$DynamicFieldValues{ $Prefix . 'Format' };
779
780        $DynamicFieldValues{$Prefix} = 1;
781
782        # check if return value structure is needed
783        if ( defined $Param{ReturnProfileStructure} && $Param{ReturnProfileStructure} eq '1' ) {
784            return \%DynamicFieldValues;
785        }
786
787        return {
788            Format => {
789                $Prefix . 'Format' => $DynamicFieldValues{ $Prefix . 'Format' } || 'Last',
790            },
791            Start => {
792                $Prefix . 'Start' => $DynamicFieldValues{ $Prefix . 'Start' } || 'day',
793            },
794            Value => {
795                $Prefix . 'Value' => $DynamicFieldValues{ $Prefix . 'Value' } || 1,
796            },
797            $Prefix => 1,
798        };
799    }
800
801    # get dynamic field value
802    my %DynamicFieldValues;
803    for my $Type (qw(Start Stop)) {
804        for my $Part (qw(Year Month Day Hour Minute)) {
805
806            # get dynamic field value from param object
807            if ( defined $Param{ParamObject} ) {
808
809                # return if value was not checked (useful in customer interface)
810                return if !$Param{ParamObject}->GetParam( Param => $Prefix );
811
812                $DynamicFieldValues{ $Prefix . $Type . $Part } = $Param{ParamObject}->GetParam(
813                    Param => $Prefix . $Type . $Part,
814                );
815            }
816
817            # otherwise get the value from the profile
818            elsif ( defined $Param{Profile} ) {
819
820                # return if value was not checked (useful in customer interface)
821                return if !$Param{Profile}->{$Prefix};
822
823                $DynamicFieldValues{ $Prefix . $Type . $Part } = $Param{Profile}->{ $Prefix . $Type . $Part };
824            }
825            else {
826                return;
827            }
828        }
829    }
830
831    # return if the field is empty (e.g. initial screen)
832    return if !$DynamicFieldValues{ $Prefix . 'StartYear' }
833        && !$DynamicFieldValues{ $Prefix . 'StartMonth' }
834        && !$DynamicFieldValues{ $Prefix . 'StartDay' }
835        && !$DynamicFieldValues{ $Prefix . 'StopYear' }
836        && !$DynamicFieldValues{ $Prefix . 'StopMonth' }
837        && !$DynamicFieldValues{ $Prefix . 'StopDay' };
838
839    $DynamicFieldValues{ $Prefix . 'StartSecond' } = '00';
840    $DynamicFieldValues{ $Prefix . 'StopSecond' }  = '59';
841
842    $DynamicFieldValues{$Prefix} = 1;
843
844    # check if return value structure is needed
845    if ( defined $Param{ReturnProfileStructure} && $Param{ReturnProfileStructure} eq '1' ) {
846        return \%DynamicFieldValues;
847    }
848
849    # add a leading zero for date parts that could be less than ten to generate a correct
850    # time stamp
851    for my $Type (qw(Start Stop)) {
852        for my $Part (qw(Month Day Hour Minute Second)) {
853            $DynamicFieldValues{ $Prefix . $Type . $Part } = sprintf "%02d",
854                $DynamicFieldValues{ $Prefix . $Type . $Part };
855        }
856    }
857
858    my $ValueStart = {
859        $Prefix . 'StartYear'   => $DynamicFieldValues{ $Prefix . 'StartYear' }   || '0000',
860        $Prefix . 'StartMonth'  => $DynamicFieldValues{ $Prefix . 'StartMonth' }  || '00',
861        $Prefix . 'StartDay'    => $DynamicFieldValues{ $Prefix . 'StartDay' }    || '00',
862        $Prefix . 'StartHour'   => $DynamicFieldValues{ $Prefix . 'StartHour' }   || '00',
863        $Prefix . 'StartMinute' => $DynamicFieldValues{ $Prefix . 'StartMinute' } || '00',
864        $Prefix . 'StartSecond' => $DynamicFieldValues{ $Prefix . 'StartSecond' } || '00',
865    };
866
867    my $ValueStop = {
868        $Prefix . 'StopYear'   => $DynamicFieldValues{ $Prefix . 'StopYear' }   || '0000',
869        $Prefix . 'StopMonth'  => $DynamicFieldValues{ $Prefix . 'StopMonth' }  || '00',
870        $Prefix . 'StopDay'    => $DynamicFieldValues{ $Prefix . 'StopDay' }    || '00',
871        $Prefix . 'StopHour'   => $DynamicFieldValues{ $Prefix . 'StopHour' }   || '00',
872        $Prefix . 'StopMinute' => $DynamicFieldValues{ $Prefix . 'StopMinute' } || '00',
873        $Prefix . 'StopSecond' => $DynamicFieldValues{ $Prefix . 'StopSecond' } || '00',
874    };
875
876    return {
877        $Prefix    => 1,
878        ValueStart => $ValueStart,
879        ValueStop  => $ValueStop,
880    };
881}
882
883sub SearchFieldPreferences {
884    my ( $Self, %Param ) = @_;
885
886    my @Preferences = (
887        {
888            Type        => 'TimePoint',
889            LabelSuffix => 'before/after',
890        },
891        {
892            Type        => 'TimeSlot',
893            LabelSuffix => 'between',
894        },
895    );
896
897    return \@Preferences;
898}
899
900sub SearchFieldParameterBuild {
901    my ( $Self, %Param ) = @_;
902
903    # set the default type
904    $Param{Type} ||= 'TimeSlot';
905
906    # get field value
907    my $Value = $Self->SearchFieldValueGet(%Param);
908
909    my $DisplayValue;
910
911    if ( defined $Value && !$Value ) {
912        $DisplayValue = '';
913    }
914
915    # do not search if value was not checked (useful for customer interface)
916    if ( !$Value ) {
917        return {
918            Parameter => {
919                Equals => $Value,
920            },
921            Display => $DisplayValue,
922        };
923    }
924
925    # search for a wild card in the value
926    if ( $Value && IsHashRefWithData($Value) ) {
927
928        my $Prefix = 'Search_DynamicField_' . $Param{DynamicFieldConfig}->{Name};
929
930        $Prefix .= $Param{Type};
931
932        if (
933            $Param{Type} eq 'TimePoint'
934            && $Value->{Start}->{ $Prefix . 'Start' }
935            && $Value->{Format}->{ $Prefix . 'Format' }
936            && $Value->{Value}->{ $Prefix . 'Value' }
937            && $Value->{$Prefix}
938            )
939        {
940
941            # to store the search parameters
942            my %Parameter;
943
944            # store in local variables for easier handling
945            my $Format = $Value->{Format}->{ $Prefix . 'Format' };
946            my $Start  = $Value->{Start}->{ $Prefix . 'Start' };
947            my $Value  = $Value->{Value}->{ $Prefix . 'Value' };
948
949            my $DiffTimeMinutes = 0;
950            if ( $Format eq 'minute' ) {
951                $DiffTimeMinutes = $Value;
952            }
953            elsif ( $Format eq 'hour' ) {
954                $DiffTimeMinutes = $Value * 60;
955            }
956            elsif ( $Format eq 'day' ) {
957                $DiffTimeMinutes = $Value * 60 * 24;
958            }
959            elsif ( $Format eq 'week' ) {
960                $DiffTimeMinutes = $Value * 60 * 24 * 7;
961            }
962            elsif ( $Format eq 'month' ) {
963                $DiffTimeMinutes = $Value * 60 * 24 * 30;
964            }
965            elsif ( $Format eq 'year' ) {
966                $DiffTimeMinutes = $Value * 60 * 24 * 365;
967            }
968
969            # get time object
970            my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
971
972            # get the current time in epoch seconds and as time-stamp
973            my $Now          = $DateTimeObject->ToEpoch();
974            my $NowTimeStamp = $DateTimeObject->ToString();
975
976            # calculate difference time seconds
977            my $DiffTimeSeconds = $DiffTimeMinutes * 60;
978
979            my $DisplayValue = '';
980
981            # define to search before or after that time stamp
982            if ( $Start eq 'Before' ) {
983
984                # we must subtract the difference because it is in the past
985                my $DateTimeObjectBefore = $Kernel::OM->Create(
986                    'Kernel::System::DateTime',
987                    ObjectParams => {
988                        Epoch => $Now - $DiffTimeSeconds,
989                    }
990                );
991
992                # only search dates in the past (before the time stamp)
993                $Parameter{SmallerThanEquals} = $DateTimeObjectBefore->ToString();
994
995                # set the display value
996                $DisplayValue = '<= ' . $DateTimeObjectBefore->ToString();
997            }
998            elsif ( $Start eq 'Last' ) {
999
1000                # we must subtract the differences because it is in the past
1001                my $DateTimeObjectLast = $Kernel::OM->Create(
1002                    'Kernel::System::DateTime',
1003                    ObjectParams => {
1004                        Epoch => $Now - $DiffTimeSeconds,
1005                    }
1006                );
1007
1008                # search dates in the past (after the time stamp and up to now)
1009                $Parameter{GreaterThanEquals} = $DateTimeObjectLast->ToString();
1010                $Parameter{SmallerThanEquals} = $NowTimeStamp;
1011
1012                # set the display value
1013                $DisplayValue = $DateTimeObjectLast->ToString() . ' - ' . $NowTimeStamp;
1014            }
1015            elsif ( $Start eq 'Next' ) {
1016
1017                # we must add the difference because it is in the future
1018                my $DateTimeObjectNext = $Kernel::OM->Create(
1019                    'Kernel::System::DateTime',
1020                    ObjectParams => {
1021                        Epoch => $Now + $DiffTimeSeconds,
1022                    }
1023                );
1024
1025                # search dates in the future (after now and up to the time stamp)
1026                $Parameter{GreaterThanEquals} = $NowTimeStamp;
1027                $Parameter{SmallerThanEquals} = $DateTimeObjectNext->ToString();
1028
1029                # set the display value
1030                $DisplayValue = $NowTimeStamp . ' - ' . $DateTimeObjectNext->ToString();
1031            }
1032            elsif ( $Start eq 'After' ) {
1033
1034                # we must add the difference because it is in the future
1035                my $DateTimeObjectAfter = $Kernel::OM->Create(
1036                    'Kernel::System::DateTime',
1037                    ObjectParams => {
1038                        Epoch => $Now + $DiffTimeSeconds,
1039                    }
1040                );
1041
1042                # only search dates in the future (after the time stamp)
1043                $Parameter{GreaterThanEquals} = $DateTimeObjectAfter->ToString();
1044
1045                # set the display value
1046                $DisplayValue = '>= ' . $DateTimeObjectAfter->ToString();
1047            }
1048
1049            # return search parameter structure
1050            return {
1051                Parameter => \%Parameter,
1052                Display   => $DisplayValue,
1053            };
1054        }
1055
1056        my $ValueStart = $Value->{ValueStart}->{ $Prefix . 'StartYear' } . '-'
1057            . $Value->{ValueStart}->{ $Prefix . 'StartMonth' } . '-'
1058            . $Value->{ValueStart}->{ $Prefix . 'StartDay' } . ' '
1059            . $Value->{ValueStart}->{ $Prefix . 'StartHour' } . ':'
1060            . $Value->{ValueStart}->{ $Prefix . 'StartMinute' } . ':'
1061            . $Value->{ValueStart}->{ $Prefix . 'StartSecond' };
1062
1063        my $ValueStop = $Value->{ValueStop}->{ $Prefix . 'StopYear' } . '-'
1064            . $Value->{ValueStop}->{ $Prefix . 'StopMonth' } . '-'
1065            . $Value->{ValueStop}->{ $Prefix . 'StopDay' } . ' '
1066            . $Value->{ValueStop}->{ $Prefix . 'StopHour' } . ':'
1067            . $Value->{ValueStop}->{ $Prefix . 'StopMinute' } . ':'
1068            . $Value->{ValueStop}->{ $Prefix . 'StopSecond' };
1069
1070        # return search parameter structure
1071        return {
1072            Parameter => {
1073                GreaterThanEquals => $ValueStart,
1074                SmallerThanEquals => $ValueStop,
1075            },
1076            Display => $ValueStart . ' - ' . $ValueStop,
1077        };
1078    }
1079
1080    return;
1081}
1082
1083sub StatsFieldParameterBuild {
1084    my ( $Self, %Param ) = @_;
1085
1086    return {
1087        Name             => $Param{DynamicFieldConfig}->{Label},
1088        Element          => 'DynamicField_' . $Param{DynamicFieldConfig}->{Name},
1089        TimePeriodFormat => 'DateInputFormatLong',
1090        Block            => 'Time',
1091    };
1092}
1093
1094sub StatsSearchFieldParameterBuild {
1095    my ( $Self, %Param ) = @_;
1096
1097    my $Value = $Param{Value};
1098
1099    # set operator
1100    my $Operator = $Param{Operator};
1101    return {} if !$Operator;
1102
1103    return {
1104        $Operator => $Value,
1105    };
1106}
1107
1108sub ReadableValueRender {
1109    my ( $Self, %Param ) = @_;
1110
1111    my $Value = defined $Param{Value} ? $Param{Value} : '';
1112
1113    # only keep date and time without seconds or milliseconds
1114    $Value =~ s{\A (\d{4} - \d{2} - \d{2} [ ] \d{2} : \d{2} ) }{$1}xms;
1115
1116    # Title is always equal to Value
1117    my $Title = $Value;
1118
1119    my $Data = {
1120        Value => $Value,
1121        Title => $Title,
1122    };
1123
1124    return $Data;
1125}
1126
1127sub TemplateValueTypeGet {
1128    my ( $Self, %Param ) = @_;
1129
1130    my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
1131
1132    # set the field types
1133    my $EditValueType   = 'SCALAR';
1134    my $SearchValueType = 'SCALAR';
1135
1136    # return the correct structure
1137    if ( $Param{FieldType} eq 'Edit' ) {
1138        return {
1139            $FieldName => $EditValueType,
1140        };
1141    }
1142    elsif ( $Param{FieldType} eq 'Search' ) {
1143        return {
1144            'Search_' . $FieldName => $SearchValueType,
1145        };
1146    }
1147    else {
1148        return {
1149            $FieldName             => $EditValueType,
1150            'Search_' . $FieldName => $SearchValueType,
1151        };
1152    }
1153}
1154
1155sub RandomValueSet {
1156    my ( $Self, %Param ) = @_;
1157
1158    my $YearValue   = int( rand(40) ) + 1_990;
1159    my $MonthValue  = int( rand(9) ) + 1;
1160    my $DayValue    = int( rand(10) ) + 10;
1161    my $HourValue   = int( rand(12) ) + 10;
1162    my $MinuteValue = int( rand(30) ) + 10;
1163    my $SecondValue = int( rand(30) ) + 10;
1164
1165    my $Value = $YearValue . '-0' . $MonthValue . '-' . $DayValue . ' '
1166        . $HourValue . ':' . $MinuteValue . ':' . $SecondValue;
1167
1168    my $Success = $Self->ValueSet(
1169        %Param,
1170        Value => $Value,
1171    );
1172
1173    if ( !$Success ) {
1174        return {
1175            Success => 0,
1176        };
1177    }
1178    return {
1179        Success => 1,
1180        Value   => $Value,
1181    };
1182}
1183
1184sub ObjectMatch {
1185    my ( $Self, %Param ) = @_;
1186
1187    my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
1188
1189    # not supported
1190    return 0;
1191}
1192
1193sub HistoricalValuesGet {
1194    my ( $Self, %Param ) = @_;
1195
1196    # get historical values from database
1197    my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet(
1198        FieldID   => $Param{DynamicFieldConfig}->{ID},
1199        ValueType => 'DateTime',
1200    );
1201
1202    # return the historical values from database
1203    return $HistoricalValues;
1204}
1205
1206sub ValueLookup {
1207    my ( $Self, %Param ) = @_;
1208
1209    my $Value = defined $Param{Key} ? $Param{Key} : '';
1210
1211    # check if a translation is possible
1212    if ( defined $Param{LanguageObject} ) {
1213
1214        # translate value
1215        $Value = $Param{LanguageObject}->FormatTimeString(
1216            $Value,
1217            'DateFormat',
1218            'NoSeconds',
1219        );
1220    }
1221
1222    return $Value;
1223}
1224
12251;
1226
1227=head1 TERMS AND CONDITIONS
1228
1229This software is part of the OTRS project (L<https://otrs.org/>).
1230
1231This software comes with ABSOLUTELY NO WARRANTY. For details, see
1232the enclosed file COPYING for license information (GPL). If you
1233did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1234
1235=cut
1236