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::Modules::AgentLinkObject;
10
11use strict;
12use warnings;
13
14use Kernel::Language qw(Translatable);
15
16our $ObjectManagerDisabled = 1;
17
18sub new {
19    my ( $Type, %Param ) = @_;
20
21    # allocate new hash for object
22    my $Self = {%Param};
23    bless( $Self, $Type );
24
25    return $Self;
26}
27
28sub Run {
29    my ( $Self, %Param ) = @_;
30
31    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
32
33    if ( $Self->{Subaction} eq 'UpdateComplextTablePreferences' ) {
34
35        # save user preferences (shown columns)
36
37        # Needed objects
38        my $ParamObject  = $Kernel::OM->Get('Kernel::System::Web::Request');
39        my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
40        my $JSONObject   = $Kernel::OM->Get('Kernel::System::JSON');
41
42        # challenge token check for write action
43        $LayoutObject->ChallengeTokenCheck();
44
45        my $SourceObject                   = $ParamObject->GetParam( Param => 'SourceObject' )                   || '';
46        my $SourceObjectID                 = $ParamObject->GetParam( Param => 'SourceObjectID' )                 || '';
47        my $DestinationObject              = $ParamObject->GetParam( Param => 'DestinationObject' )              || '';
48        my $AdditionalLinkListWithDataJSON = $ParamObject->GetParam( Param => 'AdditionalLinkListWithDataJSON' ) || '';
49
50        my $Success = $LayoutObject->ComplexTablePreferencesSet(
51            DestinationObject => $DestinationObject,
52        );
53
54        if ( !$Success ) {
55            $Kernel::OM->Get('Kernel::System::Log')->Log(
56                Priority => 'error',
57                Message  => "System was unable to update preferences!",
58            );
59            return;
60        }
61
62        # get linked objects
63        my $LinkListWithData = $Kernel::OM->Get('Kernel::System::LinkObject')->LinkListWithData(
64            Object           => $SourceObject,
65            Object2          => $DestinationObject,
66            Key              => $SourceObjectID,
67            State            => 'Valid',
68            UserID           => $Self->{UserID},
69            ObjectParameters => {
70                Ticket => {
71                    IgnoreLinkedTicketStateTypes => 1,
72                },
73            },
74        );
75
76        if ($AdditionalLinkListWithDataJSON) {
77
78            # decode JSON string
79            my $AdditionalLinkListWithData = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
80                Data => $AdditionalLinkListWithDataJSON,
81            );
82
83            $LinkListWithData = {
84                %{$LinkListWithData},
85                %{$AdditionalLinkListWithData},
86            };
87        }
88
89        # create the link table
90        my $LinkTableStrg = $LayoutObject->LinkObjectTableCreate(
91            LinkListWithData               => $LinkListWithData,
92            ViewMode                       => 'Complex',                         # only make sense for complex
93            Object                         => $SourceObject,
94            Key                            => $SourceObjectID,
95            AJAX                           => 1,
96            AdditionalLinkListWithDataJSON => $AdditionalLinkListWithDataJSON,
97        );
98
99        return $LayoutObject->Attachment(
100            ContentType => 'text/html',
101            Content     => $LinkTableStrg,
102            Type        => 'inline',
103            NoCache     => 1,
104        );
105    }
106
107    # ------------------------------------------------------------ #
108    # close
109    # ------------------------------------------------------------ #
110    if ( $Self->{Subaction} eq 'Close' ) {
111        return $LayoutObject->PopupClose(
112            Reload => 1,
113        );
114    }
115
116    # get params
117    my %Form;
118    my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
119    $Form{SourceObject} = $ParamObject->GetParam( Param => 'SourceObject' );
120    $Form{SourceKey}    = $ParamObject->GetParam( Param => 'SourceKey' );
121    $Form{Mode}         = $ParamObject->GetParam( Param => 'Mode' ) || 'Normal';
122
123    # check needed stuff
124    if ( !$Form{SourceObject} || !$Form{SourceKey} ) {
125        return $LayoutObject->ErrorScreen(
126            Message => Translatable('Need SourceObject and SourceKey!'),
127            Comment => Translatable('Please contact the administrator.'),
128        );
129    }
130
131    my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject');
132
133    # check if this is a temporary ticket link used while creating a new ticket
134    my $TemporarySourceTicketLink;
135    if (
136        $Form{Mode} eq 'Temporary'
137        && $Form{SourceObject} eq 'Ticket'
138        && $Form{SourceKey} =~ m{ \A \d+ \. \d+ }xms
139        )
140    {
141        $TemporarySourceTicketLink = 1;
142    }
143
144    # do the permission check only if it is no temporary ticket link used while creating a new ticket
145    if ( !$TemporarySourceTicketLink ) {
146
147        # permission check
148        my $Permission = $LinkObject->ObjectPermission(
149            Object => $Form{SourceObject},
150            Key    => $Form{SourceKey},
151            UserID => $Self->{UserID},
152        );
153
154        if ( !$Permission ) {
155            return $LayoutObject->NoPermission(
156                Message    => Translatable('You need ro permission!'),
157                WithHeader => 'yes',
158            );
159        }
160    }
161
162    # get form params
163    $Form{TargetIdentifier} = $ParamObject->GetParam( Param => 'TargetIdentifier' )
164        || $Form{SourceObject};
165
166    # investigate the target object
167    if ( $Form{TargetIdentifier} =~ m{ \A ( .+? ) :: ( .+ ) \z }xms ) {
168        $Form{TargetObject}    = $1;
169        $Form{TargetSubObject} = $2;
170    }
171    else {
172        $Form{TargetObject} = $Form{TargetIdentifier};
173    }
174
175    # get possible objects list
176    my %PossibleObjectsList = $LinkObject->PossibleObjectsList(
177        Object => $Form{SourceObject},
178        UserID => $Self->{UserID},
179    );
180
181    # check if target object is a possible object to link with the source object
182    if ( !$PossibleObjectsList{ $Form{TargetObject} } ) {
183        my @PossibleObjects = sort { lc $a cmp lc $b } keys %PossibleObjectsList;
184        $Form{TargetObject} = $PossibleObjects[0];
185    }
186
187    # set mode params
188    if ( $Form{Mode} eq 'Temporary' ) {
189        $Form{State} = 'Temporary';
190    }
191    else {
192        $Form{Mode}  = 'Normal';
193        $Form{State} = 'Valid';
194    }
195
196    # get source object description
197    my %SourceObjectDescription = $LinkObject->ObjectDescriptionGet(
198        Object => $Form{SourceObject},
199        Key    => $Form{SourceKey},
200        Mode   => $Form{Mode},
201        UserID => $Self->{UserID},
202    );
203
204    # output header
205    my $Output = $LayoutObject->Header( Type => 'Small' );
206    my $ActiveTab;
207
208    # ------------------------------------------------------------ #
209    # link delete
210    # ------------------------------------------------------------ #
211    if ( $Self->{Subaction} eq 'LinkDelete' && $ParamObject->GetParam( Param => 'SubmitDelete' ) ) {
212
213        # challenge token check for write action
214        $LayoutObject->ChallengeTokenCheck();
215
216        # delete all temporary links older than one day
217        $LinkObject->LinkCleanup(
218            State  => 'Temporary',
219            Age    => ( 60 * 60 * 24 ),
220            UserID => $Self->{UserID},
221        );
222
223        # get the link delete keys and target object
224        my @LinkDeleteIdentifier = $ParamObject->GetArray(
225            Param => 'LinkDeleteIdentifier',
226        );
227
228        my $SuccessCounter = 0;
229
230        # delete links from database
231        IDENTIFIER:
232        for my $Identifier (@LinkDeleteIdentifier) {
233
234            my @Target = $Identifier =~ m{^ ( [^:]+? ) :: (.+?) :: ( [^:]+? ) $}smx;
235
236            next IDENTIFIER if !$Target[0];    # TargetObject
237            next IDENTIFIER if !$Target[1];    # TargetKey
238            next IDENTIFIER if !$Target[2];    # LinkType
239
240            my $DeletePermission = $LinkObject->ObjectPermission(
241                Object => $Target[0],
242                Key    => $Target[1],
243                UserID => $Self->{UserID},
244            );
245
246            next IDENTIFIER if !$DeletePermission;
247
248            # delete link from database
249            my $Success = $LinkObject->LinkDelete(
250                Object1 => $Form{SourceObject},
251                Key1    => $Form{SourceKey},
252                Object2 => $Target[0],
253                Key2    => $Target[1],
254                Type    => $Target[2],
255                UserID  => $Self->{UserID},
256            );
257
258            if ($Success) {
259                $SuccessCounter++;
260                next IDENTIFIER;
261            }
262
263            # get target object description
264            my %TargetObjectDescription = $LinkObject->ObjectDescriptionGet(
265                Object => $Target[0],
266                Key    => $Target[1],
267                Mode   => $Form{Mode},
268                UserID => $Self->{UserID},
269            );
270
271            # output an error notification
272            $Output .= $LayoutObject->Notify(
273                Priority => 'Error',
274                Data     => $LayoutObject->{LanguageObject}->Translate(
275                    "Can not delete link with %s!",
276                    $TargetObjectDescription{Normal},
277                ),
278            );
279        }
280
281        if ($SuccessCounter) {
282            $Output .= $LayoutObject->Notify(
283                Priority => 'Info',
284                Data =>
285                    $LayoutObject->{LanguageObject}->Translate( "%s Link(s) deleted successfully.", $SuccessCounter ),
286            );
287        }
288
289        $ActiveTab = 'ManageLinks';
290    }
291
292    # ------------------------------------------------------------ #
293    # instant link delete (from the link table)
294    # ------------------------------------------------------------ #
295    elsif ( $Self->{Subaction} eq 'InstantLinkDelete' ) {
296
297        # challenge token check for write action
298        $LayoutObject->ChallengeTokenCheck();
299
300        # get target identifier and redirect URL
301        my $TargetIdentifier = $ParamObject->GetParam( Param => 'TargetIdentifier' );
302        my $Redirect         = $ParamObject->GetParam( Param => 'Redirect' );
303
304        # get target components
305        my @Target = $TargetIdentifier =~ m{^ ( [^:]+? ) :: (.+?) :: ( [^:]+? ) $}smx;
306
307        if (
308            $Target[0]       # TargetObject
309            && $Target[1]    # TargetKey
310            && $Target[2]    # LinkType
311            )
312        {
313
314            # check source permission
315            my $SourcePermission = $LinkObject->ObjectPermission(
316                Object => $Form{SourceObject},
317                Key    => $Form{SourceKey},
318                UserID => $Self->{UserID},
319            );
320
321            # check target permission
322            my $TargetPermission = $LinkObject->ObjectPermission(
323                Object => $Target[0],
324                Key    => $Target[1],
325                UserID => $Self->{UserID},
326            );
327
328            if ( !$SourcePermission || !$TargetPermission ) {
329                return $LayoutObject->NoPermission(
330                    Message    => Translatable('You need ro permission!'),
331                    WithHeader => 'yes',
332                );
333            }
334
335            # delete link from database
336            my $Success = $LinkObject->LinkDelete(
337                Object1 => $Form{SourceObject},
338                Key1    => $Form{SourceKey},
339                Object2 => $Target[0],
340                Key2    => $Target[1],
341                Type    => $Target[2],
342                UserID  => $Self->{UserID},
343            );
344        }
345
346        # build empty JSON output
347        my $JSON = $LayoutObject->JSONEncode(
348            Data => {
349                Success => 1,
350            },
351        );
352
353        # send JSON response
354        return $LayoutObject->Attachment(
355            ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
356            Content     => $JSON,
357            Type        => 'inline',
358            NoCache     => 1,
359        );
360    }
361
362    # ------------------------------------------------------------ #
363    # overview
364    # ------------------------------------------------------------ #
365
366    # get the type
367    my $TypeIdentifier = $ParamObject->GetParam( Param => 'TypeIdentifier' );
368
369    # add new links
370    if ( $ParamObject->GetParam( Param => 'SubmitLink' ) ) {
371
372        # challenge token check for write action
373        $LayoutObject->ChallengeTokenCheck();
374
375        # get the link target keys
376        my @LinkTargetKeys = $ParamObject->GetArray( Param => 'LinkTargetKeys' );
377
378        # get all links that the source object already has
379        my $LinkList = $LinkObject->LinkList(
380            Object => $Form{SourceObject},
381            Key    => $Form{SourceKey},
382            State  => $Form{State},
383            UserID => $Self->{UserID},
384        );
385
386        # split the identifier
387        my @Type = split q{::}, $TypeIdentifier;
388
389        if ( $Type[0] && $Type[1] && ( $Type[1] eq 'Source' || $Type[1] eq 'Target' ) ) {
390
391            my $SuccessCounter = 0;
392
393            # add links
394            TARGETKEYORG:
395            for my $TargetKeyOrg (@LinkTargetKeys) {
396
397                TYPE:
398                for my $LType ( sort keys %{ $LinkList->{ $Form{TargetObject} } } ) {
399
400                    # extract source and target
401                    my $Source = $LinkList->{ $Form{TargetObject} }->{$LType}->{Source} ||= {};
402                    my $Target = $LinkList->{ $Form{TargetObject} }->{$LType}->{Target} ||= {};
403
404                    # check if source and target object are already linked
405                    next TYPE
406                        if !$Source->{$TargetKeyOrg} && !$Target->{$TargetKeyOrg};
407
408                    # next type, if link already exists
409                    if ( $LType eq $Type[0] ) {
410                        next TYPE if $Type[1] eq 'Source' && $Source->{$TargetKeyOrg};
411                        next TYPE if $Type[1] eq 'Target' && $Target->{$TargetKeyOrg};
412                    }
413
414                    # check the type groups
415                    my $TypeGroupCheck = $LinkObject->PossibleType(
416                        Type1  => $Type[0],
417                        Type2  => $LType,
418                        UserID => $Self->{UserID},
419                    );
420
421                    next TYPE if $TypeGroupCheck && $Type[0] ne $LType;
422
423                    # get target object description
424                    my %TargetObjectDescription = $LinkObject->ObjectDescriptionGet(
425                        Object => $Form{TargetObject},
426                        Key    => $TargetKeyOrg,
427                        UserID => $Self->{UserID},
428                    );
429
430                    # lookup type id
431                    my $TypeID = $LinkObject->TypeLookup(
432                        Name   => $LType,
433                        UserID => $Self->{UserID},
434                    );
435
436                    # get type data
437                    my %TypeData = $LinkObject->TypeGet(
438                        TypeID => $TypeID,
439                        UserID => $Self->{UserID},
440                    );
441
442                    # investigate type name
443                    my $TypeName = $TypeData{SourceName};
444                    if ( $Target->{$TargetKeyOrg} ) {
445                        $TypeName = $TypeData{TargetName};
446                    }
447
448                    # translate the type name
449                    $TypeName = $LayoutObject->{LanguageObject}->Translate($TypeName);
450
451                    # output an error notification
452                    $Output .= $LayoutObject->Notify(
453                        Priority => 'Error',
454                        Data     => $LayoutObject->{LanguageObject}->Translate(
455                            'Can not create link with %s! Object already linked as %s.',
456                            $TargetObjectDescription{Normal},
457                            $TypeName,
458                        ),
459                    );
460
461                    next TARGETKEYORG;
462                }
463
464                my $SourceObject = $Form{TargetObject};
465                my $SourceKey    = $TargetKeyOrg;
466                my $TargetObject = $Form{SourceObject};
467                my $TargetKey    = $Form{SourceKey};
468
469                if ( $Type[1] eq 'Target' ) {
470                    $SourceObject = $Form{SourceObject};
471                    $SourceKey    = $Form{SourceKey};
472                    $TargetObject = $Form{TargetObject};
473                    $TargetKey    = $TargetKeyOrg;
474                }
475
476                # check if this is a temporary ticket link used while creating a new ticket
477                my $TemporaryTargetTicketLink;
478                if (
479                    $Form{Mode} eq 'Temporary'
480                    && $TargetObject eq 'Ticket'
481                    && $TargetKey =~ m{ \A \d+ \. \d+ }xms
482                    )
483                {
484                    $TemporaryTargetTicketLink = 1;
485                }
486
487                # do the permission check only if it is no temporary ticket link
488                # used while creating a new ticket
489                if ( !$TemporaryTargetTicketLink ) {
490
491                    my $AddPermission = $LinkObject->ObjectPermission(
492                        Object => $TargetObject,
493                        Key    => $TargetKey,
494                        UserID => $Self->{UserID},
495                    );
496
497                    next TARGETKEYORG if !$AddPermission;
498                }
499
500                # add links to database
501                my $Success = $LinkObject->LinkAdd(
502                    SourceObject => $SourceObject,
503                    SourceKey    => $SourceKey,
504                    TargetObject => $TargetObject,
505                    TargetKey    => $TargetKey,
506                    Type         => $Type[0],
507                    State        => $Form{State},
508                    UserID       => $Self->{UserID},
509                );
510
511                if ($Success) {
512                    $SuccessCounter++;
513                    next TARGETKEYORG;
514                }
515
516                # get target object description
517                my %TargetObjectDescription = $LinkObject->ObjectDescriptionGet(
518                    Object => $Form{TargetObject},
519                    Key    => $TargetKeyOrg,
520                    UserID => $Self->{UserID},
521                );
522
523                # output an error notification
524                $Output .= $LayoutObject->Notify(
525                    Priority => 'Error',
526                    Data     => $LayoutObject->{LanguageObject}->Translate(
527                        "Can not create link with %s!",
528                        $TargetObjectDescription{Normal}
529                    ),
530                );
531            }
532
533            if ($SuccessCounter) {
534                $Output .= $LayoutObject->Notify(
535                    Priority => 'Info',
536                    Data     => $LayoutObject->{LanguageObject}->Translate(
537                        "%s links added successfully.",
538                        $SuccessCounter,
539                    ),
540                );
541            }
542        }
543    }
544
545    # get the selectable object list
546    my $TargetObjectStrg = $LayoutObject->LinkObjectSelectableObjectList(
547        Object   => $Form{SourceObject},
548        Selected => $Form{TargetIdentifier},
549    );
550
551    # check needed stuff
552    if ( !$TargetObjectStrg ) {
553        return $LayoutObject->ErrorScreen(
554            Message => $LayoutObject->{LanguageObject}
555                ->Translate( 'The object %s cannot link with other object!', $Form{SourceObject} ),
556            Comment => Translatable('Please contact the administrator.'),
557        );
558    }
559
560    # output link block
561    $LayoutObject->Block(
562        Name => 'Link',
563        Data => {
564            %Form,
565            SourceObjectNormal => $SourceObjectDescription{Normal},
566            SourceObjectLong   => $SourceObjectDescription{Long},
567            TargetObjectStrg   => $TargetObjectStrg,
568        },
569    );
570
571    # output special block for temporary links
572    # to close the popup without reloading the parent window
573    if ( $Form{Mode} eq 'Temporary' ) {
574
575        $LayoutObject->AddJSData(
576            Key   => 'TemporaryLink',
577            Value => 1,
578        );
579    }
580
581    # get search option list
582    my @SearchOptionList = $LayoutObject->LinkObjectSearchOptionList(
583        Object    => $Form{TargetObject},
584        SubObject => $Form{TargetSubObject},
585    );
586
587    # output search option fields
588    for my $Option (@SearchOptionList) {
589
590        # output link search row block
591        $LayoutObject->Block(
592            Name => 'LinkSearchRow',
593            Data => $Option,
594        );
595    }
596
597    # create the search param hash
598    my %SearchParam;
599    OPTION:
600    for my $Option (@SearchOptionList) {
601
602        next OPTION if !$Option->{FormData};
603        next OPTION if $Option->{FormData}
604            && ref $Option->{FormData} eq 'ARRAY' && !@{ $Option->{FormData} };
605
606        $SearchParam{ $Option->{Key} } = $Option->{FormData};
607    }
608
609    # start search
610    my $SearchList;
611    if (
612        %SearchParam
613        || $Kernel::OM->Get('Kernel::Config')->Get('Frontend::AgentLinkObject::WildcardSearch')
614        )
615    {
616
617        $SearchList = $LinkObject->ObjectSearch(
618            Object       => $Form{TargetObject},
619            SubObject    => $Form{TargetSubObject},
620            SearchParams => \%SearchParam,
621            UserID       => $Self->{UserID},
622        );
623    }
624
625    # remove the source object from the search list
626    if ( $SearchList && $SearchList->{ $Form{SourceObject} } ) {
627
628        for my $LinkType ( sort keys %{ $SearchList->{ $Form{SourceObject} } } ) {
629
630            # extract link type List
631            my $LinkTypeList = $SearchList->{ $Form{SourceObject} }->{$LinkType};
632
633            for my $Direction ( sort keys %{$LinkTypeList} ) {
634
635                # remove the source key
636                delete $LinkTypeList->{$Direction}->{ $Form{SourceKey} };
637            }
638        }
639    }
640
641    # get already linked objects
642    my $LinkListWithData = $LinkObject->LinkListWithData(
643        Object => $Form{SourceObject},
644        Key    => $Form{SourceKey},
645        State  => $Form{State},
646        UserID => $Self->{UserID},
647    );
648
649    if ( $LinkListWithData && $LinkListWithData->{ $Form{TargetObject} } ) {
650
651        # build object id lookup hash from search list
652        my %SearchListObjectKeys;
653        for my $Key (
654            sort keys %{ $SearchList->{ $Form{TargetObject} }->{NOTLINKED}->{Source} }
655            )
656        {
657            $SearchListObjectKeys{$Key} = 1;
658        }
659
660        # check if linked objects are part of the search list
661        for my $LinkType ( sort keys %{ $LinkListWithData->{ $Form{TargetObject} } } ) {
662
663            # extract link type List
664            my $LinkTypeList = $LinkListWithData->{ $Form{TargetObject} }->{$LinkType};
665
666            for my $Direction ( sort keys %{$LinkTypeList} ) {
667
668                # extract the keys
669                KEY:
670                for my $Key ( sort keys %{ $LinkTypeList->{$Direction} } ) {
671
672                    next KEY if $SearchListObjectKeys{$Key};
673
674                    # delete from linked objects list if key is not in search list
675                    delete $LinkTypeList->{$Direction}->{$Key};
676                }
677            }
678        }
679    }
680
681    # add search result to link list
682    if ( $SearchList && $SearchList->{ $Form{TargetObject} } ) {
683        $LinkListWithData->{ $Form{TargetObject} }->{NOTLINKED} = $SearchList->{ $Form{TargetObject} }->{NOTLINKED};
684    }
685
686    # get possible types list
687    my %PossibleTypesList = $LinkObject->PossibleTypesList(
688        Object1 => $Form{SourceObject},
689        Object2 => $Form{TargetObject},
690        UserID  => $Self->{UserID},
691    );
692
693    # define blank line entry
694    my %BlankLine = (
695        Key      => '-',
696        Value    => '-------------------------',
697        Disabled => 1,
698    );
699
700    # create the selectable type list
701    my $Counter = 0;
702    my @SelectableTypesList;
703    POSSIBLETYPE:
704    for my $PossibleType ( sort { lc $a cmp lc $b } keys %PossibleTypesList ) {
705
706        # lookup type id
707        my $TypeID = $LinkObject->TypeLookup(
708            Name   => $PossibleType,
709            UserID => $Self->{UserID},
710        );
711
712        # get type
713        my %Type = $LinkObject->TypeGet(
714            TypeID => $TypeID,
715            UserID => $Self->{UserID},
716        );
717
718        # create the source name
719        my %SourceName;
720        $SourceName{Key}   = $PossibleType . '::Source';
721        $SourceName{Value} = $Type{SourceName};
722
723        push @SelectableTypesList, \%SourceName;
724
725        next POSSIBLETYPE if !$Type{Pointed};
726
727        # create the target name
728        my %TargetName;
729        $TargetName{Key}   = $PossibleType . '::Target';
730        $TargetName{Value} = $Type{TargetName};
731
732        push @SelectableTypesList, \%TargetName;
733    }
734    continue {
735
736        # add blank line
737        push @SelectableTypesList, \%BlankLine;
738
739        $Counter++;
740    }
741
742    # removed last (empty) entry
743    pop @SelectableTypesList;
744
745    # add blank lines on top and bottom of the list if more then two linktypes
746    if ( $Counter > 2 ) {
747        unshift @SelectableTypesList, \%BlankLine;
748        push @SelectableTypesList, \%BlankLine;
749    }
750
751    # create link type string
752    my $LinkTypeStrg = $LayoutObject->BuildSelection(
753        Data       => \@SelectableTypesList,
754        Name       => 'TypeIdentifier',
755        SelectedID => $TypeIdentifier || 'Normal::Source',
756        Class      => 'Modernize',
757    );
758
759    # create the link table
760    my $LinkTableStrg = $LayoutObject->LinkObjectTableCreateComplex(
761        LinkListWithData => {
762            $Form{TargetObject} => $LinkListWithData->{ $Form{TargetObject} },
763        },
764        ViewMode     => 'ComplexAdd',
765        LinkTypeStrg => $LinkTypeStrg,
766    );
767
768    # output the link table
769    $LayoutObject->Block(
770        Name => 'LinkTableComplex',
771        Data => {
772            LinkTableStrg => $LinkTableStrg,
773        },
774    );
775
776    # get already linked objects
777    $LinkListWithData = $LinkObject->LinkListWithData(
778        Object => $Form{SourceObject},
779        Key    => $Form{SourceKey},
780        State  => $Form{State},
781        UserID => $Self->{UserID},
782    );
783
784    # create the link table
785    $LinkTableStrg = $LayoutObject->LinkObjectTableCreateComplex(
786        LinkListWithData => $LinkListWithData,
787        ViewMode         => 'ComplexDelete',
788    );
789
790    my $ManageTabDisabled = 0;
791    if ( !$LinkTableStrg ) {
792        $ManageTabDisabled = 1;
793    }
794
795    # output link delete block
796    $LayoutObject->Block(
797        Name => 'Delete',
798        Data => {
799            %Form,
800            SourceObjectNormal => $SourceObjectDescription{Normal},
801        },
802    );
803
804    # output the link table
805    $LayoutObject->Block(
806        Name => 'DeleteTableComplex',
807        Data => {
808            LinkTableStrg => $LinkTableStrg,
809        },
810    );
811
812    # start template output
813    $Output .= $LayoutObject->Output(
814        TemplateFile => 'AgentLinkObject',
815        Data         => {
816            SourceObjectNormal => $SourceObjectDescription{Normal},
817            SourceObjectLong   => $SourceObjectDescription{Long},
818            TargetObjectStrg   => $TargetObjectStrg,
819            ActiveTab          => $ActiveTab,
820            ManageTabDisabled  => $ManageTabDisabled,
821        }
822    );
823
824    $Output .= $LayoutObject->Footer( Type => 'Small' );
825
826    return $Output;
827}
828
8291;
830