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::Multiselect;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15
16use parent qw(Kernel::System::DynamicField::Driver::BaseSelect);
17
18our @ObjectDependencies = (
19    'Kernel::Config',
20    'Kernel::System::DynamicFieldValue',
21    'Kernel::System::Log',
22    'Kernel::System::Main',
23);
24
25=head1 NAME
26
27Kernel::System::DynamicField::Driver::Multiselect
28
29=head1 DESCRIPTION
30
31DynamicFields Multiselect Driver delegate
32
33=head1 PUBLIC INTERFACE
34
35This module implements the public interface of L<Kernel::System::DynamicField::Backend>.
36Please look there for a detailed reference of the functions.
37
38=head2 new()
39
40usually, you want to create an instance of this
41by using Kernel::System::DynamicField::Backend->new();
42
43=cut
44
45sub new {
46    my ( $Type, %Param ) = @_;
47
48    # allocate new hash for object
49    my $Self = {};
50    bless( $Self, $Type );
51
52    # set field behaviors
53    $Self->{Behaviors} = {
54        'IsACLReducible'               => 1,
55        'IsNotificationEventCondition' => 1,
56        'IsSortable'                   => 0,
57        'IsFiltrable'                  => 0,
58        'IsStatsCondition'             => 1,
59        'IsCustomerInterfaceCapable'   => 1,
60        'IsLikeOperatorCapable'        => 1,
61    };
62
63    # get the Dynamic Field Backend custom extensions
64    my $DynamicFieldDriverExtensions
65        = $Kernel::OM->Get('Kernel::Config')->Get('DynamicFields::Extension::Driver::Multiselect');
66
67    EXTENSION:
68    for my $ExtensionKey ( sort keys %{$DynamicFieldDriverExtensions} ) {
69
70        # skip invalid extensions
71        next EXTENSION if !IsHashRefWithData( $DynamicFieldDriverExtensions->{$ExtensionKey} );
72
73        # create a extension config shortcut
74        my $Extension = $DynamicFieldDriverExtensions->{$ExtensionKey};
75
76        # check if extension has a new module
77        if ( $Extension->{Module} ) {
78
79            # check if module can be loaded
80            if (
81                !$Kernel::OM->Get('Kernel::System::Main')->RequireBaseClass( $Extension->{Module} )
82                )
83            {
84                die "Can't load dynamic fields backend module"
85                    . " $Extension->{Module}! $@";
86            }
87        }
88
89        # check if extension contains more behaviors
90        if ( IsHashRefWithData( $Extension->{Behaviors} ) ) {
91
92            %{ $Self->{Behaviors} } = (
93                %{ $Self->{Behaviors} },
94                %{ $Extension->{Behaviors} }
95            );
96        }
97    }
98
99    return $Self;
100}
101
102sub ValueGet {
103    my ( $Self, %Param ) = @_;
104
105    my $DFValue = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->ValueGet(
106        FieldID  => $Param{DynamicFieldConfig}->{ID},
107        ObjectID => $Param{ObjectID},
108    );
109
110    return if !$DFValue;
111    return if !IsArrayRefWithData($DFValue);
112    return if !IsHashRefWithData( $DFValue->[0] );
113
114    # extract real values
115    my @ReturnData;
116    for my $Item ( @{$DFValue} ) {
117        push @ReturnData, $Item->{ValueText};
118    }
119
120    return \@ReturnData;
121}
122
123sub ValueSet {
124    my ( $Self, %Param ) = @_;
125
126    # check for valid possible values list
127    if ( !$Param{DynamicFieldConfig}->{Config}->{PossibleValues} ) {
128        $Kernel::OM->Get('Kernel::System::Log')->Log(
129            Priority => 'error',
130            Message  => "Need PossibleValues in DynamicFieldConfig!",
131        );
132        return;
133    }
134
135    # check value
136    my @Values;
137    if ( ref $Param{Value} eq 'ARRAY' ) {
138        @Values = @{ $Param{Value} };
139    }
140    else {
141        @Values = ( $Param{Value} );
142    }
143
144    # get dynamic field value object
145    my $DynamicFieldValueObject = $Kernel::OM->Get('Kernel::System::DynamicFieldValue');
146
147    my $Success;
148    if ( IsArrayRefWithData( \@Values ) ) {
149
150        # if there is at least one value to set, this means one or more values are selected,
151        #    set those values!
152        my @ValueText;
153        for my $Item (@Values) {
154            push @ValueText, { ValueText => $Item };
155        }
156
157        $Success = $DynamicFieldValueObject->ValueSet(
158            FieldID  => $Param{DynamicFieldConfig}->{ID},
159            ObjectID => $Param{ObjectID},
160            Value    => \@ValueText,
161            UserID   => $Param{UserID},
162        );
163    }
164    else {
165
166        # otherwise no value was selected, then in fact this means that any value there should be
167        # deleted
168        $Success = $DynamicFieldValueObject->ValueDelete(
169            FieldID  => $Param{DynamicFieldConfig}->{ID},
170            ObjectID => $Param{ObjectID},
171            UserID   => $Param{UserID},
172        );
173    }
174
175    return $Success;
176}
177
178sub ValueIsDifferent {
179    my ( $Self, %Param ) = @_;
180
181    # special cases where the values are different but they should be reported as equals
182    if (
183        !defined $Param{Value1}
184        && ref $Param{Value2} eq 'ARRAY'
185        && !IsArrayRefWithData( $Param{Value2} )
186        )
187    {
188        return;
189    }
190    if (
191        !defined $Param{Value2}
192        && ref $Param{Value1} eq 'ARRAY'
193        && !IsArrayRefWithData( $Param{Value1} )
194        )
195    {
196        return;
197    }
198
199    # compare the results
200    return DataIsDifferent(
201        Data1 => \$Param{Value1},
202        Data2 => \$Param{Value2}
203    );
204}
205
206sub ValueValidate {
207    my ( $Self, %Param ) = @_;
208
209    # check value
210    my @Values;
211    if ( IsArrayRefWithData( $Param{Value} ) ) {
212        @Values = @{ $Param{Value} };
213    }
214    else {
215        @Values = ( $Param{Value} );
216    }
217
218    # get dynamic field value object
219    my $DynamicFieldValueObject = $Kernel::OM->Get('Kernel::System::DynamicFieldValue');
220
221    my $Success;
222    for my $Item (@Values) {
223
224        $Success = $DynamicFieldValueObject->ValueValidate(
225            Value => {
226                ValueText => $Item,
227            },
228            UserID => $Param{UserID}
229        );
230
231        return if !$Success;
232    }
233
234    return $Success;
235}
236
237sub FieldValueValidate {
238    my ( $Self, %Param ) = @_;
239
240    # Check for valid possible values list.
241    if ( !IsHashRefWithData( $Param{DynamicFieldConfig}->{Config}->{PossibleValues} ) ) {
242        $Kernel::OM->Get('Kernel::System::Log')->Log(
243            Priority => 'error',
244            Message  => "Need PossibleValues in Multiselect DynamicFieldConfig!",
245        );
246        return;
247    }
248
249    # Check for defined value.
250    if ( !defined $Param{Value} ) {
251        $Kernel::OM->Get('Kernel::System::Log')->Log(
252            Priority => 'error',
253            Message  => "Need Value in Multiselect DynamicField!",
254        );
255        return;
256    }
257
258    # Check if value parameter exists in possible values config.
259    if ( length $Param{Value} ) {
260        my @Values;
261        if ( ref $Param{Value} eq 'ARRAY' ) {
262            @Values = @{ $Param{Value} };
263        }
264        else {
265            push @Values, $Param{Value};
266        }
267
268        for my $Value (@Values) {
269            return if !defined $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Value};
270        }
271    }
272
273    return 1;
274}
275
276sub EditFieldRender {
277    my ( $Self, %Param ) = @_;
278
279    # take config from field config
280    my $FieldConfig = $Param{DynamicFieldConfig}->{Config};
281    my $FieldName   = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
282    my $FieldLabel  = $Param{DynamicFieldConfig}->{Label};
283
284    my $Value;
285
286    # set the field value or default
287    if ( $Param{UseDefaultValue} ) {
288        $Value = ( defined $FieldConfig->{DefaultValue} ? $FieldConfig->{DefaultValue} : '' );
289    }
290    $Value = $Param{Value} // $Value;
291
292    # check if a value in a template (GenericAgent etc.)
293    # is configured for this dynamic field
294    if (
295        IsHashRefWithData( $Param{Template} )
296        && defined $Param{Template}->{$FieldName}
297        )
298    {
299        $Value = $Param{Template}->{$FieldName};
300    }
301
302    # extract the dynamic field value from the web request
303    my $FieldValue = $Self->EditFieldValueGet(
304        %Param,
305    );
306
307    # set values from ParamObject if present
308    if ( IsArrayRefWithData($FieldValue) ) {
309        $Value = $FieldValue;
310    }
311
312    # check and set class if necessary
313    my $FieldClass = 'DynamicFieldText Modernize';
314    if ( defined $Param{Class} && $Param{Class} ne '' ) {
315        $FieldClass .= ' ' . $Param{Class};
316    }
317
318    # set field as mandatory
319    if ( $Param{Mandatory} ) {
320        $FieldClass .= ' Validate_Required';
321    }
322
323    # set error css class
324    if ( $Param{ServerError} ) {
325        $FieldClass .= ' ServerError';
326    }
327
328    # set TreeView class
329    if ( $FieldConfig->{TreeView} ) {
330        $FieldClass .= ' DynamicFieldWithTreeView';
331    }
332
333    # set PossibleValues, use PossibleValuesFilter if defined
334    my $PossibleValues = $Param{PossibleValuesFilter} // $Self->PossibleValuesGet(%Param);
335
336    # check value
337    my $SelectedValuesArrayRef;
338    if ( defined $Value ) {
339        if ( ref $Value eq 'ARRAY' ) {
340            $SelectedValuesArrayRef = $Value;
341        }
342        else {
343            $SelectedValuesArrayRef = [$Value];
344        }
345    }
346
347    my $DataValues = $Self->BuildSelectionDataGet(
348        DynamicFieldConfig => $Param{DynamicFieldConfig},
349        PossibleValues     => $PossibleValues,
350        Value              => $Value,
351    );
352
353    my $HTMLString = $Param{LayoutObject}->BuildSelection(
354        Data        => $DataValues || {},
355        Name        => $FieldName,
356        SelectedID  => $SelectedValuesArrayRef,
357        Translation => $FieldConfig->{TranslatableValues} || 0,
358        Class       => $FieldClass,
359        HTMLQuote   => 1,
360        Multiple    => 1,
361    );
362
363    if ( $FieldConfig->{TreeView} ) {
364        my $TreeSelectionMessage = $Param{LayoutObject}->{LanguageObject}->Translate("Show Tree Selection");
365        $HTMLString
366            .= ' <a href="#" title="'
367            . $TreeSelectionMessage
368            . '" class="ShowTreeSelection"><span>'
369            . $TreeSelectionMessage . '</span><i class="fa fa-sitemap"></i></a>';
370    }
371
372    if ( $Param{Mandatory} ) {
373        my $DivID = $FieldName . 'Error';
374
375        my $FieldRequiredMessage = $Param{LayoutObject}->{LanguageObject}->Translate("This field is required.");
376
377        # for client side validation
378        $HTMLString .= <<"EOF";
379
380<div id="$DivID" class="TooltipErrorMessage">
381    <p>
382        $FieldRequiredMessage
383    </p>
384</div>
385EOF
386    }
387
388    if ( $Param{ServerError} ) {
389
390        my $ErrorMessage = $Param{ErrorMessage} || 'This field is required.';
391        $ErrorMessage = $Param{LayoutObject}->{LanguageObject}->Translate($ErrorMessage);
392        my $DivID = $FieldName . 'ServerError';
393
394        # for server side validation
395        $HTMLString .= <<"EOF";
396
397<div id="$DivID" class="TooltipErrorMessage">
398    <p>
399        $ErrorMessage
400    </p>
401</div>
402EOF
403    }
404
405    if ( $Param{AJAXUpdate} ) {
406
407        my $FieldSelector = '#' . $FieldName;
408
409        my $FieldsToUpdate;
410        if ( IsArrayRefWithData( $Param{UpdatableFields} ) ) {
411
412            # Remove current field from updatable fields list
413            my @FieldsToUpdate = grep { $_ ne $FieldName } @{ $Param{UpdatableFields} };
414
415            # quote all fields, put commas in between them
416            $FieldsToUpdate = join( ', ', map {"'$_'"} @FieldsToUpdate );
417        }
418
419        # add js to call FormUpdate()
420        $Param{LayoutObject}->AddJSOnDocumentComplete( Code => <<"EOF");
421\$('$FieldSelector').bind('change', function (Event) {
422    Core.AJAX.FormUpdate(\$(this).parents('form'), 'AJAXUpdate', '$FieldName', [ $FieldsToUpdate ]);
423});
424Core.App.Subscribe('Event.AJAX.FormUpdate.Callback', function(Data) {
425    var FieldName = '$FieldName';
426    if (Data[FieldName] && \$('#' + FieldName).hasClass('DynamicFieldWithTreeView')) {
427        Core.UI.TreeSelection.RestoreDynamicFieldTreeView(\$('#' + FieldName), Data[FieldName], '' , 1);
428    }
429});
430EOF
431    }
432
433    # call EditLabelRender on the common Driver
434    my $LabelString = $Self->EditLabelRender(
435        %Param,
436        Mandatory => $Param{Mandatory} || '0',
437        FieldName => $FieldName,
438    );
439
440    my $Data = {
441        Field => $HTMLString,
442        Label => $LabelString,
443    };
444
445    return $Data;
446}
447
448sub EditFieldValueGet {
449    my ( $Self, %Param ) = @_;
450
451    my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
452
453    my $Value;
454
455    # check if there is a Template and retrieve the dynamic field value from there
456    if ( IsHashRefWithData( $Param{Template} ) && defined $Param{Template}->{$FieldName} ) {
457        $Value = $Param{Template}->{$FieldName};
458    }
459
460    # otherwise get dynamic field value from the web request
461    elsif (
462        defined $Param{ParamObject}
463        && ref $Param{ParamObject} eq 'Kernel::System::Web::Request'
464        )
465    {
466        my @Data = $Param{ParamObject}->GetArray( Param => $FieldName );
467
468        # delete empty values (can happen if the user has selected the "-" entry)
469        my $Index = 0;
470        ITEM:
471        for my $Item ( sort @Data ) {
472
473            if ( !$Item ) {
474                splice( @Data, $Index, 1 );
475                next ITEM;
476            }
477            $Index++;
478        }
479
480        $Value = \@Data;
481    }
482
483    if ( defined $Param{ReturnTemplateStructure} && $Param{ReturnTemplateStructure} eq 1 ) {
484        return {
485            $FieldName => $Value,
486        };
487    }
488
489    # for this field the normal return an the ReturnValueStructure are the same
490    return $Value;
491}
492
493sub EditFieldValueValidate {
494    my ( $Self, %Param ) = @_;
495
496    # get the field value from the http request
497    my $Values = $Self->EditFieldValueGet(
498        DynamicFieldConfig => $Param{DynamicFieldConfig},
499        ParamObject        => $Param{ParamObject},
500
501        # not necessary for this Driver but place it for consistency reasons
502        ReturnValueStructure => 1,
503    );
504
505    my $ServerError;
506    my $ErrorMessage;
507
508    # perform necessary validations
509    if ( $Param{Mandatory} && !IsArrayRefWithData($Values) ) {
510        return {
511            ServerError => 1,
512        };
513    }
514    else {
515
516        # get possible values list
517        my $PossibleValues = $Param{DynamicFieldConfig}->{Config}->{PossibleValues};
518
519        # overwrite possible values if PossibleValuesFilter
520        if ( defined $Param{PossibleValuesFilter} ) {
521            $PossibleValues = $Param{PossibleValuesFilter};
522        }
523
524        # validate if value is in possible values list (but let pass empty values)
525        for my $Item ( @{$Values} ) {
526            if ( !$PossibleValues->{$Item} ) {
527                $ServerError  = 1;
528                $ErrorMessage = 'The field content is invalid';
529            }
530        }
531    }
532
533    # create resulting structure
534    my $Result = {
535        ServerError  => $ServerError,
536        ErrorMessage => $ErrorMessage,
537    };
538
539    return $Result;
540}
541
542sub DisplayValueRender {
543    my ( $Self, %Param ) = @_;
544
545    # set HTMLOutput as default if not specified
546    if ( !defined $Param{HTMLOutput} ) {
547        $Param{HTMLOutput} = 1;
548    }
549
550    # set Value and Title variables
551    my $Value         = '';
552    my $Title         = '';
553    my $ValueMaxChars = $Param{ValueMaxChars} || '';
554    my $TitleMaxChars = $Param{TitleMaxChars} || '';
555
556    # check value
557    my @Values;
558    if ( ref $Param{Value} eq 'ARRAY' ) {
559        @Values = @{ $Param{Value} };
560    }
561    else {
562        @Values = ( $Param{Value} );
563    }
564
565    # get real values
566    my $PossibleValues     = $Param{DynamicFieldConfig}->{Config}->{PossibleValues};
567    my $TranslatableValues = $Param{DynamicFieldConfig}->{Config}->{TranslatableValues};
568
569    my @ReadableValues;
570    my @ReadableTitles;
571
572    my $ShowValueEllipsis;
573    my $ShowTitleEllipsis;
574
575    VALUEITEM:
576    for my $Item (@Values) {
577        next VALUEITEM if !$Item;
578
579        my $ReadableValue = $Item;
580
581        if ( $PossibleValues->{$Item} ) {
582            $ReadableValue = $PossibleValues->{$Item};
583            if ($TranslatableValues) {
584                $ReadableValue = $Param{LayoutObject}->{LanguageObject}->Translate($ReadableValue);
585            }
586        }
587
588        my $ReadableLength = length $ReadableValue;
589
590        # set title equal value
591        my $ReadableTitle = $ReadableValue;
592
593        # cut strings if needed
594        if ( $ValueMaxChars ne '' ) {
595
596            if ( length $ReadableValue > $ValueMaxChars ) {
597                $ShowValueEllipsis = 1;
598            }
599            $ReadableValue = substr $ReadableValue, 0, $ValueMaxChars;
600
601            # decrease the max parameter
602            $ValueMaxChars = $ValueMaxChars - $ReadableLength;
603            if ( $ValueMaxChars < 0 ) {
604                $ValueMaxChars = 0;
605            }
606        }
607
608        if ( $TitleMaxChars ne '' ) {
609
610            if ( length $ReadableTitle > $ValueMaxChars ) {
611                $ShowTitleEllipsis = 1;
612            }
613            $ReadableTitle = substr $ReadableTitle, 0, $TitleMaxChars;
614
615            # decrease the max parameter
616            $TitleMaxChars = $TitleMaxChars - $ReadableLength;
617            if ( $TitleMaxChars < 0 ) {
618                $TitleMaxChars = 0;
619            }
620        }
621
622        # HTMLOutput transformations
623        if ( $Param{HTMLOutput} ) {
624
625            $ReadableValue = $Param{LayoutObject}->Ascii2Html(
626                Text => $ReadableValue,
627            );
628
629            $ReadableTitle = $Param{LayoutObject}->Ascii2Html(
630                Text => $ReadableTitle,
631            );
632        }
633
634        if ( length $ReadableValue ) {
635            push @ReadableValues, $ReadableValue;
636        }
637        if ( length $ReadableTitle ) {
638            push @ReadableTitles, $ReadableTitle;
639        }
640    }
641
642    # get specific field settings
643    my $FieldConfig = $Kernel::OM->Get('Kernel::Config')->Get('DynamicFields::Driver')->{Multiselect} || {};
644
645    # set new line separator
646    my $ItemSeparator = $FieldConfig->{ItemSeparator} || ', ';
647
648    $Value = join( $ItemSeparator, @ReadableValues );
649    $Title = join( $ItemSeparator, @ReadableTitles );
650
651    if ($ShowValueEllipsis) {
652        $Value .= '...';
653    }
654    if ($ShowTitleEllipsis) {
655        $Title .= '...';
656    }
657
658    # this field type does not support the Link Feature
659    my $Link;
660
661    # create return structure
662    my $Data = {
663        Value => $Value,
664        Title => $Title,
665        Link  => $Link,
666    };
667
668    return $Data;
669}
670
671sub SearchFieldParameterBuild {
672    my ( $Self, %Param ) = @_;
673
674    # get field value
675    my $Value = $Self->SearchFieldValueGet(%Param);
676
677    my $DisplayValue;
678
679    if ( defined $Value && !$Value ) {
680        $DisplayValue = '';
681    }
682
683    if ($Value) {
684        if ( ref $Value eq 'ARRAY' ) {
685
686            my @DisplayItemList;
687            for my $Item ( @{$Value} ) {
688
689                # set the display value
690                my $DisplayItem = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Item}
691                    || $Item;
692
693                # translate the value
694                if (
695                    $Param{DynamicFieldConfig}->{Config}->{TranslatableValues}
696                    && defined $Param{LayoutObject}
697                    )
698                {
699                    $DisplayItem = $Param{LayoutObject}->{LanguageObject}->Translate($DisplayItem);
700                }
701
702                push @DisplayItemList, $DisplayItem;
703            }
704
705            # combine different values into one string
706            $DisplayValue = join ' + ', @DisplayItemList;
707        }
708        else {
709
710            # set the display value
711            $DisplayValue = $Param{DynamicFieldConfig}->{Config}->{PossibleValues}->{$Value};
712
713            # translate the value
714            if (
715                $Param{DynamicFieldConfig}->{Config}->{TranslatableValues}
716                && defined $Param{LayoutObject}
717                )
718            {
719                $DisplayValue = $Param{LayoutObject}->{LanguageObject}->Translate($DisplayValue);
720            }
721        }
722    }
723
724    # return search parameter structure
725    return {
726        Parameter => {
727            Equals => $Value,
728        },
729        Display => $DisplayValue,
730    };
731}
732
733sub StatsFieldParameterBuild {
734    my ( $Self, %Param ) = @_;
735
736    # set PossibleValues
737    my $Values = $Param{DynamicFieldConfig}->{Config}->{PossibleValues};
738
739    # get historical values from database
740    my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet(
741        FieldID   => $Param{DynamicFieldConfig}->{ID},
742        ValueType => 'Text,',
743    );
744
745    # add historic values to current values (if they don't exist anymore)
746    for my $Key ( sort keys %{$HistoricalValues} ) {
747        if ( !$Values->{$Key} ) {
748            $Values->{$Key} = $HistoricalValues->{$Key};
749        }
750    }
751
752    # use PossibleValuesFilter if defined
753    $Values = $Param{PossibleValuesFilter} // $Values;
754
755    return {
756        Values             => $Values,
757        Name               => $Param{DynamicFieldConfig}->{Label},
758        Element            => 'DynamicField_' . $Param{DynamicFieldConfig}->{Name},
759        TranslatableValues => $Param{DynamicFieldConfig}->{Config}->{TranslatableValues},
760        Block              => 'MultiSelectField',
761    };
762}
763
764sub ReadableValueRender {
765    my ( $Self, %Param ) = @_;
766
767    # set Value and Title variables
768    my $Value = '';
769    my $Title = '';
770
771    # check value
772    my @Values;
773    if ( ref $Param{Value} eq 'ARRAY' ) {
774        @Values = @{ $Param{Value} };
775    }
776    else {
777        @Values = ( $Param{Value} );
778    }
779
780    my @ReadableValues;
781
782    VALUEITEM:
783    for my $Item (@Values) {
784        next VALUEITEM if !$Item;
785
786        push @ReadableValues, $Item;
787    }
788
789    # set new line separator
790    my $ItemSeparator = ', ';
791
792    # Output transformations
793    $Value = join( $ItemSeparator, @ReadableValues );
794    $Title = $Value;
795
796    # cut strings if needed
797    if ( $Param{ValueMaxChars} && length($Value) > $Param{ValueMaxChars} ) {
798        $Value = substr( $Value, 0, $Param{ValueMaxChars} ) . '...';
799    }
800    if ( $Param{TitleMaxChars} && length($Title) > $Param{TitleMaxChars} ) {
801        $Title = substr( $Title, 0, $Param{TitleMaxChars} ) . '...';
802    }
803
804    # create return structure
805    my $Data = {
806        Value => $Value,
807        Title => $Title,
808    };
809
810    return $Data;
811}
812
813sub TemplateValueTypeGet {
814    my ( $Self, %Param ) = @_;
815
816    my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
817
818    # set the field types
819    my $EditValueType   = 'ARRAY';
820    my $SearchValueType = 'ARRAY';
821
822    # return the correct structure
823    if ( $Param{FieldType} eq 'Edit' ) {
824        return {
825            $FieldName => $EditValueType,
826        };
827    }
828    elsif ( $Param{FieldType} eq 'Search' ) {
829        return {
830            'Search_' . $FieldName => $SearchValueType,
831        };
832    }
833    else {
834        return {
835            $FieldName             => $EditValueType,
836            'Search_' . $FieldName => $SearchValueType,
837        };
838    }
839}
840
841sub ObjectMatch {
842    my ( $Self, %Param ) = @_;
843
844    my $FieldName = 'DynamicField_' . $Param{DynamicFieldConfig}->{Name};
845
846    # the attribute must be an array
847    return 0 if !IsArrayRefWithData( $Param{ObjectAttributes}->{$FieldName} );
848
849    my $Match;
850
851    # search in all values for this attribute
852    VALUE:
853    for my $AttributeValue ( @{ $Param{ObjectAttributes}->{$FieldName} } ) {
854
855        next VALUE if !defined $AttributeValue;
856
857        # only need to match one
858        if ( $Param{Value} eq $AttributeValue ) {
859            $Match = 1;
860            last VALUE;
861        }
862    }
863
864    return $Match;
865}
866
867sub HistoricalValuesGet {
868    my ( $Self, %Param ) = @_;
869
870    # get historical values from database
871    my $HistoricalValues = $Kernel::OM->Get('Kernel::System::DynamicFieldValue')->HistoricalValueGet(
872        FieldID   => $Param{DynamicFieldConfig}->{ID},
873        ValueType => 'Text',
874    );
875
876    # return the historical values from database
877    return $HistoricalValues;
878}
879
880sub ValueLookup {
881    my ( $Self, %Param ) = @_;
882
883    my @Keys;
884    if ( ref $Param{Key} eq 'ARRAY' ) {
885        @Keys = @{ $Param{Key} };
886    }
887    else {
888        @Keys = ( $Param{Key} );
889    }
890
891    # get real values
892    my $PossibleValues = $Param{DynamicFieldConfig}->{Config}->{PossibleValues};
893
894    # to store final values
895    my @Values;
896
897    KEYITEM:
898    for my $Item (@Keys) {
899        next KEYITEM if !$Item;
900
901        # set the value as the key by default
902        my $Value = $Item;
903
904        # try to convert key to real value
905        if ( $PossibleValues->{$Item} ) {
906            $Value = $PossibleValues->{$Item};
907
908            # check if translation is possible
909            if (
910                defined $Param{LanguageObject}
911                && $Param{DynamicFieldConfig}->{Config}->{TranslatableValues}
912                )
913            {
914
915                # translate value
916                $Value = $Param{LanguageObject}->Translate($Value);
917            }
918        }
919        push @Values, $Value;
920    }
921
922    return \@Values;
923}
924
925sub BuildSelectionDataGet {
926    my ( $Self, %Param ) = @_;
927
928    my $FieldConfig            = $Param{DynamicFieldConfig}->{Config};
929    my $FilteredPossibleValues = $Param{PossibleValues};
930
931    # get the possible values again as it might or might not contain the possible none and it could
932    # also be overwritten
933    my $ConfigPossibleValues = $Self->PossibleValuesGet(%Param);
934
935    # check if $PossibleValues differs from configured PossibleValues
936    # and show values which are not contained as disabled if TreeView => 1
937    if ( $FieldConfig->{TreeView} ) {
938
939        if ( keys %{$ConfigPossibleValues} != keys %{$FilteredPossibleValues} ) {
940
941            # define variables to use later in the for loop
942            my @Values;
943            my $Parents;
944            my %DisabledElements;
945            my %ProcessedElements;
946            my $PosibleNoneSet;
947
948            my %Values;
949            if ( defined $Param{Value} && IsArrayRefWithData( $Param{Value} ) ) {
950
951                # create a lookup table
952                %Values = map { $_ => 1 } @{ $Param{Value} };
953            }
954
955            # loop on all filtered possible values
956            for my $Key ( sort keys %{$FilteredPossibleValues} ) {
957
958                # special case for possible none
959                if ( !$Key && !$PosibleNoneSet && $FieldConfig->{PossibleNone} ) {
960
961                    my $Selected;
962                    if (
963                        !IsHashRefWithData( \%Values )
964                        || ( defined $Values{''} && $Values{''} )
965                        )
966                    {
967                        $Selected = 1;
968                    }
969
970                    # add possible none
971                    push @Values, {
972                        Key      => $Key,
973                        Value    => $ConfigPossibleValues->{$Key} || '-',
974                        Selected => $Selected,
975                    };
976                }
977
978                # try to split its parents GrandParent::Parent::Son
979                my @Elements = split /::/, $Key;
980
981                # reset parents
982                $Parents = '';
983
984                # get each element in the hierarchy
985                ELEMENT:
986                for my $Element (@Elements) {
987
988                    # add its own parents for the complete name
989                    my $ElementLongName = $Parents . $Element;
990
991                    # set new parent (before skip already processed)
992                    $Parents .= $Element . '::';
993
994                    # skip if already processed
995                    next ELEMENT if $ProcessedElements{$ElementLongName};
996
997                    my $Disabled;
998
999                    # check if element exists in the original data or if it is already marked
1000                    if (
1001                        !defined $FilteredPossibleValues->{$ElementLongName}
1002                        && !$DisabledElements{$ElementLongName}
1003                        )
1004                    {
1005
1006                        # mark element as disabled
1007                        $DisabledElements{$ElementLongName} = 1;
1008
1009                        # also set the disabled flag for current element to add
1010                        $Disabled = 1;
1011                    }
1012
1013                    # set element as already processed
1014                    $ProcessedElements{$ElementLongName} = 1;
1015
1016                    # check if the current element is the selected one
1017                    my $Selected;
1018                    if ( IsHashRefWithData( \%Values ) && $Values{$ElementLongName} ) {
1019                        $Selected = 1;
1020                    }
1021
1022                    # add element to the new list of possible values (now including missing parents)
1023                    push @Values, {
1024                        Key      => $ElementLongName,
1025                        Value    => $ConfigPossibleValues->{$ElementLongName} || $ElementLongName,
1026                        Disabled => $Disabled,
1027                        Selected => $Selected,
1028                    };
1029                }
1030            }
1031            $FilteredPossibleValues = \@Values;
1032        }
1033    }
1034
1035    return $FilteredPossibleValues;
1036}
1037
10381;
1039
1040=head1 TERMS AND CONDITIONS
1041
1042This software is part of the OTRS project (L<https://otrs.org/>).
1043
1044This software comes with ABSOLUTELY NO WARRANTY. For details, see
1045the enclosed file COPYING for license information (GPL). If you
1046did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1047
1048=cut
1049