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::DateTime;
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::DateTime - System configuration date-time 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::DateTime');
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 00:00:00',
64                            'ValueType' => 'DateTime',
65                        },
66                    ],
67                },
68            ],
69        },
70        EffectiveValue => '2016-02-02 01:12:22',                    # (optional)
71        UserID         => 1,                                        # (required)
72    );
73
74Result:
75    %Result = (
76        EffectiveValue => '2016-02-02 03:12:22',                    # 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 DateTime must be a scalar!';
104        return %Result;
105    }
106
107    if ( $Param{EffectiveValue} !~ m{\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}} ) {
108        $Result{Error} = 'EffectiveValue for DateTime must be in format YYYY-MM-DD hh:mm:ss!';
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 DateTime($Param{EffectiveValue}) must be in format YYYY-MM-DD hh:mm:ss!",
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 DateTime($Param{EffectiveValue}) must be in format YYYY-MM-DD hh:mm:ss!",
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 DateTime in OTRSTimeZone!"
165            );
166
167            $Kernel::OM->Get('Kernel::System::Log')->Log(
168                Priority => 'error',
169                Message  => "System was not able to calculate user DateTime in OTRSTimeZone!"
170            );
171        }
172    }
173
174    if ( !$DateTimeObject ) {
175        $Result{Error} = $Kernel::OM->Get('Kernel::Language')->Translate(
176            "EffectiveValue for DateTime($Param{EffectiveValue}) must be in format YYYY-MM-DD hh:mm:ss!",
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 01:00:59', # (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 01:00:59',
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        SkipEffectiveValueCheck => 1,       # (optional) If enabled, system will not perform effective value check.
206                                            #            Default: 1.
207        UserID                  => 1,       # (required) UserID
208    );
209
210Returns:
211
212    $SettingHTML = '<div class "Field"...</div>';
213
214=cut
215
216sub SettingRender {
217    my ( $Self, %Param ) = @_;
218
219    for my $Needed (qw(Name UserID)) {
220        if ( !defined $Param{$Needed} ) {
221            $Kernel::OM->Get('Kernel::System::Log')->Log(
222                Priority => 'error',
223                Message  => "Need $Needed",
224            );
225            return;
226        }
227    }
228
229    $Param{Class} //= '';
230    $Param{Class} .= ' DateTime';
231    $Param{DefaultValue} //= '';
232
233    my $IDSuffix = $Param{IDSuffix} || '';
234
235    my $LanguageObject = $Kernel::OM->Get('Kernel::Language');
236
237    my $EffectiveValue = $Param{EffectiveValue};
238    if (
239        !defined $EffectiveValue
240        && $Param{Item}
241        && $Param{Item}->[0]->{Content}
242        )
243    {
244        $EffectiveValue = $Param{Item}->[0]->{Content};
245    }
246
247    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
248
249    # Check if there is Datepicker before we add it.
250    my $HasDatepicker = $LayoutObject->{HasDatepicker};
251
252    my $Name = $Param{Name} . $IDSuffix;
253
254    my %EffectiveValueCheck = (
255        Success => 1,
256    );
257
258    if ( !$Param{SkipEffectiveValueCheck} ) {
259        %EffectiveValueCheck = $Self->SettingEffectiveValueCheck(
260            EffectiveValue   => $EffectiveValue,
261            XMLContentParsed => {
262                Value => [
263                    {
264                        Item => $Param{Item},
265                    },
266                ],
267            },
268            UserID => $Param{UserID},
269        );
270    }
271
272    my $TimeZone = $Kernel::OM->Get('Kernel::Config')->Get("OTRSTimeZone");
273
274    my $DateTimeObject = $Kernel::OM->Create(
275        'Kernel::System::DateTime',
276        ObjectParams => {
277            String   => $EffectiveValue,
278            TimeZone => $TimeZone,
279        },
280    );
281
282    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
283        UserID => $Param{UserID},
284    );
285
286    if ( $Preferences{UserTimeZone} ) {
287        my $Success = $DateTimeObject->ToTimeZone(
288            TimeZone => $Preferences{UserTimeZone},
289        );
290        $TimeZone = $Preferences{UserTimeZone};
291
292        if ( !$Success ) {
293            $Kernel::OM->Get('Kernel::System::Log')->Log(
294                Priority => 'error',
295                Message  => "System was not able to calculate DateTime in user timezone!"
296            );
297        }
298    }
299
300    my @Date = split m{-|\s|:}, $DateTimeObject->ToString();
301
302    my $HTML = "<div class='SettingContent'>\n";
303    $HTML .= $LayoutObject->BuildDateSelection(
304        Prefix           => $Name,
305        $Name . "Class"  => $Param{Class},
306        $Name . "Year"   => $Date[0],
307        $Name . "Month"  => $Date[1],
308        $Name . "Day"    => $Date[2],
309        $Name . "Hour"   => $Date[3],
310        $Name . "Minute" => $Date[4],
311        $Name . "Second" => $Date[5],
312        YearDiff         => 10,
313        Format           => 'DateInputFormatLong',
314        Validate         => 1,
315        Disabled         => $Param{RW} ? 0 : 1,
316        OverrideTimeZone => 1,
317    );
318
319    my $TimeZoneText = $Kernel::OM->Get('Kernel::Language')->Translate("Time Zone");
320    $HTML .= "<span class='TimeZoneText'>$TimeZoneText: $TimeZone</span>\n";
321
322    if ( !$EffectiveValueCheck{Success} ) {
323        my $Message = $LanguageObject->Translate("Value is not correct! Please, consider updating this field.");
324
325        $HTML .= $Param{IsValid} ? "<div class='BadEffectiveValue'>\n" : "<div>\n";
326        $HTML .= "<p>* $Message</p>\n";
327        $HTML .= "</div>\n";
328    }
329
330    $HTML .= "</div>\n";
331
332    if ( !$Param{IsArray} && !$Param{IsHash} ) {
333        my $DefaultText = $Kernel::OM->Get('Kernel::Language')->Translate('Default');
334
335        $HTML .= <<"EOF";
336                                <div class=\"WidgetMessage Bottom\">
337                                    $DefaultText: $Param{DefaultValue}
338                                </div>
339EOF
340    }
341
342    if ( $Param{IsAjax} && $LayoutObject->{_JSOnDocumentComplete} && $Param{RW} ) {
343        for my $JS ( @{ $LayoutObject->{_JSOnDocumentComplete} } ) {
344
345            $HTML .= "<script>$JS</script>";
346        }
347    }
348
349    if ( $Param{IsAjax} ) {
350
351       # Remove JS generated in BuildDateSelection() call (setting is disabled or it's already sent together with HTML).
352       # It also prevents multiple Datepicker initializations (if there are several on the page).
353        pop @{ $LayoutObject->{_JSOnDocumentComplete} };
354
355        if ( !$HasDatepicker ) {
356            my $VacationDays  = $LayoutObject->DatepickerGetVacationDays();
357            my $TextDirection = $LanguageObject->{TextDirection} || '';
358
359            my $JSONString = $Kernel::OM->Get('Kernel::System::JSON')->Encode(
360                Data => {
361                    VacationDays => $VacationDays,
362                    IsRTL        => ( $TextDirection eq 'rtl' ) ? 1 : 0,
363                },
364            );
365
366            $HTML .= "<script>
367                Core.Config.Set('Datepicker', $JSONString);
368            </script>";
369
370            # If there are several DateTime settings, don't run this block again.
371            $LayoutObject->{HasDatepicker} = 1;
372        }
373    }
374
375    return $HTML;
376}
377
378=head2 AddItem()
379
380Generate HTML for new array/hash item.
381
382    my $HTML = $ValueTypeObject->AddItem(
383        Name           => 'SettingName',    (required) Name
384        DefaultItem    => {                 (optional) DefaultItem hash, if available
385            Content => '2017-01-01 01:45:00',
386            ValueType => 'DateTime',
387        },
388        IDSuffix       => '_Array1',        (optional) IDSuffix is needed for arrays and hashes.
389    );
390
391Returns:
392
393    $HTML = '<select class="Validate_DateMonth  Date" id="SettingName_Array1Month" ...';
394
395=cut
396
397sub AddItem {
398    my ( $Self, %Param ) = @_;
399
400    # Check needed stuff.
401    for my $Needed (qw(Name)) {
402        if ( !$Param{$Needed} ) {
403            $Kernel::OM->Get('Kernel::System::Log')->Log(
404                Priority => 'error',
405                Message  => "Need $Needed!",
406            );
407            return;
408        }
409    }
410
411    my $IDSuffix = $Param{IDSuffix} || '';
412    my $Class    = $Param{Class}    || '';
413    $Class .= ' DateTime Entry';
414
415    my $Name = $Param{Name} . $IDSuffix;
416
417    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
418    my $TimeZone     = $Kernel::OM->Get('Kernel::Config')->Get("OTRSTimeZone");
419
420    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
421        UserID => $Param{UserID},
422    );
423
424    my $DefaultValue;
425    if ( $Param{DefaultItem} ) {
426
427        my $DateTimeObject = $Kernel::OM->Create(
428            'Kernel::System::DateTime',
429            ObjectParams => {
430                String   => $Param{DefaultItem}->{Content},
431                TimeZone => $TimeZone,
432            },
433        );
434
435        if ( $Preferences{UserTimeZone} ) {
436            my $Success = $DateTimeObject->ToTimeZone(
437                TimeZone => $Preferences{UserTimeZone},
438            );
439
440            if ( !$Success ) {
441                $Kernel::OM->Get('Kernel::System::Log')->Log(
442                    Priority => 'error',
443                    Message  => "System was not able to calculate DateTime in user timezone!"
444                );
445            }
446        }
447
448        $DefaultValue = $DateTimeObject->ToString();
449    }
450
451    if ( $Preferences{UserTimeZone} ) {
452        $TimeZone = $Preferences{UserTimeZone};
453    }
454
455    my $Result;
456
457    if ($DefaultValue) {
458        $DefaultValue =~ m{(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})};
459
460        $Result = $LayoutObject->BuildDateSelection(
461            Prefix           => $Name,
462            $Name . "Year"   => $1,
463            $Name . "Month"  => $2,
464            $Name . "Day"    => $3,
465            $Name . "Hour"   => $4,
466            $Name . "Minute" => $5,
467            $Name . "Second" => $6,
468            $Name . "Class"  => $Class,
469            YearDiff         => 10,
470            Format           => 'DateInputFormatLong',
471            Validate         => 1,
472            OverrideTimeZone => 1,
473        );
474    }
475    else {
476        $Result = $LayoutObject->BuildDateSelection(
477            Prefix           => $Name,
478            $Name . "Class"  => $Class,
479            YearDiff         => 10,
480            Format           => 'DateInputFormatLong',
481            Validate         => 1,
482            OverrideTimeZone => 1,
483        );
484    }
485
486    my $TimeZoneText = $Kernel::OM->Get('Kernel::Language')->Translate("Time Zone");
487    $Result .= "<span class='TimeZoneText'>$TimeZoneText: $TimeZone</span>\n";
488
489    for my $JS ( @{ $LayoutObject->{_JSOnDocumentComplete} } ) {
490        $Result .= "<script>$JS</script>";
491    }
492
493    return $Result;
494}
495
4961;
497
498=head1 TERMS AND CONDITIONS
499
500This software is part of the OTRS project (L<https://otrs.org/>).
501
502This software comes with ABSOLUTELY NO WARRANTY. For details, see
503the enclosed file COPYING for license information (GPL). If you
504did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
505
506=cut
507