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