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::Output::HTML::Layout::LinkObject;
10
11use strict;
12use warnings;
13
14use Kernel::System::LinkObject;
15use Kernel::Language qw(Translatable);
16use Kernel::System::VariableCheck qw(:all);
17
18our $ObjectManagerDisabled = 1;
19
20=head1 NAME
21
22Kernel::Output::HTML::Layout::LinkObject - all LinkObject-related HTML functions
23
24=head1 DESCRIPTION
25
26All LinkObject-related HTML functions
27
28=head1 PUBLIC INTERFACE
29
30=head2 LinkObjectTableCreate()
31
32create a output table
33
34    my $String = $LayoutObject->LinkObjectTableCreate(
35        LinkListWithData => $LinkListWithDataRef,
36        ViewMode         => 'Simple', # (Simple|SimpleRaw|Complex|ComplexAdd|ComplexDelete|ComplexRaw)
37    );
38
39=cut
40
41sub LinkObjectTableCreate {
42    my ( $Self, %Param ) = @_;
43
44    # check needed stuff
45    for my $Argument (qw(LinkListWithData ViewMode)) {
46        if ( !$Param{$Argument} ) {
47            $Kernel::OM->Get('Kernel::System::Log')->Log(
48                Priority => 'error',
49                Message  => "Need $Argument!",
50            );
51            return;
52        }
53    }
54
55    if ( $Param{ViewMode} =~ m{ \A Simple }xms ) {
56
57        return $Self->LinkObjectTableCreateSimple(
58            LinkListWithData               => $Param{LinkListWithData},
59            ViewMode                       => $Param{ViewMode},
60            AdditionalLinkListWithDataJSON => $Param{AdditionalLinkListWithDataJSON},
61        );
62    }
63    else {
64
65        return $Self->LinkObjectTableCreateComplex(
66            LinkListWithData               => $Param{LinkListWithData},
67            ViewMode                       => $Param{ViewMode},
68            AJAX                           => $Param{AJAX},
69            SourceObject                   => $Param{Object},
70            ObjectID                       => $Param{Key},
71            AdditionalLinkListWithDataJSON => $Param{AdditionalLinkListWithDataJSON},
72        );
73    }
74}
75
76=head2 LinkObjectTableCreateComplex()
77
78create a complex output table
79
80    my $String = $LayoutObject->LinkObjectTableCreateComplex(
81        LinkListWithData => $LinkListRef,
82        ViewMode         => 'Complex', # (Complex|ComplexAdd|ComplexDelete|ComplexRaw)
83    );
84
85=cut
86
87sub LinkObjectTableCreateComplex {
88    my ( $Self, %Param ) = @_;
89
90    # get log object
91    my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
92
93    # create new instance of the layout object
94    my $LayoutObject  = Kernel::Output::HTML::Layout->new( %{$Self} );
95    my $LayoutObject2 = Kernel::Output::HTML::Layout->new( %{$Self} );
96
97    # check needed stuff
98    for my $Argument (qw(LinkListWithData ViewMode)) {
99        if ( !$Param{$Argument} ) {
100            $LogObject->Log(
101                Priority => 'error',
102                Message  => "Need $Argument!",
103            );
104            return;
105        }
106    }
107
108    # check link list
109    if ( ref $Param{LinkListWithData} ne 'HASH' ) {
110        $LogObject->Log(
111            Priority => 'error',
112            Message  => 'LinkListWithData must be a hash reference!',
113        );
114        return;
115    }
116
117    return if !%{ $Param{LinkListWithData} };
118
119    # convert the link list
120    my %LinkList;
121    for my $Object ( sort keys %{ $Param{LinkListWithData} } ) {
122
123        for my $LinkType ( sort keys %{ $Param{LinkListWithData}->{$Object} } ) {
124
125            # extract link type List
126            my $LinkTypeList = $Param{LinkListWithData}->{$Object}->{$LinkType};
127
128            for my $Direction ( sort keys %{$LinkTypeList} ) {
129
130                # extract direction list
131                my $DirectionList = $Param{LinkListWithData}->{$Object}->{$LinkType}->{$Direction};
132
133                for my $ObjectKey ( sort keys %{$DirectionList} ) {
134
135                    $LinkList{$Object}->{$ObjectKey}->{$LinkType} = $Direction;
136                }
137            }
138        }
139    }
140
141    my @OutputData;
142    OBJECT:
143    for my $Object ( sort { lc $a cmp lc $b } keys %{ $Param{LinkListWithData} } ) {
144
145        # load backend
146        my $BackendObject = $Self->_LoadLinkObjectLayoutBackend(
147            Object => $Object,
148        );
149
150        next OBJECT if !$BackendObject;
151
152        # get block data
153        my @BlockData = $BackendObject->TableCreateComplex(
154            ObjectLinkListWithData => $Param{LinkListWithData}->{$Object},
155            Action                 => $Self->{Action},
156            ObjectID               => $Param{ObjectID},
157        );
158
159        next OBJECT if !@BlockData;
160
161        push @OutputData, @BlockData;
162    }
163
164    # error handling
165    for my $Block (@OutputData) {
166
167        ITEM:
168        for my $Item ( @{ $Block->{ItemList} } ) {
169            if ( !grep { $_->{Key} } @{$Item} ) {
170                $Item->[0] = {
171                    Type => 'Text',
172                    Content =>
173                        'ERROR: Key attribute not found in any column of the item list.',
174                };
175            }
176
177            next ITEM if $Block->{Object};
178
179            if ( !$Block->{Object} ) {
180                $Item->[0] = {
181                    Type    => 'Text',
182                    Content => 'ERROR: Object attribute not found in the block data.',
183                };
184            }
185        }
186    }
187
188    # get config option to show the link delete button
189    my $ShowDeleteButton = $Kernel::OM->Get('Kernel::Config')->Get('LinkObject::ShowDeleteButton');
190
191    # add "linked as" column to the table
192    for my $Block (@OutputData) {
193
194        # define the headline column
195        my $Column = {
196            Content => $Kernel::OM->Get('Kernel::Language')->Translate('Linked as'),
197        };
198
199        # add new column to the headline
200        push @{ $Block->{Headline} }, $Column;
201
202        # permission check
203        my $SourcePermission;
204        if ( $Param{SourceObject} && $Param{ObjectID} && $ShowDeleteButton ) {
205
206            # get source permission
207            $SourcePermission = $Kernel::OM->Get('Kernel::System::LinkObject')->ObjectPermission(
208                Object => $Param{SourceObject},
209                Key    => $Param{ObjectID},
210                UserID => $Self->{UserID},
211            );
212
213            # we show the column headline if we got source permission
214            if ($SourcePermission) {
215                $Column = {
216                    Content  => $Kernel::OM->Get('Kernel::Language')->Translate('Delete'),
217                    CssClass => 'Center Last',
218                };
219
220                # add new column to the headline
221                push @{ $Block->{Headline} }, $Column;
222            }
223
224        }
225        for my $Item ( @{ $Block->{ItemList} } ) {
226
227            my %LinkDeleteData;
228            my $TargetPermission;
229
230            # Search for key.
231            my ($ItemWithKey) = grep { $_->{Key} } @{$Item};
232
233            if ( $Param{SourceObject} && $Param{ObjectID} && $ItemWithKey->{Key} && $ShowDeleteButton ) {
234
235                for my $LinkType ( sort keys %{ $LinkList{ $Block->{Object} }->{ $ItemWithKey->{Key} } } ) {
236
237                    # get target permission
238                    $TargetPermission = $Kernel::OM->Get('Kernel::System::LinkObject')->ObjectPermission(
239                        Object => $Block->{Object},
240                        Key    => $ItemWithKey->{Key},
241                        UserID => $Self->{UserID},
242                    );
243
244                    # build the delete link only if we also got target permission
245                    if ($TargetPermission) {
246
247                        my %InstantLinkDeleteData;
248
249                        # depending on the link type direction source and target must be switched
250                        if ( $LinkList{ $Block->{Object} }->{ $ItemWithKey->{Key} }->{$LinkType} eq 'Source' ) {
251                            $LinkDeleteData{SourceObject} = $Block->{Object};
252                            $LinkDeleteData{SourceKey}    = $ItemWithKey->{Key};
253                            $LinkDeleteData{TargetIdentifier}
254                                = $Param{SourceObject} . '::' . $Param{ObjectID} . '::' . $LinkType;
255                        }
256                        else {
257                            $LinkDeleteData{SourceObject} = $Param{SourceObject};
258                            $LinkDeleteData{SourceKey}    = $Param{ObjectID};
259                            $LinkDeleteData{TargetIdentifier}
260                                = $Block->{Object} . '::' . $ItemWithKey->{Key} . '::' . $LinkType;
261                        }
262                    }
263                }
264            }
265
266            # define check-box cell
267            my $CheckboxCell = {
268                Type         => 'LinkTypeList',
269                Content      => '',
270                LinkTypeList => $LinkList{ $Block->{Object} }->{ $ItemWithKey->{Key} },
271                Translate    => 1,
272            };
273
274            # add check-box cell to item
275            push @{$Item}, $CheckboxCell;
276
277            # check if delete icon should be shown
278            if ( $Param{SourceObject} && $Param{ObjectID} && $SourcePermission && $ShowDeleteButton ) {
279
280                if ($TargetPermission) {
281
282                    # build delete link
283                    push @{$Item}, {
284                        Type      => 'DeleteLinkIcon',
285                        CssClass  => 'Center Last',
286                        Translate => 1,
287                        %LinkDeleteData,
288                    };
289                }
290                else {
291                    # build no delete link, instead use empty values
292                    # to keep table formatting correct
293                    push @{$Item}, {
294                        Type     => 'Plain',
295                        CssClass => 'Center Last',
296                        Content  => '',
297                    };
298                }
299            }
300        }
301    }
302
303    return @OutputData if $Param{ViewMode} && $Param{ViewMode} eq 'ComplexRaw';
304
305    if ( $Param{ViewMode} eq 'ComplexAdd' ) {
306
307        for my $Block (@OutputData) {
308
309            # define the headline column
310            my $Column;
311
312            # add new column to the headline
313            unshift @{ $Block->{Headline} }, $Column;
314
315            for my $Item ( @{ $Block->{ItemList} } ) {
316
317                # search for key
318                my ($ItemWithKey) = grep { $_->{Key} } @{$Item};
319
320                # define check-box cell
321                my $CheckboxCell = {
322                    Type    => 'Checkbox',
323                    Name    => 'LinkTargetKeys',
324                    Content => $ItemWithKey->{Key},
325                };
326
327                # add check-box cell to item
328                unshift @{$Item}, $CheckboxCell;
329            }
330        }
331    }
332
333    if ( $Param{ViewMode} eq 'ComplexDelete' ) {
334
335        for my $Block (@OutputData) {
336
337            # define the headline column
338            my $Column = {
339                Content => ' ',
340            };
341
342            # add new column to the headline
343            unshift @{ $Block->{Headline} }, $Column;
344
345            for my $Item ( @{ $Block->{ItemList} } ) {
346
347                # search for key
348                my ($ItemWithKey) = grep { $_->{Key} } @{$Item};
349
350                # define check-box delete cell
351                my $CheckboxCell = {
352                    Type         => 'CheckboxDelete',
353                    Object       => $Block->{Object},
354                    Content      => '',
355                    Key          => $ItemWithKey->{Key},
356                    LinkTypeList => $LinkList{ $Block->{Object} }->{ $ItemWithKey->{Key} },
357                    Translate    => 1,
358                };
359
360                # add check-box cell to item
361                unshift @{$Item}, $CheckboxCell;
362            }
363        }
364    }
365
366    # # create new instance of the layout object
367    # my $LayoutObject  = Kernel::Output::HTML::Layout->new( %{$Self} );
368    # my $LayoutObject2 = Kernel::Output::HTML::Layout->new( %{$Self} );
369
370    # output the table complex block
371    $LayoutObject->Block(
372        Name => 'TableComplex',
373    );
374
375    # set block description
376    my $BlockDescription = $Param{ViewMode} eq 'ComplexAdd' ? Translatable('Search Result') : Translatable('Linked');
377
378    my $BlockCounter = 0;
379
380    my $Config             = $Kernel::OM->Get('Kernel::Config')->Get("LinkObject::ComplexTable") || {};
381    my $SettingsVisibility = $Kernel::OM->Get('Kernel::Config')->Get("LinkObject::ComplexTable::SettingsVisibility")
382        || {};
383
384    my @SettingsVisible = ();
385
386    if ( IsHashRefWithData($SettingsVisibility) ) {
387        for my $Key ( sort keys %{$SettingsVisibility} ) {
388
389            for my $Item ( @{ $SettingsVisibility->{$Key} } ) {
390
391                # check if it's not in array
392                if ( !grep { $Item eq $_ } @SettingsVisible ) {
393                    push @SettingsVisible, $Item;
394                }
395            }
396        }
397    }
398
399    # get OriginalAction
400    my $OriginalAction = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'OriginalAction' )
401        || $Self->{Action};
402
403    my @LinkObjectTables;
404    BLOCK:
405    for my $Block (@OutputData) {
406
407        next BLOCK if !$Block->{ItemList};
408        next BLOCK if ref $Block->{ItemList} ne 'ARRAY';
409        next BLOCK if !@{ $Block->{ItemList} };
410
411        # output the block
412        $LayoutObject->Block(
413            Name => 'TableComplexBlock',
414            Data => {
415                BlockDescription => $BlockDescription,
416                Blockname        => $Block->{Blockname} . ' (' . scalar @{ $Block->{ItemList} } . ')',
417                Name             => $Block->{Blockname},
418                NameForm         => $Block->{Blockname},
419                AJAX             => $Param{AJAX},
420            },
421        );
422
423        # check if registered in SysConfig
424        if (
425            # AgentLinkObject not allowed because it would result in nested forms
426            $OriginalAction ne 'AgentLinkObject'
427            && IsHashRefWithData($Config)
428            && $Config->{ $Block->{Blockname} }
429            && grep { $OriginalAction eq $_ } @SettingsVisible
430            )
431        {
432            my $SourceObjectData = '';
433            if ( $Block->{ObjectName} && $Block->{ObjectID} ) {
434                $SourceObjectData = "<input type='hidden' name='$Block->{ObjectName}' value='$Block->{ObjectID}' />";
435            }
436
437            $LayoutObject->Block(
438                Name => 'ContentLargePreferences',
439                Data => {
440                    Name => $Block->{Blockname},
441                },
442            );
443
444            my %Preferences = $Self->ComplexTablePreferencesGet(
445                Config  => $Config->{ $Block->{Blockname} },
446                PrefKey => "LinkObject::ComplexTable-" . $Block->{Blockname},
447            );
448
449            # Add translations for the allocation lists for regular columns.
450            for my $Column ( @{ $Block->{AllColumns} } ) {
451                $LayoutObject->AddJSData(
452                    Key   => 'Column' . $Column->{ColumnName},
453                    Value => $LayoutObject->{LanguageObject}->Translate( $Column->{ColumnTranslate} ),
454                );
455            }
456
457            # Prepare LinkObjectTables for JS config.
458            push @LinkObjectTables, $Block->{Blockname};
459
460            $LayoutObject->Block(
461                Name => 'ContentLargePreferencesForm',
462                Data => {
463                    Name     => $Block->{Blockname},
464                    NameForm => $Block->{Blockname},
465                },
466            );
467
468            $LayoutObject->Block(
469                Name => $Preferences{Name} . 'PreferencesItem' . $Preferences{Block},
470                Data => {
471                    %Preferences,
472                    NameForm                       => $Block->{Blockname},
473                    NamePref                       => $Preferences{Name},
474                    Name                           => $Block->{Blockname},
475                    SourceObject                   => $Param{SourceObject},
476                    DestinationObject              => $Block->{Blockname},
477                    OriginalAction                 => $OriginalAction,
478                    SourceObjectData               => $SourceObjectData,
479                    AdditionalLinkListWithDataJSON => $Param{AdditionalLinkListWithDataJSON},
480                },
481            );
482        }
483
484        # output table headline
485        for my $HeadlineColumn ( @{ $Block->{Headline} } ) {
486
487            # output a headline column block
488            $LayoutObject->Block(
489                Name => 'TableComplexBlockColumn',
490                Data => $HeadlineColumn,
491            );
492        }
493
494        # output item list
495        for my $Row ( @{ $Block->{ItemList} } ) {
496
497            # output a table row block
498            $LayoutObject->Block(
499                Name => 'TableComplexBlockRow',
500            );
501
502            for my $Column ( @{$Row} ) {
503
504                # create the content string
505                my $Content = $Self->_LinkObjectContentStringCreate(
506                    Object       => $Block->{Object},
507                    ContentData  => $Column,
508                    LayoutObject => $LayoutObject2,
509                );
510
511                # output a table column block
512                $LayoutObject->Block(
513                    Name => 'TableComplexBlockRowColumn',
514                    Data => {
515                        %{$Column},
516                        Content => $Content,
517                    },
518                );
519            }
520        }
521
522        if ( $Param{ViewMode} eq 'ComplexAdd' ) {
523
524            # output the action row block
525            $LayoutObject->Block(
526                Name => 'TableComplexBlockActionRow',
527            );
528
529            $LayoutObject->Block(
530                Name => 'TableComplexBlockActionRowBulk',
531                Data => {
532                    Name        => Translatable('Bulk'),
533                    TableNumber => $BlockCounter,
534                },
535            );
536
537            # output the footer block
538            $LayoutObject->Block(
539                Name => 'TableComplexBlockFooterAdd',
540                Data => {
541                    LinkTypeStrg => $Param{LinkTypeStrg} || '',
542                },
543            );
544        }
545
546        elsif ( $Param{ViewMode} eq 'ComplexDelete' ) {
547
548            # output the action row block
549            $LayoutObject->Block(
550                Name => 'TableComplexBlockActionRow',
551            );
552
553            $LayoutObject->Block(
554                Name => 'TableComplexBlockActionRowBulk',
555                Data => {
556                    Name        => Translatable('Bulk'),
557                    TableNumber => $BlockCounter,
558                },
559            );
560
561            # output the footer block
562            $LayoutObject->Block(
563                Name => 'TableComplexBlockFooterDelete',
564            );
565        }
566        else {
567
568            # output the footer block
569            $LayoutObject->Block(
570                Name => 'TableComplexBlockFooterNormal',
571            );
572        }
573
574        # increase BlockCounter to set correct IDs for Select All Check-boxes
575        $BlockCounter++;
576    }
577
578    # Send LinkObjectTables to JS.
579    $LayoutObject->AddJSData(
580        Key   => 'LinkObjectTables',
581        Value => \@LinkObjectTables,
582    );
583
584    return $LayoutObject->Output(
585        TemplateFile => 'LinkObject',
586        AJAX         => $Param{AJAX},
587    );
588}
589
590=head2 LinkObjectTableCreateSimple()
591
592create a simple output table
593
594    my $String = $LayoutObject->LinkObjectTableCreateSimple(
595        LinkListWithData => $LinkListWithDataRef,
596        ViewMode         => 'SimpleRaw',            # (optional) (Simple|SimpleRaw)
597    );
598
599=cut
600
601sub LinkObjectTableCreateSimple {
602    my ( $Self, %Param ) = @_;
603
604    # check needed stuff
605    if ( !$Param{LinkListWithData} || ref $Param{LinkListWithData} ne 'HASH' ) {
606        $Kernel::OM->Get('Kernel::System::Log')->Log(
607            Priority => 'error',
608            Message  => 'Need LinkListWithData!',
609        );
610        return;
611    }
612
613    # get type list
614    my %TypeList = $Kernel::OM->Get('Kernel::System::LinkObject')->TypeList(
615        UserID => $Self->{UserID},
616    );
617
618    return if !%TypeList;
619
620    my %OutputData;
621    OBJECT:
622    for my $Object ( sort keys %{ $Param{LinkListWithData} } ) {
623
624        # load backend
625        my $BackendObject = $Self->_LoadLinkObjectLayoutBackend(
626            Object => $Object,
627        );
628
629        next OBJECT if !$BackendObject;
630
631        # get link output data
632        my %LinkOutputData = $BackendObject->TableCreateSimple(
633            ObjectLinkListWithData => $Param{LinkListWithData}->{$Object},
634        );
635
636        next OBJECT if !%LinkOutputData;
637
638        for my $LinkType ( sort keys %LinkOutputData ) {
639
640            $OutputData{$LinkType}->{$Object} = $LinkOutputData{$LinkType}->{$Object};
641        }
642    }
643
644    return %OutputData if $Param{ViewMode} && $Param{ViewMode} eq 'SimpleRaw';
645
646    # create new instance of the layout object
647    my $LayoutObject  = Kernel::Output::HTML::Layout->new( %{$Self} );
648    my $LayoutObject2 = Kernel::Output::HTML::Layout->new( %{$Self} );
649
650    my $Count = 0;
651    for my $LinkTypeLinkDirection ( sort { lc $a cmp lc $b } keys %OutputData ) {
652        $Count++;
653
654        # output the table simple block
655        if ( $Count == 1 ) {
656            $LayoutObject->Block(
657                Name => 'TableSimple',
658            );
659        }
660
661        # investigate link type name
662        my @LinkData     = split q{::}, $LinkTypeLinkDirection;
663        my $LinkTypeName = $TypeList{ $LinkData[0] }->{ $LinkData[1] . 'Name' };
664
665        # output the type block
666        $LayoutObject->Block(
667            Name => 'TableSimpleType',
668            Data => {
669                LinkTypeName => $LinkTypeName,
670            },
671        );
672
673        # extract object list
674        my $ObjectList = $OutputData{$LinkTypeLinkDirection};
675
676        for my $Object ( sort { lc $a cmp lc $b } keys %{$ObjectList} ) {
677
678            for my $Item ( @{ $ObjectList->{$Object} } ) {
679
680                # create the content string
681                my $Content = $Self->_LinkObjectContentStringCreate(
682                    Object       => $Object,
683                    ContentData  => $Item,
684                    LayoutObject => $LayoutObject2,
685                );
686
687                # output the type block
688                $LayoutObject->Block(
689                    Name => 'TableSimpleTypeRow',
690                    Data => {
691                        %{$Item},
692                        Content => $Content,
693                    },
694                );
695            }
696        }
697    }
698
699    # show no linked object available
700    if ( !$Count ) {
701        $LayoutObject->Block(
702            Name => 'TableSimpleNone',
703            Data => {},
704        );
705    }
706
707    return $LayoutObject->Output(
708        TemplateFile => 'LinkObject',
709    );
710}
711
712=head2 LinkObjectSelectableObjectList()
713
714return a selection list of link-able objects
715
716    my $String = $LayoutObject->LinkObjectSelectableObjectList(
717        Object   => 'Ticket',
718        Selected => $Identifier,  # (optional)
719    );
720
721=cut
722
723sub LinkObjectSelectableObjectList {
724    my ( $Self, %Param ) = @_;
725
726    # check needed stuff
727    if ( !$Param{Object} ) {
728        $Kernel::OM->Get('Kernel::System::Log')->Log(
729            Priority => 'error',
730            Message  => 'Need Object!'
731        );
732        return;
733    }
734
735    # get possible objects list
736    my %PossibleObjectsList = $Kernel::OM->Get('Kernel::System::LinkObject')->PossibleObjectsList(
737        Object => $Param{Object},
738        UserID => $Self->{UserID},
739    );
740
741    return if !%PossibleObjectsList;
742
743    # get the select lists
744    my @SelectableObjectList;
745    my @SelectableTempList;
746    my $AddBlankLines;
747    POSSIBLEOBJECT:
748    for my $PossibleObject ( sort { lc $a cmp lc $b } keys %PossibleObjectsList ) {
749
750        # load backend
751        my $BackendObject = $Self->_LoadLinkObjectLayoutBackend(
752            Object => $PossibleObject,
753        );
754
755        return if !$BackendObject;
756
757        # get object select list
758        my @SelectableList = $BackendObject->SelectableObjectList(
759            %Param,
760        );
761
762        next POSSIBLEOBJECT if !@SelectableList;
763
764        push @SelectableTempList,   \@SelectableList;
765        push @SelectableObjectList, @SelectableList;
766
767        next POSSIBLEOBJECT if $AddBlankLines;
768
769        # check each keys if blank lines must be added
770        ROW:
771        for my $Row (@SelectableList) {
772            next ROW if !$Row->{Key} || $Row->{Key} !~ m{ :: }xms;
773            $AddBlankLines = 1;
774            last ROW;
775        }
776    }
777
778    # add blank lines
779    if ($AddBlankLines) {
780
781        # reset list
782        @SelectableObjectList = ();
783
784        # define blank line entry
785        my %BlankLine = (
786            Key      => '-',
787            Value    => '-------------------------',
788            Disabled => 1,
789        );
790
791        # insert the blank lines
792        for my $Elements (@SelectableTempList) {
793            push @SelectableObjectList, @{$Elements};
794        }
795        continue {
796            push @SelectableObjectList, \%BlankLine;
797        }
798
799        # add blank lines in top of the list
800        unshift @SelectableObjectList, \%BlankLine;
801    }
802
803    # create new instance of the layout object
804    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
805
806    # create target object string
807    my $TargetObjectStrg = $LayoutObject->BuildSelection(
808        Data     => \@SelectableObjectList,
809        Name     => 'TargetIdentifier',
810        Class    => 'Modernize',
811        TreeView => 1,
812    );
813
814    return $TargetObjectStrg;
815}
816
817=head2 LinkObjectSearchOptionList()
818
819return a list of search options
820
821    my @SearchOptionList = $LayoutObject->LinkObjectSearchOptionList(
822        Object    => 'Ticket',
823        SubObject => 'Bla',     # (optional)
824    );
825
826=cut
827
828sub LinkObjectSearchOptionList {
829    my ( $Self, %Param ) = @_;
830
831    # check needed stuff
832    if ( !$Param{Object} ) {
833        $Kernel::OM->Get('Kernel::System::Log')->Log(
834            Priority => 'error',
835            Message  => 'Need Object!'
836        );
837        return;
838    }
839
840    # load backend
841    my $BackendObject = $Self->_LoadLinkObjectLayoutBackend(
842        Object => $Param{Object},
843    );
844
845    return if !$BackendObject;
846
847    # get search option list
848    my @SearchOptionList = $BackendObject->SearchOptionList(
849        %Param,
850    );
851
852    return @SearchOptionList;
853}
854
855=head2 ComplexTablePreferencesGet()
856
857get items needed for AllocationList initialization.
858
859    my %Preferences = $LayoutObject->ComplexTablePreferencesGet(
860        Config  => {
861            'DefaultColumns' => {
862                'Age' => 1,
863                'EscalationTime' => 1,
864                ...
865            },
866            Priority => {
867                'Age' => 120,
868                'TicketNumber' => 100,
869                ...
870            }
871        }.
872        PrefKey => "LinkObject::ComplexTable-Ticket",
873    );
874
875returns:
876    %Preferences =  {
877        'ColumnsAvailable' => '["Age","Changed","CustomerID","CustomerName","CustomerUserID",...]',
878        'Block' => 'AllocationList',
879        'Translation' => 1,
880        'Name' => 'ContentLarge',
881        'Columns' => '{"Columns":{"SLA":0,"Type":0,"Owner":0,"Service":0,"CustomerUserID":0,...}}',
882        'Desc' => 'Shown Columns',
883        'ColumnsEnabled' => '["State","TicketNumber","Title","Created","Queue"]',
884    };
885
886=cut
887
888sub ComplexTablePreferencesGet {
889    my ( $Self, %Param ) = @_;
890
891    # configure columns
892    my @ColumnsEnabled;
893    my @ColumnsAvailable;
894    my @ColumnsAvailableNotEnabled;
895
896    # check for default settings
897    if (
898        $Param{Config}->{DefaultColumns}
899        && IsHashRefWithData( $Param{Config}->{DefaultColumns} )
900        )
901    {
902        @ColumnsAvailable = grep { $Param{Config}->{DefaultColumns}->{$_} }
903            keys %{ $Param{Config}->{DefaultColumns} };
904        @ColumnsEnabled = grep { $Param{Config}->{DefaultColumns}->{$_} eq '2' }
905            keys %{ $Param{Config}->{DefaultColumns} };
906
907        if (
908            $Param{Config}->{Priority}
909            && IsHashRefWithData( $Param{Config}->{Priority} )
910            )
911        {
912            # sort according to priority defined in SysConfig
913            @ColumnsEnabled
914                = sort { $Param{Config}->{Priority}->{$a} <=> $Param{Config}->{Priority}->{$b} } @ColumnsEnabled;
915        }
916    }
917
918    # check if the user has filter preferences for this widget
919    my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
920        UserID => $Self->{UserID},
921    );
922
923    # get JSON object
924    my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
925
926    # if preference settings are available, take them
927    if ( $Preferences{ $Param{PrefKey} } ) {
928
929        my $ColumnsEnabled = $JSONObject->Decode(
930            Data => $Preferences{ $Param{PrefKey} },
931        );
932
933        @ColumnsEnabled = grep { $ColumnsEnabled->{Columns}->{$_} == 1 }
934            keys %{ $ColumnsEnabled->{Columns} };
935
936        if ( $ColumnsEnabled->{Order} && @{ $ColumnsEnabled->{Order} } ) {
937            @ColumnsEnabled = @{ $ColumnsEnabled->{Order} };
938        }
939
940        # remove duplicate columns
941        my %UniqueColumns;
942        my @ColumnsEnabledAux;
943
944        for my $Column (@ColumnsEnabled) {
945            if ( !$UniqueColumns{$Column} ) {
946                push @ColumnsEnabledAux, $Column;
947            }
948            $UniqueColumns{$Column} = 1;
949        }
950
951        # set filtered column list
952        @ColumnsEnabled = @ColumnsEnabledAux;
953    }
954
955    my %Columns;
956    for my $ColumnName ( sort { $a cmp $b } @ColumnsAvailable ) {
957        $Columns{Columns}->{$ColumnName} = ( grep { $ColumnName eq $_ } @ColumnsEnabled ) ? 1 : 0;
958        if ( !grep { $_ eq $ColumnName } @ColumnsEnabled ) {
959            push @ColumnsAvailableNotEnabled, $ColumnName;
960        }
961    }
962    $Columns{Order} = \@ColumnsEnabled;
963
964    my %Params = (
965        Desc             => Translatable('Shown Columns'),
966        Name             => "ContentLarge",
967        Block            => 'AllocationList',
968        Columns          => $JSONObject->Encode( Data => \%Columns ),
969        ColumnsEnabled   => $JSONObject->Encode( Data => \@ColumnsEnabled ),
970        ColumnsAvailable => $JSONObject->Encode( Data => \@ColumnsAvailableNotEnabled ),
971        Translation      => 1,
972    );
973
974    return %Params;
975}
976
977=head2 ComplexTablePreferencesSet()
978
979set user preferences.
980
981    my $Success = $LayoutObject->ComplexTablePreferencesSet(
982        DestinationObject => 'Ticket',
983    );
984
985=cut
986
987sub ComplexTablePreferencesSet {
988    my ( $Self, %Param ) = @_;
989
990    # check needed stuff
991    for my $Needed (qw( DestinationObject)) {
992        if ( !$Param{$Needed} ) {
993            $Kernel::OM->Get('Kernel::System::Log')->Log(
994                Priority => 'error',
995                Message  => "Need $Needed!"
996            );
997            return;
998        }
999    }
1000
1001    # needed objects
1002    my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
1003    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
1004    my $JSONObject   = $Kernel::OM->Get('Kernel::System::JSON');
1005
1006    my $Result = 'Unknown';
1007    my $Config = $ConfigObject->Get("LinkObject::ComplexTable") || {};
1008
1009    # get default preferences
1010    my %Preferences = $Self->ComplexTablePreferencesGet(
1011        Config  => $Config->{ $Param{DestinationObject} },
1012        PrefKey => "LinkObject::ComplexTable-" . $Param{DestinationObject},
1013    );
1014
1015    if ( !%Preferences ) {
1016        $Kernel::OM->Get('Kernel::System::Log')->Log(
1017            Priority => 'error',
1018            Message  => "No preferences for $Param{DestinationObject}!"
1019        );
1020        return;
1021    }
1022
1023    # get params
1024    my $Value = $ParamObject->GetParam( Param => $Preferences{Name} );
1025
1026    # decode JSON value
1027    my $Preference = $JSONObject->Decode(
1028        Data => $Value,
1029    );
1030
1031    # remove Columns (not needed)
1032    delete $Preference->{Columns};
1033
1034    if ( $Param{DestinationObject} eq 'Ticket' ) {
1035
1036        # Make sure that ticket number is always present, otherwise there will be problems.
1037        if ( !grep { $_ eq 'TicketNumber' } @{ $Preference->{Order} } ) {
1038            unshift @{ $Preference->{Order} }, 'TicketNumber';
1039        }
1040    }
1041
1042    if ( IsHashRefWithData($Preference) ) {
1043
1044        $Value = $JSONObject->Encode(
1045            Data => $Preference,
1046        );
1047
1048        # update runtime vars
1049        $Self->{ $Preferences{Name} } = $Value;
1050
1051        # update session
1052        $Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
1053            SessionID => $Self->{SessionID},
1054            Key       => $Preferences{Name},
1055            Value     => $Value,
1056        );
1057
1058        # update preferences
1059        if ( !$ConfigObject->Get('DemoSystem') ) {
1060            $Kernel::OM->Get('Kernel::System::User')->SetPreferences(
1061                UserID => $Self->{UserID},
1062                Key    => "LinkObject::ComplexTable-" . $Param{DestinationObject},
1063                Value  => $Value,
1064            );
1065
1066            return 1;
1067        }
1068    }
1069
1070    return 0;
1071}
1072
1073=begin Internal:
1074
1075=head2 _LinkObjectContentStringCreate()
1076
1077return a output string
1078
1079    my $String = $LayoutObject->_LinkObjectContentStringCreate(
1080        Object       => 'Ticket',
1081        ContentData  => $HashRef,
1082        LayoutObject => $LocalLayoutObject,
1083    );
1084
1085=cut
1086
1087sub _LinkObjectContentStringCreate {
1088    my ( $Self, %Param ) = @_;
1089
1090    # check needed stuff
1091    for my $Argument (qw(Object ContentData LayoutObject)) {
1092        if ( !$Param{$Argument} ) {
1093            $Kernel::OM->Get('Kernel::System::Log')->Log(
1094                Priority => 'error',
1095                Message  => "Need $Argument!",
1096            );
1097            return;
1098        }
1099    }
1100
1101    # load link core module
1102    my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject');
1103
1104    # load backend
1105    my $BackendObject = $Self->_LoadLinkObjectLayoutBackend(
1106        Object => $Param{Object},
1107    );
1108
1109    # create content string in backend module
1110    if ($BackendObject) {
1111
1112        my $ContentString = $BackendObject->ContentStringCreate(
1113            %Param,
1114            LinkObject   => $LinkObject,
1115            LayoutObject => $Param{LayoutObject},
1116        );
1117
1118        return $ContentString if defined $ContentString;
1119    }
1120
1121    # extract content
1122    my $Content = $Param{ContentData};
1123
1124    # set blockname
1125    my $Blockname = $Content->{Type};
1126
1127    # set global default value
1128    $Content->{MaxLength} ||= 100;
1129
1130    # prepare linktypelist
1131    if ( $Content->{Type} eq 'LinkTypeList' ) {
1132
1133        $Blockname = 'Plain';
1134
1135        # get type list
1136        my %TypeList = $LinkObject->TypeList(
1137            UserID => $Self->{UserID},
1138        );
1139
1140        return if !%TypeList;
1141
1142        my @LinkNameList;
1143        LINKTYPE:
1144        for my $LinkType ( sort { lc $a cmp lc $b } keys %{ $Content->{LinkTypeList} } ) {
1145
1146            next LINKTYPE if $LinkType eq 'NOTLINKED';
1147
1148            # extract direction
1149            my $Direction = $Content->{LinkTypeList}->{$LinkType};
1150
1151            # extract linkname
1152            my $LinkName = $TypeList{$LinkType}->{ $Direction . 'Name' };
1153
1154            # translate
1155            if ( $Content->{Translate} ) {
1156                $LinkName = $Param{LayoutObject}->{LanguageObject}->Translate($LinkName);
1157            }
1158
1159            push @LinkNameList, $LinkName;
1160        }
1161
1162        # join string
1163        my $String = join qq{\n}, @LinkNameList;
1164
1165        # transform ascii to html
1166        $Content->{Content} = $Param{LayoutObject}->Ascii2Html(
1167            Text           => $String || '-',
1168            HTMLResultMode => 1,
1169            LinkFeature    => 0,
1170        );
1171    }
1172
1173    # prepare checkbox delete
1174    elsif ( $Content->{Type} eq 'CheckboxDelete' ) {
1175
1176        $Blockname = 'Plain';
1177
1178        # get type list
1179        my %TypeList = $LinkObject->TypeList(
1180            UserID => $Self->{UserID},
1181        );
1182
1183        return if !%TypeList;
1184
1185        LINKTYPE:
1186        for my $LinkType ( sort { lc $a cmp lc $b } keys %{ $Content->{LinkTypeList} } ) {
1187
1188            next LINKTYPE if $LinkType eq 'NOTLINKED';
1189
1190            # extract direction
1191            my $Direction = $Content->{LinkTypeList}->{$LinkType};
1192
1193            # extract linkname
1194            my $LinkName = $TypeList{$LinkType}->{ $Direction . 'Name' };
1195
1196            # translate
1197            if ( $Content->{Translate} ) {
1198                $LinkName = $Param{LayoutObject}->{LanguageObject}->Translate($LinkName);
1199            }
1200
1201            # run checkbox block
1202            $Param{LayoutObject}->Block(
1203                Name => 'Checkbox',
1204                Data => {
1205                    %{$Content},
1206                    Name    => 'LinkDeleteIdentifier',
1207                    Title   => $LinkName,
1208                    Content => $Content->{Object} . '::' . $Content->{Key} . '::' . $LinkType,
1209                },
1210            );
1211        }
1212
1213        $Content->{Content} = $Param{LayoutObject}->Output(
1214            TemplateFile => 'LinkObject',
1215        );
1216    }
1217
1218    elsif ( $Content->{Type} eq 'TimeLong' ) {
1219        $Blockname = 'TimeLong';
1220    }
1221
1222    elsif ( $Content->{Type} eq 'Date' ) {
1223        $Blockname = 'Date';
1224    }
1225
1226    # prepare text
1227    elsif ( $Content->{Type} eq 'Text' || !$Content->{Type} ) {
1228
1229        $Blockname = $Content->{Translate} ? 'TextTranslate' : 'Text';
1230        $Content->{Content} ||= '-';
1231    }
1232
1233    # run block
1234    $Param{LayoutObject}->Block(
1235        Name => $Blockname,
1236        Data => $Content,
1237    );
1238
1239    return $Param{LayoutObject}->Output(
1240        TemplateFile => 'LinkObject',
1241    );
1242}
1243
1244=head2 _LoadLinkObjectLayoutBackend()
1245
1246load a linkobject layout backend module
1247
1248    $BackendObject = $LayoutObject->_LoadLinkObjectLayoutBackend(
1249        Object => 'Ticket',
1250    );
1251
1252=cut
1253
1254sub _LoadLinkObjectLayoutBackend {
1255    my ( $Self, %Param ) = @_;
1256
1257    # get log object
1258    my $LogObject = $Kernel::OM->Get('Kernel::System::Log');
1259
1260    # check needed stuff
1261    if ( !$Param{Object} ) {
1262        $LogObject->Log(
1263            Priority => 'error',
1264            Message  => 'Need Object!',
1265        );
1266        return;
1267    }
1268
1269    # check if object is already cached
1270    return $Self->{Cache}->{LoadLinkObjectLayoutBackend}->{ $Param{Object} }
1271        if $Self->{Cache}->{LoadLinkObjectLayoutBackend}->{ $Param{Object} };
1272
1273    my $GenericModule = "Kernel::Output::HTML::LinkObject::$Param{Object}";
1274
1275    # load the backend module
1276    if ( !$Kernel::OM->Get('Kernel::System::Main')->Require($GenericModule) ) {
1277        $LogObject->Log(
1278            Priority => 'error',
1279            Message  => "Can't load backend module $Param{Object}!"
1280        );
1281        return;
1282    }
1283
1284    # create new instance
1285    my $BackendObject = $GenericModule->new(
1286        %{$Self},
1287        %Param,
1288    );
1289
1290    if ( !$BackendObject ) {
1291        $LogObject->Log(
1292            Priority => 'error',
1293            Message  => "Can't create a new instance of backend module $Param{Object}!",
1294        );
1295        return;
1296    }
1297
1298    # cache the object
1299    $Self->{Cache}->{LoadLinkObjectLayoutBackend}->{ $Param{Object} } = $BackendObject;
1300
1301    return $BackendObject;
1302}
1303
1304=end Internal:
1305
1306=cut
1307
13081;
1309
1310=head1 TERMS AND CONDITIONS
1311
1312This software is part of the OTRS project (L<https://otrs.org/>).
1313
1314This software comes with ABSOLUTELY NO WARRANTY. For details, see
1315the enclosed file COPYING for license information (GPL). If you
1316did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1317
1318=cut
1319