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## nofilter(TidyAll::Plugin::OTRS::Perl::LayoutObject)
9package Kernel::System::SysConfig::ValueType::Date;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15
16use parent qw(Kernel::System::SysConfig::BaseValueType);
17
18our @ObjectDependencies = (
19    'Kernel::Config',
20    'Kernel::Language',
21    'Kernel::Output::HTML::Layout',
22    'Kernel::System::Log',
23    'Kernel::System::JSON',
24    'Kernel::System::User',
25);
26
27=head1 NAME
28
29Kernel::System::SysConfig::ValueType::Date - System configuration date value type backed.
30
31=head1 PUBLIC INTERFACE
32
33=head2 new()
34
35Create an object. Do not use it directly, instead use:
36
37    use Kernel::System::ObjectManager;
38    local $Kernel::OM = Kernel::System::ObjectManager->new();
39    my $ValueTypeObject = $Kernel::OM->Get('Kernel::System::SysConfig::ValueType::Date');
40
41=cut
42
43sub new {
44    my ( $Type, %Param ) = @_;
45
46    # Allocate new hash for object.
47    my $Self = {};
48    bless( $Self, $Type );
49
50    return $Self;
51}
52
53=head2 SettingEffectiveValueCheck()
54
55Check if provided EffectiveValue matches structure defined in XMLContentParsed.
56
57    my %Result = $ValueTypeObject->SettingEffectiveValueCheck(
58        XMLContentParsed => {                           # (required)
59            Value => [
60                {
61                    'Item' => [
62                        {
63                            'Content'   => '2016-01-01',
64                            'ValueType' => 'Date',
65                        },
66                    ],
67                },
68            ],
69        },
70        EffectiveValue => '2016-02-02',                 # (optional)
71        UserID         => 1,                            # (required)
72    );
73
74Result:
75    %Result = (
76        EffectiveValue => '2016-02-03',                 # Note that EffectiveValue can be modified
77        Success => 1,
78        Error   => undef,
79    );
80
81=cut
82
83sub SettingEffectiveValueCheck {
84    my ( $Self, %Param ) = @_;
85
86    for my $Needed (qw(XMLContentParsed UserID)) {
87        if ( !$Param{$Needed} ) {
88            $Kernel::OM->Get('Kernel::System::Log')->Log(
89                Priority => 'error',
90                Message  => "Need $Needed!"
91            );
92
93            return;
94        }
95    }
96
97    my %Result = (
98        Success => 0,
99    );
100
101    # Data should be scalar.
102    if ( ref $Param{EffectiveValue} ) {
103        $Result{Error} = 'EffectiveValue for Date must be a scalar!';
104        return %Result;
105    }
106
107    if ( $Param{EffectiveValue} !~ m{\d{4}-\d{2}-\d{2}} ) {
108        $Result{Error} = "EffectiveValue for Date($Param{EffectiveValue}) must be in format YYYY-MM-DD!";
109        return %Result;
110    }
111
112    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
113        UserID => $Param{UserID},
114    );
115
116    my $OTRSTimeZone = $Kernel::OM->Get('Kernel::Config')->Get("OTRSTimeZone");
117    my $DateTimeObject;
118
119    if ( !$Preferences{UserTimeZone} || $Preferences{UserTimeZone} eq $OTRSTimeZone ) {
120        $DateTimeObject = $Kernel::OM->Create(
121            'Kernel::System::DateTime',
122            ObjectParams => {
123                TimeZone => $OTRSTimeZone,
124            },
125        );
126
127        my $SetSuccess = $DateTimeObject->Set( String => $Param{EffectiveValue} );
128
129        if ( !$SetSuccess ) {
130            $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate(
131                "EffectiveValue for Date($Param{EffectiveValue}) must be in format YYYY-MM-DD!",
132            );
133            return %Result;
134        }
135
136        $Result{EffectiveValue} = $Param{EffectiveValue};
137    }
138    else {
139        $DateTimeObject = $Kernel::OM->Create(
140            'Kernel::System::DateTime',
141            ObjectParams => {
142                TimeZone => $Preferences{UserTimeZone},
143            },
144        );
145
146        my $SetSuccess = $DateTimeObject->Set( String => $Param{EffectiveValue} );
147
148        if ( !$SetSuccess ) {
149            $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate(
150                "EffectiveValue for Date($Param{EffectiveValue}) must be in format YYYY-MM-DD!",
151            );
152            return %Result;
153        }
154
155        my $Success = $DateTimeObject->ToTimeZone(
156            TimeZone => $OTRSTimeZone,
157        );
158
159        if ($Success) {
160            $Result{EffectiveValue} = $DateTimeObject->ToString();
161        }
162        else {
163            $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate(
164                "System was not able to calculate user Date in OTRSTimeZone!"
165            );
166
167            $Kernel::OM->Get('Kernel::System::Log')->Log(
168                Priority => 'error',
169                Message  => "System was not able to calculate user Date in OTRSTimeZone!"
170            );
171        }
172    }
173
174    if ( !$DateTimeObject ) {
175        $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate(
176            "EffectiveValue for Date($Param{EffectiveValue}) must be in format YYYY-MM-DD!",
177        );
178        return %Result;
179    }
180
181    $Result{Success} = 1;
182
183    return %Result;
184}
185
186=head2 SettingRender()
187
188Extracts the effective value from a XML parsed setting.
189
190    my $SettingHTML = $ValueTypeObject->SettingRender(
191        Name           => 'SettingName',
192        EffectiveValue => '2016-02-02',     # (optional)
193        DefaultValue   => 'Product 5',      # (optional)
194        Class          => 'My class'        # (optional)
195        RW             => 1,                # (optional) Allow editing. Default 0.
196        Item           => [                 # (optional) XML parsed item
197            {
198                'ValueType' => 'Date',
199                'Content' => '2016-02-02',
200                'ValueRegex' => '',
201            },
202        ],
203        IsArray  => 1,                      # (optional) Item is part of the array
204        IsHash   => 1,                      # (optional) Item is part of the hash
205        IDSuffix => 1,                      # (optional) Suffix will be added to the element ID
206        SkipEffectiveValueCheck => 1,       # (optional) If enabled, system will not perform effective value check.
207                                            #            Default: 1.
208        UserID                  => 1,       # (required) UserID
209    );
210
211Returns:
212
213    $SettingHTML = '<div class "Field"...</div>';
214
215=cut
216
217sub SettingRender {
218    my ( $Self, %Param ) = @_;
219
220    for my $Needed (qw(Name UserID)) {
221        if ( !defined $Param{$Needed} ) {
222            $Kernel::OM->Get('Kernel::System::Log')->Log(
223                Priority => 'error',
224                Message  => "Need $Needed",
225            );
226            return;
227        }
228    }
229
230    $Param{Class} //= '';
231    $Param{Class} .= ' Date';
232    $Param{DefaultValue} //= '';
233
234    my $IDSuffix = $Param{IDSuffix} || '';
235
236    my $LanguageObject = $Kernel::OM->Get('Kernel::Language');
237
238    my $EffectiveValue = $Param{EffectiveValue};
239    if (
240        !defined $EffectiveValue
241        && $Param{Item}
242        && $Param{Item}->[0]->{Content}
243        )
244    {
245        $EffectiveValue = $Param{Item}->[0]->{Content};
246    }
247
248    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
249
250    # Check if there is Datepicker before we add it.
251    my $HasDatepicker = $LayoutObject->{HasDatepicker};
252
253    my $Name = $Param{Name} . $IDSuffix;
254
255    my %EffectiveValueCheck = (
256        Success => 1,
257    );
258
259    if ( !$Param{SkipEffectiveValueCheck} ) {
260        %EffectiveValueCheck = $Self->SettingEffectiveValueCheck(
261            EffectiveValue   => $EffectiveValue,
262            XMLContentParsed => {
263                Value => [
264                    {
265                        Item => $Param{Item},
266                    },
267                ],
268            },
269            UserID => $Param{UserID},
270        );
271    }
272
273    my $TimeZone = $Kernel::OM->Get('Kernel::Config')->Get("OTRSTimeZone");
274
275    my $DateTimeObject = $Kernel::OM->Create(
276        'Kernel::System::DateTime',
277        ObjectParams => {
278            String   => $EffectiveValue,
279            TimeZone => $TimeZone,
280        },
281    );
282
283    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
284        UserID => $Param{UserID},
285    );
286
287    if ( $Preferences{UserTimeZone} ) {
288        my $Success = $DateTimeObject->ToTimeZone(
289            TimeZone => $Preferences{UserTimeZone},
290        );
291
292        $TimeZone = $Preferences{UserTimeZone};
293
294        if ( !$Success ) {
295            $Kernel::OM->Get('Kernel::System::Log')->Log(
296                Priority => 'error',
297                Message  => "System was not able to calculate DateTime in user timezone!"
298            );
299        }
300    }
301
302    my @Date = split m{-|\s|:}, $DateTimeObject->ToString();
303
304    my $HTML = "<div class='SettingContent'>\n";
305    $HTML .= $LayoutObject->BuildDateSelection(
306        Prefix           => $Name,
307        $Name . "Class"  => $Param{Class},
308        $Name . "Year"   => $Date[0],
309        $Name . "Month"  => $Date[1],
310        $Name . "Day"    => $Date[2],
311        YearDiff         => 10,
312        Format           => 'DateInputFormat',
313        Validate         => 1,
314        Disabled         => $Param{RW} ? 0 : 1,
315        OverrideTimeZone => 1,
316    );
317
318    my $TimeZoneText = $Kernel::OM->Get('Kernel::Language')->Translate("Time Zone");
319    $HTML .= "<span class='TimeZoneText'>$TimeZoneText: $TimeZone</span>\n";
320
321    if ( !$EffectiveValueCheck{Success} ) {
322        my $Message = $LanguageObject->Translate("Value is not correct! Please, consider updating this field.");
323
324        $HTML .= $Param{IsValid} ? "<div class='BadEffectiveValue'>\n" : "<div>\n";
325        $HTML .= "<p>* $Message</p>\n";
326        $HTML .= "</div>\n";
327    }
328
329    $HTML .= "</div>\n";
330
331    if ( !$Param{IsArray} && !$Param{IsHash} ) {
332        my $DefaultText = $LanguageObject->Translate('Default');
333
334        $HTML .= <<"EOF";
335                                <div class=\"WidgetMessage Bottom\">
336                                    $DefaultText: $Param{DefaultValue}
337                                </div>
338EOF
339    }
340
341    if ( $Param{IsAjax} && $LayoutObject->{_JSOnDocumentComplete} && $Param{RW} ) {
342        for my $JS ( @{ $LayoutObject->{_JSOnDocumentComplete} } ) {
343            $HTML .= "<script>$JS</script>";
344        }
345    }
346
347    if ( $Param{IsAjax} ) {
348
349       # Remove JS generated in BuildDateSelection() call (setting is disabled or it's already sent together with HTML).
350       # It also prevents multiple Datepicker initializations (if there are several on the page).
351        pop @{ $LayoutObject->{_JSOnDocumentComplete} };
352
353        if ( !$HasDatepicker ) {
354            my $VacationDays  = $LayoutObject->DatepickerGetVacationDays();
355            my $TextDirection = $LanguageObject->{TextDirection} || '';
356
357            my $JSONString = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
358                Data => {
359                    VacationDays => $VacationDays,
360                    IsRTL        => ( $TextDirection eq 'rtl' ) ? 1 : 0,
361                },
362            );
363
364            $HTML .= "<script>
365                Core.Config.Set('Datepicker', $JSONString);
366            </script>";
367
368            # If there are several DateTime settings, don't run this block again.
369            $LayoutObject->{HasDatepicker} = 1;
370        }
371    }
372
373    return $HTML;
374}
375
376=head2 AddItem()
377
378Generate HTML for new array/hash item.
379
380    my $HTML = $ValueTypeObject->AddItem(
381        Name           => 'SettingName',    (required) Name
382        DefaultItem    => {                 (optional) DefaultItem hash, if available
383            Content => '2017-01-01',
384            ValueType => 'Date',
385        },
386        IDSuffix       => '_Array1',        (optional) IDSuffix is needed for arrays and hashes.
387    );
388
389Returns:
390
391    $HTML = '<select class="Validate_DateMonth  Date" id="SettingName_Array1Month" ...';
392
393=cut
394
395sub AddItem {
396    my ( $Self, %Param ) = @_;
397
398    # Check needed stuff.
399    for my $Needed (qw(Name)) {
400        if ( !$Param{$Needed} ) {
401            $Kernel::OM->Get('Kernel::System::Log')->Log(
402                Priority => 'error',
403                Message  => "Need $Needed!",
404            );
405            return;
406        }
407    }
408
409    my $IDSuffix = $Param{IDSuffix} || '';
410    my $Class    = $Param{Class}    || '';
411    $Class .= ' Date Entry';
412
413    my $Name = $Param{Name} . $IDSuffix;
414
415    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
416    my $TimeZone     = $Kernel::OM->Get('Kernel::Config')->Get("OTRSTimeZone");
417
418    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
419        UserID => $Param{UserID},
420    );
421
422    my $DefaultValue;
423    if ( $Param{DefaultItem} ) {
424        my $DateTimeObject = $Kernel::OM->Create(
425            'Kernel::System::DateTime',
426            ObjectParams => {
427                String   => $Param{DefaultItem}->{Content},
428                TimeZone => $TimeZone,
429            },
430        );
431
432        if ( $Preferences{UserTimeZone} ) {
433            my $Success = $DateTimeObject->ToTimeZone(
434                TimeZone => $Preferences{UserTimeZone},
435            );
436
437            if ( !$Success ) {
438                $Kernel::OM->Get('Kernel::System::Log')->Log(
439                    Priority => 'error',
440                    Message  => "System was not able to calculate DateTime in user timezone!"
441                );
442            }
443        }
444
445        $DefaultValue = $DateTimeObject->ToString();
446    }
447
448    if ( $Preferences{UserTimeZone} ) {
449        $TimeZone = $Preferences{UserTimeZone};
450    }
451
452    my $Result;
453
454    if ($DefaultValue) {
455        my @Date = split m{-|\s|:}, $DefaultValue;
456
457        $Result = $LayoutObject->BuildDateSelection(
458            Prefix           => $Name,
459            $Name . "Year"   => $Date[0],
460            $Name . "Month"  => $Date[1],
461            $Name . "Day"    => $Date[2],
462            $Name . "Class"  => $Class,
463            YearDiff         => 10,
464            Format           => 'DateInputFormat',
465            Validate         => 1,
466            OverrideTimeZone => 1,
467        );
468    }
469    else {
470        $Result = $LayoutObject->BuildDateSelection(
471            Prefix           => $Name,
472            $Name . "Class"  => $Class,
473            YearDiff         => 10,
474            Format           => 'DateInputFormat',
475            Validate         => 1,
476            OverrideTimeZone => 1,
477        );
478    }
479
480    my $TimeZoneText = $Kernel::OM->Get('Kernel::Language')->Translate("Time Zone");
481    $Result .= "<span class='TimeZoneText'>$TimeZoneText: $TimeZone</span>\n";
482
483    for my $JS ( @{ $LayoutObject->{_JSOnDocumentComplete} } ) {
484        $Result .= "<script>$JS</script>";
485    }
486
487    return $Result;
488}
489
4901;
491
492=head1 TERMS AND CONDITIONS
493
494This software is part of the OTRS project (L<https://otrs.org/>).
495
496This software comes with ABSOLUTELY NO WARRANTY. For details, see
497the enclosed file COPYING for license information (GPL). If you
498did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
499
500=cut
501