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