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