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::TicketOverview::Small; 10 11use strict; 12use warnings; 13 14use Kernel::System::VariableCheck qw(:all); 15use Kernel::Language qw(Translatable); 16 17our @ObjectDependencies = ( 18 'Kernel::Config', 19 'Kernel::Language', 20 'Kernel::System::Log', 21 'Kernel::Output::HTML::Layout', 22 'Kernel::System::CustomerUser', 23 'Kernel::System::Group', 24 'Kernel::System::User', 25 'Kernel::System::JSON', 26 'Kernel::System::DynamicField', 27 'Kernel::System::Ticket::ColumnFilter', 28 'Kernel::System::DynamicField::Backend', 29 'Kernel::System::Ticket', 30 'Kernel::System::Ticket::Article', 31 'Kernel::System::Main', 32); 33 34sub new { 35 my ( $Type, %Param ) = @_; 36 37 # allocate new hash for object 38 my $Self = \%Param; 39 bless( $Self, $Type ); 40 41 # get UserID param 42 $Self->{UserID} = $Param{UserID} || die "Got no UserID!"; 43 44 # get config object 45 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 46 47 # set pref for columns key 48 $Self->{PrefKeyColumns} = 'UserFilterColumnsEnabled' . '-' . $Self->{Action}; 49 50 # load backend config 51 my $BackendConfigKey = 'Ticket::Frontend::' . $Self->{Action}; 52 $Self->{Config} = $ConfigObject->Get($BackendConfigKey); 53 54 my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences( 55 UserID => $Self->{UserID}, 56 ); 57 58 # get JSON object 59 my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON'); 60 61 # set stored filters if present 62 my $StoredFiltersKey = 'UserStoredFilterColumns-' . $Self->{Action}; 63 if ( $Preferences{$StoredFiltersKey} ) { 64 my $StoredFilters = $JSONObject->Decode( 65 Data => $Preferences{$StoredFiltersKey}, 66 ); 67 $Self->{StoredFilters} = $StoredFilters; 68 } 69 70 # get the configured dyanmic fields from the Small Overview setting as a basis 71 my %DefaultDynamicFields = %{ $ConfigObject->Get("Ticket::Frontend::OverviewSmall")->{DynamicField} || {} }; 72 73 my %DefaultColumns = map { 'DynamicField_' . $_ => $DefaultDynamicFields{$_} } sort keys %DefaultDynamicFields; 74 75 # take general settings (Frontend::Agent) if not defined for the screen 76 $Self->{Config}->{DefaultColumns} //= $ConfigObject->Get('DefaultOverviewColumns'); 77 78 # check for default settings specific for this screen, should overide the dynamic fields 79 %DefaultColumns = ( %DefaultColumns, %{ $Self->{Config}->{DefaultColumns} || {} } ); 80 81 # configure columns 82 my @ColumnsAvailable = grep { $DefaultColumns{$_} ne '0' } sort keys %DefaultColumns; 83 my @ColumnsEnabled = grep { $DefaultColumns{$_} eq '2' } sort _DefaultColumnSort keys %DefaultColumns; 84 85 # if preference settings are available, take them 86 if ( $Preferences{ $Self->{PrefKeyColumns} } ) { 87 88 my $ColumnsEnabled = $JSONObject->Decode( 89 Data => $Preferences{ $Self->{PrefKeyColumns} }, 90 ); 91 92 # remove duplicate columns 93 my %UniqueColumns; 94 my @ColumnsEnabledAux; 95 96 for my $Column ( @{$ColumnsEnabled} ) { 97 if ( !$UniqueColumns{$Column} ) { 98 push @ColumnsEnabledAux, $Column; 99 } 100 $UniqueColumns{$Column} = 1; 101 } 102 103 # set filtered column list 104 @ColumnsEnabled = @ColumnsEnabledAux; 105 106 } 107 108 # always set TicketNumber 109 if ( !grep { $_ eq 'TicketNumber' } @ColumnsEnabled ) { 110 unshift @ColumnsEnabled, 'TicketNumber'; 111 } 112 113 $Self->{ColumnsEnabled} = \@ColumnsEnabled; 114 $Self->{ColumnsAvailable} = \@ColumnsAvailable; 115 116 { 117 118 # loop through all the dynamic fields to get the ones that should be shown 119 DYNAMICFIELDNAME: 120 for my $DynamicFieldName (@ColumnsEnabled) { 121 122 next DYNAMICFIELDNAME if $DynamicFieldName !~ m{ DynamicField_ }xms; 123 124 # remove dynamic field prefix 125 my $FieldName = $DynamicFieldName; 126 $FieldName =~ s/DynamicField_//gi; 127 $Self->{DynamicFieldFilter}->{$FieldName} = 1; 128 } 129 } 130 131 # get the dynamic fields for this screen 132 $Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 133 Valid => 1, 134 ObjectType => ['Ticket'], 135 FieldFilter => $Self->{DynamicFieldFilter} || {}, 136 ); 137 138 # hash with all valid sortable columns (taken from TicketSearch) 139 # SortBy => 'Age', # Created|Owner|Responsible|CustomerID|State|TicketNumber|Queue 140 # |Priority|Type|Lock|Title|Service|Changed|SLA|PendingTime|EscalationTime 141 # | EscalationUpdateTime|EscalationResponseTime|EscalationSolutionTime 142 $Self->{ValidSortableColumns} = { 143 'Age' => 1, 144 'Created' => 1, 145 'Owner' => 1, 146 'Responsible' => 1, 147 'CustomerID' => 1, 148 'State' => 1, 149 'TicketNumber' => 1, 150 'Queue' => 1, 151 'Priority' => 1, 152 'Type' => 1, 153 'Lock' => 1, 154 'Title' => 1, 155 'Service' => 1, 156 'Changed' => 1, 157 'SLA' => 1, 158 'PendingTime' => 1, 159 'EscalationTime' => 1, 160 'EscalationUpdateTime' => 1, 161 'EscalationResponseTime' => 1, 162 'EscalationSolutionTime' => 1, 163 }; 164 165 $Self->{AvailableFilterableColumns} = { 166 'Owner' => 1, 167 'Responsible' => 1, 168 'CustomerID' => 1, 169 'CustomerUserID' => 1, 170 'State' => 1, 171 'Queue' => 1, 172 'Priority' => 1, 173 'Type' => 1, 174 'Lock' => 1, 175 'Service' => 1, 176 'SLA' => 1, 177 }; 178 179 # remove queue from filters on AgentTicketQueue 180 if ( $Self->{Action} eq 'AgentTicketQueue' ) { 181 delete $Self->{AvailableFilterableColumns}->{Queue}; 182 } 183 184 # remove service from filters on AgentTicketService 185 if ( $Self->{Action} eq 'AgentTicketService' ) { 186 delete $Self->{AvailableFilterableColumns}->{Service}; 187 } 188 189 # get dynamic field backend object 190 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 191 192 # get filterable dynamic fields 193 # cycle trough the activated dynamic fields for this screen 194 DYNAMICFIELD: 195 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 196 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 197 198 my $IsFiltrable = $DynamicFieldBackendObject->HasBehavior( 199 DynamicFieldConfig => $DynamicFieldConfig, 200 Behavior => 'IsFiltrable', 201 ); 202 203 # if the dynamic field is filterable add it to the AvailableFilterableColumns hash 204 if ($IsFiltrable) { 205 $Self->{AvailableFilterableColumns}->{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = 1; 206 } 207 } 208 209 # get sortable dynamic fields 210 # cycle trough the activated dynamic fields for this screen 211 DYNAMICFIELD: 212 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 213 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 214 215 my $IsSortable = $DynamicFieldBackendObject->HasBehavior( 216 DynamicFieldConfig => $DynamicFieldConfig, 217 Behavior => 'IsSortable', 218 ); 219 220 # if the dynamic field is sortable add it to the ValidSortableColumns hash 221 if ($IsSortable) { 222 $Self->{ValidSortableColumns}->{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = 1; 223 } 224 } 225 226 return $Self; 227} 228 229sub ActionRow { 230 my ( $Self, %Param ) = @_; 231 232 # get needed objects 233 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 234 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 235 236 # check if bulk feature is enabled 237 my $BulkFeature = 0; 238 if ( $Param{Bulk} && $ConfigObject->Get('Ticket::Frontend::BulkFeature') ) { 239 my @Groups; 240 if ( $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') ) { 241 @Groups = @{ $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') }; 242 } 243 if ( !@Groups ) { 244 $BulkFeature = 1; 245 } 246 else { 247 my $GroupObject = $Kernel::OM->Get('Kernel::System::Group'); 248 GROUP: 249 for my $Group (@Groups) { 250 my $HasPermission = $GroupObject->PermissionCheck( 251 UserID => $Self->{UserID}, 252 GroupName => $Group, 253 Type => 'rw', 254 ); 255 if ($HasPermission) { 256 $BulkFeature = 1; 257 last GROUP; 258 } 259 } 260 } 261 } 262 263 $LayoutObject->Block( 264 Name => 'DocumentActionRow', 265 Data => \%Param, 266 ); 267 268 if ($BulkFeature) { 269 $LayoutObject->Block( 270 Name => 'DocumentActionRowBulk', 271 Data => { 272 %Param, 273 Name => Translatable('Bulk'), 274 }, 275 ); 276 } 277 278 # check if there was a column filter and no results, and print a link to back 279 if ( scalar @{ $Param{TicketIDs} } == 0 && $Param{LastColumnFilter} ) { 280 $LayoutObject->Block( 281 Name => 'DocumentActionRowLastColumnFilter', 282 Data => { 283 %Param, 284 }, 285 ); 286 } 287 288 my %ColumnTranslations; 289 my $LanguageObject = $Kernel::OM->Get('Kernel::Language'); 290 291 # add translations for the allocation lists for regular columns 292 my $Columns = $Self->{Config}->{DefaultColumns} || $ConfigObject->Get('DefaultOverviewColumns') || {}; 293 if ( $Columns && IsHashRefWithData($Columns) ) { 294 295 COLUMN: 296 for my $Column ( sort keys %{$Columns} ) { 297 298 # dynamic fields will be translated in the next block 299 next COLUMN if $Column =~ m{ \A DynamicField_ }xms; 300 301 my $TranslatedWord = $Column; 302 if ( $Column eq 'EscalationTime' ) { 303 $TranslatedWord = Translatable('Service Time'); 304 } 305 elsif ( $Column eq 'EscalationResponseTime' ) { 306 $TranslatedWord = Translatable('First Response Time'); 307 } 308 elsif ( $Column eq 'EscalationSolutionTime' ) { 309 $TranslatedWord = Translatable('Solution Time'); 310 } 311 elsif ( $Column eq 'EscalationUpdateTime' ) { 312 $TranslatedWord = Translatable('Update Time'); 313 } 314 elsif ( $Column eq 'PendingTime' ) { 315 $TranslatedWord = Translatable('Pending till'); 316 } 317 elsif ( $Column eq 'CustomerCompanyName' ) { 318 $TranslatedWord = Translatable('Customer Name'); 319 } 320 elsif ( $Column eq 'CustomerID' ) { 321 $TranslatedWord = Translatable('Customer ID'); 322 } 323 elsif ( $Column eq 'CustomerName' ) { 324 $TranslatedWord = Translatable('Customer User Name'); 325 } 326 elsif ( $Column eq 'CustomerUserID' ) { 327 $TranslatedWord = Translatable('Customer User ID'); 328 } 329 330 # send data to JS 331 $LayoutObject->AddJSData( 332 Key => 'Column' . $Column, 333 Value => $LanguageObject->Translate($TranslatedWord), 334 ); 335 } 336 } 337 338 # add translations for the allocation lists for dynamic field columns 339 my $ColumnsDynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet( 340 Valid => 0, 341 ObjectType => ['Ticket'], 342 ); 343 344 if ( $ColumnsDynamicField && IsArrayRefWithData($ColumnsDynamicField) ) { 345 346 my $Counter = 0; 347 348 DYNAMICFIELD: 349 for my $DynamicField ( sort @{$ColumnsDynamicField} ) { 350 351 next DYNAMICFIELD if !$DynamicField; 352 353 $Counter++; 354 355 # send data to JS 356 $LayoutObject->AddJSData( 357 Key => 'ColumnDynamicField_' . $DynamicField->{Name}, 358 Value => $LanguageObject->Translate( $DynamicField->{Label} ), 359 ); 360 } 361 } 362 363 my $Output = $LayoutObject->Output( 364 TemplateFile => 'AgentTicketOverviewSmall', 365 Data => \%Param, 366 ); 367 368 return $Output; 369} 370 371sub SortOrderBar { 372 my ( $Self, %Param ) = @_; 373 374 return ''; 375} 376 377sub Run { 378 my ( $Self, %Param ) = @_; 379 380 # If $Param{EnableColumnFilters} is not sent, we want to disable all filters 381 # for the current screen. We localize the setting for this sub and change it 382 # after that, if needed. The original value will be restored after this function. 383 local $Self->{AvailableFilterableColumns} = $Self->{AvailableFilterableColumns}; 384 if ( !$Param{EnableColumnFilters} ) { 385 $Self->{AvailableFilterableColumns} = {}; # disable all column filters 386 } 387 388 for my $Item (qw(TicketIDs PageShown StartHit)) { 389 if ( !$Param{$Item} ) { 390 $Kernel::OM->Get('Kernel::System::Log')->Log( 391 Priority => 'error', 392 Message => "Need $Item!", 393 ); 394 return; 395 } 396 } 397 398 # get needed objects 399 my $ConfigObject = $Kernel::OM->Get('Kernel::Config'); 400 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 401 402 # check if bulk feature is enabled 403 my $BulkFeature = 0; 404 if ( $Param{Bulk} && $ConfigObject->Get('Ticket::Frontend::BulkFeature') ) { 405 my @Groups; 406 if ( $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') ) { 407 @Groups = @{ $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') }; 408 } 409 if ( !@Groups ) { 410 $BulkFeature = 1; 411 } 412 else { 413 my $GroupObject = $Kernel::OM->Get('Kernel::System::Group'); 414 GROUP: 415 for my $Group (@Groups) { 416 my $HasPermission = $GroupObject->PermissionCheck( 417 UserID => $Self->{UserID}, 418 GroupName => $Group, 419 Type => 'rw', 420 ); 421 if ($HasPermission) { 422 $BulkFeature = 1; 423 last GROUP; 424 } 425 } 426 } 427 } 428 429 my $Counter = 0; 430 my @ArticleBox; 431 432 my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket'); 433 my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article'); 434 435 # Get extended ticket data if columns are used, save performance. See bug#14748. 436 my %ExtendedColumnsHash = ( 437 FirstResponse => 1, 438 FirstResponseInMin => 1, 439 FirstResponseDiffInMin => 1, 440 SolutionInMin => 1, 441 SolutionDiffInMin => 1, 442 FirstLock => 1, 443 ); 444 445 my $Extended = 0; 446 if ( grep { $ExtendedColumnsHash{$_} } @{ $Self->{ColumnsEnabled} } ) { 447 $Extended = 1; 448 } 449 450 for my $TicketID ( @{ $Param{TicketIDs} } ) { 451 $Counter++; 452 if ( $Counter >= $Param{StartHit} && $Counter < ( $Param{PageShown} + $Param{StartHit} ) ) { 453 454 # Get last customer article. 455 my @Articles = $ArticleObject->ArticleList( 456 TicketID => $TicketID, 457 SenderType => 'customer', 458 OnlyLast => 1, 459 ); 460 461 # If the ticket has no customer article, get the last agent article. 462 if ( !@Articles ) { 463 @Articles = $ArticleObject->ArticleList( 464 TicketID => $TicketID, 465 SenderType => 'agent', 466 OnlyLast => 1, 467 ); 468 } 469 470 # Finally, if everything failed, get latest article. 471 if ( !@Articles ) { 472 @Articles = $ArticleObject->ArticleList( 473 TicketID => $TicketID, 474 OnlyLast => 1, 475 ); 476 } 477 478 my %Article; 479 for my $Article (@Articles) { 480 %Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet( 481 %{$Article}, 482 DynamicFields => 0, 483 ); 484 } 485 486 # Get ticket data. 487 my %Ticket = $TicketObject->TicketGet( 488 TicketID => $TicketID, 489 Extended => $Extended, 490 DynamicFields => 0, 491 ); 492 493 %Article = ( %Article, %Ticket ); 494 495 # Get channel specific fields. 496 if ( $Article{ArticleID} ) { 497 my %ArticleFields = $LayoutObject->ArticleFields( 498 TicketID => $TicketID, 499 ArticleID => $Article{ArticleID}, 500 ); 501 FIELD: 502 for my $FieldKey (qw(Sender Subject)) { 503 next FIELD if !defined $ArticleFields{$FieldKey}->{Value}; 504 $Article{$FieldKey} = $ArticleFields{$FieldKey}->{Realname} // $ArticleFields{$FieldKey}->{Value}; 505 } 506 } 507 508 # Fallback for tickets without articles: get at least basic ticket data. 509 if ( !%Article ) { 510 %Article = %Ticket; 511 if ( !$Article{Title} ) { 512 $Article{Title} = $LayoutObject->{LanguageObject}->Translate( 513 'This ticket has no title or subject' 514 ); 515 } 516 $Article{Subject} = $Article{Title}; 517 } 518 519 # show ticket create time in small view 520 $Article{Created} = $Ticket{Created}; 521 522 # create human age 523 $Article{Age} = $LayoutObject->CustomerAge( 524 Age => $Article{Age}, 525 Space => ' ', 526 ); 527 528 # get ACL restrictions 529 my %PossibleActions; 530 my $Counter = 0; 531 532 # get all registered Actions 533 if ( ref $ConfigObject->Get('Frontend::Module') eq 'HASH' ) { 534 535 my %Actions = %{ $ConfigObject->Get('Frontend::Module') }; 536 537 # only use those Actions that stats with AgentTicket 538 %PossibleActions = map { ++$Counter => $_ } 539 grep { substr( $_, 0, length 'AgentTicket' ) eq 'AgentTicket' } 540 sort keys %Actions; 541 } 542 543 my $ACL = $TicketObject->TicketAcl( 544 Data => \%PossibleActions, 545 Action => $Self->{Action}, 546 TicketID => $Article{TicketID}, 547 ReturnType => 'Action', 548 ReturnSubType => '-', 549 UserID => $Self->{UserID}, 550 ); 551 my %AclAction = %PossibleActions; 552 if ($ACL) { 553 %AclAction = $TicketObject->TicketAclActionData(); 554 } 555 556 # run ticket pre menu modules 557 my @ActionItems; 558 if ( ref $ConfigObject->Get('Ticket::Frontend::PreMenuModule') eq 'HASH' ) { 559 my %Menus = %{ $ConfigObject->Get('Ticket::Frontend::PreMenuModule') }; 560 my @Items; 561 MENU: 562 for my $Menu ( sort keys %Menus ) { 563 564 # load module 565 if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Menus{$Menu}->{Module} ) ) { 566 return $LayoutObject->FatalError(); 567 } 568 my $Object = $Menus{$Menu}->{Module}->new( 569 %{$Self}, 570 TicketID => $Article{TicketID}, 571 ); 572 573 # run module 574 my $Item = $Object->Run( 575 %Param, 576 Ticket => \%Article, 577 ACL => \%AclAction, 578 Config => $Menus{$Menu}, 579 ); 580 next MENU if !$Item; 581 next MENU if ref $Item ne 'HASH'; 582 583 # add session id if needed 584 if ( !$LayoutObject->{SessionIDCookie} && $Item->{Link} ) { 585 $Item->{Link} 586 .= ';' 587 . $LayoutObject->{SessionName} . '=' 588 . $LayoutObject->{SessionID}; 589 } 590 591 # create id 592 $Item->{ID} = $Item->{Name}; 593 $Item->{ID} =~ s/(\s|&|;)//ig; 594 595 my $Output; 596 if ( $Item->{Block} ) { 597 $LayoutObject->Block( 598 Name => $Item->{Block}, 599 Data => $Item, 600 ); 601 $Output = $LayoutObject->Output( 602 TemplateFile => 'AgentTicketOverviewSmall', 603 Data => $Item, 604 ); 605 } 606 else { 607 $Output = '<li id="' 608 . $Item->{ID} 609 . '"><a href="#" title="' 610 . $LayoutObject->{LanguageObject}->Translate( $Item->{Description} ) 611 . '">' 612 . $LayoutObject->{LanguageObject}->Translate( $Item->{Name} ) 613 . '</a></li>'; 614 } 615 616 $Output =~ s/\n+//g; 617 $Output =~ s/\s+/ /g; 618 $Output =~ s/<\!--.+?-->//g; 619 620 push @ActionItems, { 621 HTML => $Output, 622 ID => $Item->{ID}, 623 Link => $LayoutObject->{Baselink} . $Item->{Link}, 624 Target => $Item->{Target}, 625 PopupType => $Item->{PopupType}, 626 Description => $Item->{Description}, 627 }; 628 $Article{ActionItems} = \@ActionItems; 629 } 630 } 631 push @ArticleBox, \%Article; 632 } 633 } 634 635 # check if sysconfig is a hash reference 636 if ( IsArrayRefWithData( $Self->{ColumnsEnabled} ) ) { 637 638 # check if column is really filterable 639 COLUMNNAME: 640 for my $ColumnName ( @{ $Self->{ColumnsEnabled} } ) { 641 next COLUMNNAME if !grep { $_ eq $ColumnName } @{ $Self->{ColumnsEnabled} }; 642 next COLUMNNAME if !$Self->{AvailableFilterableColumns}->{$ColumnName}; 643 $Self->{ValidFilterableColumns}->{$ColumnName} = 1; 644 } 645 } 646 647 my $ColumnValues = $Self->_GetColumnValues( 648 OriginalTicketIDs => $Param{OriginalTicketIDs}, 649 ); 650 651 # send data to JS 652 $LayoutObject->AddJSData( 653 Key => 'LinkPage', 654 Value => $Param{LinkPage}, 655 ); 656 657 $LayoutObject->Block( 658 Name => 'DocumentContent', 659 Data => \%Param, 660 ); 661 662 # array to save the column names to do the query 663 my @Col = @{ $Self->{ColumnsEnabled} }; 664 665 # define special ticket columns 666 my %SpecialColumns = ( 667 TicketNumber => 1, 668 Owner => 1, 669 Responsible => 1, 670 CustomerID => 1, 671 ); 672 673 # get dynamic field backend object 674 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 675 676 $Param{OrderBy} = $Param{OrderBy} || 'Up'; 677 678 my $TicketData = scalar @ArticleBox; 679 if ($TicketData) { 680 681 $LayoutObject->Block( Name => 'OverviewTable' ); 682 $LayoutObject->Block( Name => 'TableHeader' ); 683 684 if ($BulkFeature) { 685 $LayoutObject->Block( 686 Name => 'GeneralOverviewHeader', 687 ); 688 $LayoutObject->Block( 689 Name => 'BulkNavBar', 690 Data => \%Param, 691 ); 692 } 693 694 # meta items 695 my @TicketMetaItems = $LayoutObject->TicketMetaItemsCount(); 696 for my $Item (@TicketMetaItems) { 697 698 $LayoutObject->Block( 699 Name => 'GeneralOverviewHeader', 700 ); 701 702 my $CSS = ''; 703 my $OrderBy = $Param{OrderBy}; 704 my $Link; 705 my $Title = $LayoutObject->{LanguageObject}->Translate($Item); 706 707 if ( $Param{SortBy} && ( $Param{SortBy} eq $Item ) ) { 708 my $TitleDesc; 709 710 # Change order for sorting column. 711 $OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up'; 712 713 if ( $OrderBy eq 'Down' ) { 714 $CSS .= ' SortAscendingLarge'; 715 $TitleDesc = Translatable('sorted ascending'); 716 } 717 else { 718 $CSS .= ' SortDescendingLarge'; 719 $TitleDesc = Translatable('sorted descending'); 720 } 721 722 $TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc); 723 $Title .= ', ' . $TitleDesc; 724 } 725 726 $LayoutObject->Block( 727 Name => 'OverviewNavBarPageFlag', 728 Data => { 729 CSS => $CSS, 730 Title => $Title, 731 }, 732 ); 733 734 if ( $Item eq 'New Article' ) { 735 $LayoutObject->Block( 736 Name => 'OverviewNavBarPageFlagEmpty', 737 Data => { 738 Name => $Item, 739 } 740 ); 741 } 742 else { 743 $LayoutObject->Block( 744 Name => 'OverviewNavBarPageFlagLink', 745 Data => { 746 %Param, 747 Name => $Item, 748 CSS => $CSS, 749 OrderBy => $OrderBy, 750 Title => $Title, 751 }, 752 ); 753 } 754 755 } 756 757 my $CSS = ''; 758 759 # show special ticket columns, if needed 760 COLUMN: 761 for my $Column (@Col) { 762 763 $LayoutObject->Block( 764 Name => 'GeneralOverviewHeader', 765 ); 766 767 $CSS = $Column; 768 my $Title = $LayoutObject->{LanguageObject}->Translate($Column); 769 my $OrderBy = $Param{OrderBy}; 770 771 # output overall block so TicketNumber as well as other columns can be ordered 772 $LayoutObject->Block( 773 Name => 'OverviewNavBarPageTicketHeader', 774 Data => {}, 775 ); 776 777 if ( $SpecialColumns{$Column} ) { 778 779 if ( $Param{SortBy} && ( $Param{SortBy} eq $Column ) ) { 780 my $TitleDesc; 781 782 # Change order for sorting column. 783 $OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up'; 784 785 if ( $OrderBy eq 'Down' ) { 786 $CSS .= ' SortAscendingLarge'; 787 $TitleDesc = Translatable('sorted ascending'); 788 } 789 else { 790 $CSS .= ' SortDescendingLarge'; 791 $TitleDesc = Translatable('sorted descending'); 792 } 793 794 $TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc); 795 $Title .= ', ' . $TitleDesc; 796 } 797 798 # translate the column name to write it in the current language 799 my $TranslatedWord; 800 if ( $Column eq 'CustomerID' ) { 801 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer ID'); 802 } 803 else { 804 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate($Column); 805 } 806 807 my $FilterTitle = $TranslatedWord; 808 my $FilterTitleDesc = Translatable('filter not active'); 809 if ( 810 $Self->{StoredFilters} && 811 ( 812 $Self->{StoredFilters}->{$Column} || 813 $Self->{StoredFilters}->{ $Column . 'IDs' } 814 ) 815 ) 816 { 817 $CSS .= ' FilterActive'; 818 $FilterTitleDesc = Translatable('filter active'); 819 } 820 $FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc); 821 $FilterTitle .= ', ' . $FilterTitleDesc; 822 823 $LayoutObject->Block( 824 Name => 825 $Column eq 'TicketNumber' 826 ? 'OverviewNavBarPageTicketNumber' 827 : 'OverviewNavBarPageColumn', 828 Data => { 829 %Param, 830 OrderBy => $OrderBy, 831 ColumnName => $Column || '', 832 CSS => $CSS || '', 833 ColumnNameTranslated => $TranslatedWord || $Column, 834 Title => $Title, 835 }, 836 ); 837 838 # verify if column is filterable and sortable 839 if ( 840 $Self->{ValidFilterableColumns}->{$Column} 841 && $Self->{ValidSortableColumns}->{$Column} 842 ) 843 { 844 my $Css; 845 if ( $Column eq 'CustomerID' || $Column eq 'Owner' || $Column eq 'Responsible' ) { 846 $Css .= ' Hidden'; 847 } 848 849 # variable to save the filter's html code 850 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 851 ColumnName => $Column, 852 Label => $Column, 853 ColumnValues => $ColumnValues->{$Column}, 854 SelectedValue => $Param{GetColumnFilter}->{$Column} || '', 855 Css => $Css, 856 ); 857 858 $LayoutObject->Block( 859 Name => 'OverviewNavBarPageColumnFilterLink', 860 Data => { 861 %Param, 862 ColumnName => $Column, 863 CSS => $CSS, 864 ColumnNameTranslated => $TranslatedWord || $Column, 865 ColumnFilterStrg => $ColumnFilterHTML, 866 OrderBy => $OrderBy, 867 Title => $Title, 868 FilterTitle => $FilterTitle, 869 }, 870 ); 871 872 if ( $Column eq 'CustomerID' ) { 873 874 $LayoutObject->Block( 875 Name => 876 'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerIDSearch', 877 Data => {}, 878 ); 879 880 # send data to JS 881 $LayoutObject->AddJSData( 882 Key => 'CustomerIDAutocomplete', 883 Value => { 884 'QueryDelay' => 100, 885 'MaxResultsDisplayed' => 20, 886 'MinQueryLength' => 2, 887 }, 888 ); 889 } 890 elsif ( $Column eq 'Owner' || $Column eq 'Responsible' ) { 891 892 $LayoutObject->Block( 893 Name => 'ContentLargeTicketGenericHeaderColumnFilterLinkUserSearch', 894 Data => {}, 895 ); 896 897 # send data to JS 898 $LayoutObject->AddJSData( 899 Key => 'UserAutocomplete', 900 Value => { 901 'QueryDelay' => 100, 902 'MaxResultsDisplayed' => 20, 903 'MinQueryLength' => 2, 904 }, 905 ); 906 } 907 908 } 909 910 # verify if column is filterable 911 elsif ( $Self->{ValidFilterableColumns}->{$Column} ) { 912 913 # variable to save the filter's HTML code 914 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 915 ColumnName => $Column, 916 Label => $Column, 917 ColumnValues => $ColumnValues->{$Column}, 918 SelectedValue => $Param{GetColumnFilter}->{$Column} || '', 919 ); 920 921 $LayoutObject->Block( 922 Name => 'OverviewNavBarPageColumnFilter', 923 Data => { 924 %Param, 925 ColumnName => $Column, 926 CSS => $CSS, 927 ColumnNameTranslated => $TranslatedWord || $Column, 928 ColumnFilterStrg => $ColumnFilterHTML, 929 OrderBy => $OrderBy, 930 Title => $Title, 931 FilterTitle => $FilterTitle, 932 }, 933 ); 934 } 935 936 # verify if column is sortable 937 elsif ( $Self->{ValidSortableColumns}->{$Column} ) { 938 $LayoutObject->Block( 939 Name => 'OverviewNavBarPageColumnLink', 940 Data => { 941 %Param, 942 ColumnName => $Column, 943 CSS => $CSS, 944 ColumnNameTranslated => $TranslatedWord || $Column, 945 OrderBy => $OrderBy, 946 Title => $Title, 947 }, 948 ); 949 } 950 else { 951 $LayoutObject->Block( 952 Name => 'OverviewNavBarPageColumnEmpty', 953 Data => { 954 %Param, 955 ColumnName => $Column, 956 CSS => $CSS, 957 ColumnNameTranslated => $TranslatedWord || $Column, 958 Title => $Title, 959 }, 960 ); 961 } 962 next COLUMN; 963 } 964 elsif ( $Column !~ m{\A DynamicField_}xms ) { 965 966 if ( $Param{SortBy} && ( $Param{SortBy} eq $Column ) ) { 967 my $TitleDesc; 968 969 # Change order for sorting column. 970 $OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up'; 971 972 if ( $OrderBy eq 'Down' ) { 973 $CSS .= ' SortAscendingLarge'; 974 $TitleDesc = Translatable('sorted ascending'); 975 } 976 else { 977 $CSS .= ' SortDescendingLarge'; 978 $TitleDesc = Translatable('sorted descending'); 979 } 980 981 $TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc); 982 $Title .= ', ' . $TitleDesc; 983 } 984 985 # translate the column name to write it in the current language 986 my $TranslatedWord; 987 if ( $Column eq 'EscalationTime' ) { 988 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Service Time'); 989 } 990 elsif ( $Column eq 'EscalationResponseTime' ) { 991 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('First Response Time'); 992 } 993 elsif ( $Column eq 'EscalationSolutionTime' ) { 994 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Solution Time'); 995 } 996 elsif ( $Column eq 'EscalationUpdateTime' ) { 997 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Update Time'); 998 } 999 elsif ( $Column eq 'PendingTime' ) { 1000 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Pending till'); 1001 } 1002 elsif ( $Column eq 'CustomerCompanyName' ) { 1003 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer Name'); 1004 } 1005 elsif ( $Column eq 'CustomerName' ) { 1006 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User Name'); 1007 } 1008 elsif ( $Column eq 'CustomerUserID' ) { 1009 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User ID'); 1010 } 1011 else { 1012 $TranslatedWord = $LayoutObject->{LanguageObject}->Translate($Column); 1013 } 1014 1015 my $FilterTitle = $TranslatedWord; 1016 my $FilterTitleDesc = Translatable('filter not active'); 1017 if ( $Self->{StoredFilters} && $Self->{StoredFilters}->{ $Column . 'IDs' } ) { 1018 $CSS .= ' FilterActive'; 1019 $FilterTitleDesc = Translatable('filter active'); 1020 } 1021 $FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc); 1022 $FilterTitle .= ', ' . $FilterTitleDesc; 1023 1024 $LayoutObject->Block( 1025 Name => 'OverviewNavBarPageColumn', 1026 Data => { 1027 %Param, 1028 ColumnName => $Column, 1029 CSS => $CSS, 1030 ColumnNameTranslated => $TranslatedWord || $Column, 1031 }, 1032 ); 1033 1034 # verify if column is filterable and sortable 1035 if ( 1036 $Self->{ValidFilterableColumns}->{$Column} 1037 && $Self->{ValidSortableColumns}->{$Column} 1038 ) 1039 { 1040 1041 # variable to save the filter's HTML code 1042 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1043 ColumnName => $Column, 1044 Label => $Column, 1045 ColumnValues => $ColumnValues->{$Column}, 1046 SelectedValue => $Param{GetColumnFilter}->{$Column} || '', 1047 ); 1048 1049 $LayoutObject->Block( 1050 Name => 'OverviewNavBarPageColumnFilterLink', 1051 Data => { 1052 %Param, 1053 ColumnName => $Column, 1054 CSS => $CSS, 1055 ColumnNameTranslated => $TranslatedWord || $Column, 1056 ColumnFilterStrg => $ColumnFilterHTML, 1057 OrderBy => $OrderBy, 1058 Title => $Title, 1059 FilterTitle => $FilterTitle, 1060 }, 1061 ); 1062 } 1063 1064 # verify if column is just filterable 1065 elsif ( $Self->{ValidFilterableColumns}->{$Column} ) { 1066 1067 my $Css; 1068 if ( $Column eq 'CustomerUserID' ) { 1069 $Css = 'Hidden'; 1070 } 1071 1072 # variable to save the filter's HTML code 1073 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1074 ColumnName => $Column, 1075 Label => $Column, 1076 ColumnValues => $ColumnValues->{$Column}, 1077 SelectedValue => $Param{GetColumnFilter}->{$Column} || '', 1078 Css => $Css, 1079 ); 1080 $LayoutObject->Block( 1081 Name => 'OverviewNavBarPageColumnFilter', 1082 Data => { 1083 %Param, 1084 ColumnName => $Column, 1085 CSS => $CSS, 1086 ColumnNameTranslated => $TranslatedWord || $Column, 1087 ColumnFilterStrg => $ColumnFilterHTML, 1088 OrderBy => $OrderBy, 1089 Title => $Title, 1090 FilterTitle => $FilterTitle, 1091 }, 1092 ); 1093 if ( $Column eq 'CustomerUserID' ) { 1094 1095 $LayoutObject->Block( 1096 Name => 1097 'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerUserSearch', 1098 Data => {}, 1099 ); 1100 1101 # send data to JS 1102 $LayoutObject->AddJSData( 1103 Key => 'CustomerUserAutocomplete', 1104 Value => { 1105 'QueryDelay' => 100, 1106 'MaxResultsDisplayed' => 20, 1107 'MinQueryLength' => 2, 1108 }, 1109 ); 1110 } 1111 } 1112 1113 # verify if column is sortable 1114 elsif ( $Self->{ValidSortableColumns}->{$Column} ) { 1115 $LayoutObject->Block( 1116 Name => 'OverviewNavBarPageColumnLink', 1117 Data => { 1118 %Param, 1119 ColumnName => $Column, 1120 CSS => $CSS, 1121 ColumnNameTranslated => $TranslatedWord || $Column, 1122 OrderBy => $OrderBy, 1123 Title => $Title, 1124 }, 1125 ); 1126 } 1127 else { 1128 $LayoutObject->Block( 1129 Name => 'OverviewNavBarPageColumnEmpty', 1130 Data => { 1131 %Param, 1132 ColumnName => $Column, 1133 CSS => $CSS, 1134 ColumnNameTranslated => $TranslatedWord || $Column, 1135 Title => $Title, 1136 }, 1137 ); 1138 } 1139 } 1140 1141 # show the DFs 1142 else { 1143 1144 my $DynamicFieldConfig; 1145 my $DFColumn = $Column; 1146 $DFColumn =~ s/DynamicField_//g; 1147 DYNAMICFIELD: 1148 for my $DFConfig ( @{ $Self->{DynamicField} } ) { 1149 next DYNAMICFIELD if !IsHashRefWithData($DFConfig); 1150 next DYNAMICFIELD if $DFConfig->{Name} ne $DFColumn; 1151 1152 $DynamicFieldConfig = $DFConfig; 1153 last DYNAMICFIELD; 1154 } 1155 next COLUMN if !IsHashRefWithData($DynamicFieldConfig); 1156 1157 my $Label = $DynamicFieldConfig->{Label}; 1158 $Title = $Label; 1159 my $FilterTitle = $Label; 1160 1161 # get field sortable condition 1162 my $IsSortable = $DynamicFieldBackendObject->HasBehavior( 1163 DynamicFieldConfig => $DynamicFieldConfig, 1164 Behavior => 'IsSortable', 1165 ); 1166 1167 if ($IsSortable) { 1168 my $CSS = 'DynamicField_' . $DynamicFieldConfig->{Name}; 1169 if ( 1170 $Param{SortBy} 1171 && ( $Param{SortBy} eq ( 'DynamicField_' . $DynamicFieldConfig->{Name} ) ) 1172 ) 1173 { 1174 my $TitleDesc; 1175 1176 # Change order for sorting column. 1177 $OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up'; 1178 1179 if ( $OrderBy eq 'Down' ) { 1180 $CSS .= ' SortAscendingLarge'; 1181 $TitleDesc = Translatable('sorted ascending'); 1182 } 1183 else { 1184 $CSS .= ' SortDescendingLarge'; 1185 $TitleDesc = Translatable('sorted descending'); 1186 } 1187 1188 $TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc); 1189 $Title .= ', ' . $TitleDesc; 1190 } 1191 1192 my $FilterTitleDesc = Translatable('filter not active'); 1193 if ( $Self->{StoredFilters} && $Self->{StoredFilters}->{$Column} ) { 1194 $CSS .= ' FilterActive'; 1195 $FilterTitleDesc = Translatable('filter active'); 1196 } 1197 $FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc); 1198 $FilterTitle .= ', ' . $FilterTitleDesc; 1199 1200 $LayoutObject->Block( 1201 Name => 'OverviewNavBarPageDynamicField', 1202 Data => { 1203 %Param, 1204 CSS => $CSS, 1205 }, 1206 ); 1207 1208 my $DynamicFieldName = 'DynamicField_' . $DynamicFieldConfig->{Name}; 1209 1210 if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) { 1211 1212 # variable to save the filter's HTML code 1213 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1214 ColumnName => $DynamicFieldName, 1215 Label => $Label, 1216 ColumnValues => $ColumnValues->{$DynamicFieldName}, 1217 SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '', 1218 ); 1219 1220 $LayoutObject->Block( 1221 Name => 'OverviewNavBarPageDynamicFieldFiltrableSortable', 1222 Data => { 1223 %Param, 1224 OrderBy => $OrderBy, 1225 Label => $Label, 1226 DynamicFieldName => $DynamicFieldConfig->{Name}, 1227 ColumnFilterStrg => $ColumnFilterHTML, 1228 Title => $Title, 1229 FilterTitle => $FilterTitle, 1230 }, 1231 ); 1232 } 1233 1234 else { 1235 $LayoutObject->Block( 1236 Name => 'OverviewNavBarPageDynamicFieldSortable', 1237 Data => { 1238 %Param, 1239 OrderBy => $OrderBy, 1240 Label => $Label, 1241 DynamicFieldName => $DynamicFieldConfig->{Name}, 1242 Title => $Title, 1243 }, 1244 ); 1245 } 1246 1247 # example of dynamic fields order customization 1248 $LayoutObject->Block( 1249 Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name}, 1250 Data => { 1251 %Param, 1252 CSS => $CSS, 1253 }, 1254 ); 1255 1256 if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) { 1257 1258 # variable to save the filter's HTML code 1259 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1260 ColumnName => $DynamicFieldName, 1261 Label => $Label, 1262 ColumnValues => $ColumnValues->{$DynamicFieldName}, 1263 SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '', 1264 ); 1265 1266 $LayoutObject->Block( 1267 Name => 'OverviewNavBarPageDynamicField' 1268 . $DynamicFieldConfig->{Name} 1269 . '_FiltrableSortable', 1270 Data => { 1271 %Param, 1272 OrderBy => $OrderBy, 1273 Label => $Label, 1274 DynamicFieldName => $DynamicFieldConfig->{Name}, 1275 ColumnFilterStrg => $ColumnFilterHTML, 1276 Title => $Title, 1277 }, 1278 ); 1279 } 1280 else { 1281 $LayoutObject->Block( 1282 Name => 'OverviewNavBarPageDynamicField_' 1283 . $DynamicFieldConfig->{Name} 1284 . '_Sortable', 1285 Data => { 1286 %Param, 1287 OrderBy => $OrderBy, 1288 Label => $Label, 1289 DynamicFieldName => $DynamicFieldConfig->{Name}, 1290 Title => $Title, 1291 }, 1292 ); 1293 } 1294 } 1295 else { 1296 1297 my $DynamicFieldName = 'DynamicField_' . $DynamicFieldConfig->{Name}; 1298 my $CSS = $DynamicFieldName; 1299 1300 $LayoutObject->Block( 1301 Name => 'OverviewNavBarPageDynamicField', 1302 Data => { 1303 %Param, 1304 CSS => $CSS, 1305 }, 1306 ); 1307 1308 if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) { 1309 1310 # variable to save the filter's HTML code 1311 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1312 ColumnName => $DynamicFieldName, 1313 Label => $Label, 1314 ColumnValues => $ColumnValues->{$DynamicFieldName}, 1315 SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '', 1316 ); 1317 1318 $LayoutObject->Block( 1319 Name => 'OverviewNavBarPageDynamicFieldFiltrableNotSortable', 1320 Data => { 1321 %Param, 1322 Label => $Label, 1323 DynamicFieldName => $DynamicFieldConfig->{Name}, 1324 ColumnFilterStrg => $ColumnFilterHTML, 1325 Title => $Title, 1326 FilterTitle => $FilterTitle, 1327 }, 1328 ); 1329 } 1330 else { 1331 $LayoutObject->Block( 1332 Name => 'OverviewNavBarPageDynamicFieldNotSortable', 1333 Data => { 1334 %Param, 1335 Label => $Label, 1336 Title => $Title, 1337 }, 1338 ); 1339 } 1340 1341 # example of dynamic fields order customization 1342 $LayoutObject->Block( 1343 Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name}, 1344 Data => { 1345 %Param, 1346 }, 1347 ); 1348 1349 if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) { 1350 1351 # variable to save the filter's HTML code 1352 my $ColumnFilterHTML = $Self->_InitialColumnFilter( 1353 ColumnName => $DynamicFieldName, 1354 Label => $Label, 1355 ColumnValues => $ColumnValues->{$DynamicFieldName}, 1356 SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '', 1357 ); 1358 1359 $LayoutObject->Block( 1360 Name => 'OverviewNavBarPageDynamicField_' 1361 . $DynamicFieldConfig->{Name} 1362 . '_FiltrableNotSortable', 1363 Data => { 1364 %Param, 1365 Label => $Label, 1366 DynamicFieldName => $DynamicFieldConfig->{Name}, 1367 ColumnFilterStrg => $ColumnFilterHTML, 1368 Title => $Title, 1369 }, 1370 ); 1371 } 1372 else { 1373 $LayoutObject->Block( 1374 Name => 'OverviewNavBarPageDynamicField_' 1375 . $DynamicFieldConfig->{Name} 1376 . '_NotSortable', 1377 Data => { 1378 %Param, 1379 Label => $Label, 1380 Title => $Title, 1381 }, 1382 ); 1383 } 1384 } 1385 1386 } 1387 1388 } 1389 1390 $LayoutObject->Block( Name => 'TableBody' ); 1391 1392 } 1393 else { 1394 $LayoutObject->Block( Name => 'NoTicketFound' ); 1395 } 1396 1397 my %ActionRowTickets; 1398 1399 for my $ArticleRef (@ArticleBox) { 1400 1401 # get last customer article 1402 my %Article = %{$ArticleRef}; 1403 1404 # escalation human times 1405 if ( $Article{EscalationTime} ) { 1406 $Article{EscalationTimeHuman} = $LayoutObject->CustomerAge( 1407 Age => $Article{EscalationTime}, 1408 TimeShowAlwaysLong => 1, 1409 Space => ' ', 1410 ); 1411 $Article{EscalationTimeWorkingTime} = $LayoutObject->CustomerAge( 1412 Age => $Article{EscalationTimeWorkingTime}, 1413 TimeShowAlwaysLong => 1, 1414 Space => ' ', 1415 ); 1416 } 1417 1418 # customer info 1419 my %CustomerInfo; 1420 if ( $Param{Config}->{CustomerInfo} ) { 1421 if ( $Article{CustomerUserID} ) { 1422 1423 # get customer user object 1424 my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser'); 1425 1426 $Article{CustomerName} = $CustomerUserObject->CustomerName( 1427 UserLogin => $Article{CustomerUserID}, 1428 ); 1429 1430 %CustomerInfo = $CustomerUserObject->CustomerUserDataGet( 1431 User => $Article{CustomerUserID}, 1432 ); 1433 1434 INFOKEY: 1435 for my $InfoKey ( sort keys %CustomerInfo ) { 1436 next INFOKEY if $InfoKey =~ m{\ACustomer}xms; 1437 1438 $CustomerInfo{ 'Customer' . $InfoKey } = $CustomerInfo{$InfoKey}; 1439 } 1440 } 1441 } 1442 1443 # get user object 1444 my $UserObject = $Kernel::OM->Get('Kernel::System::User'); 1445 1446 # user info 1447 my %UserInfo = $UserObject->GetUserData( 1448 UserID => $Article{OwnerID}, 1449 ); 1450 1451 # Responsible info. 1452 my %ResponsibleInfo = $UserObject->GetUserData( 1453 UserID => $Article{ResponsibleID}, 1454 ); 1455 $UserInfo{ResponsibleInfo} = \%ResponsibleInfo; 1456 1457 $LayoutObject->Block( 1458 Name => 'Record', 1459 Data => { %Article, %UserInfo }, 1460 ); 1461 1462 # check if bulk feature is enabled 1463 if ($BulkFeature) { 1464 $LayoutObject->Block( 1465 Name => 'GeneralOverviewRow', 1466 ); 1467 $LayoutObject->Block( 1468 Name => Translatable('Bulk'), 1469 Data => { %Article, %UserInfo }, 1470 ); 1471 } 1472 1473 # show ticket flags 1474 my @TicketMetaItems = $LayoutObject->TicketMetaItems( 1475 Ticket => \%Article, 1476 ); 1477 for my $Item (@TicketMetaItems) { 1478 $LayoutObject->Block( 1479 Name => 'GeneralOverviewRow', 1480 ); 1481 $LayoutObject->Block( 1482 Name => 'ContentLargeTicketGenericRowMeta', 1483 Data => $Item, 1484 ); 1485 if ($Item) { 1486 $LayoutObject->Block( 1487 Name => 'ContentLargeTicketGenericRowMetaImage', 1488 Data => $Item, 1489 ); 1490 } 1491 } 1492 1493 # save column content 1494 my $DataValue; 1495 1496 # show all needed columns 1497 TICKETCOLUMN: 1498 for my $TicketColumn (@Col) { 1499 $LayoutObject->Block( 1500 Name => 'GeneralOverviewRow', 1501 ); 1502 if ( $TicketColumn !~ m{\A DynamicField_}xms ) { 1503 $LayoutObject->Block( 1504 Name => 'RecordTicketData', 1505 Data => {}, 1506 ); 1507 1508 if ( $SpecialColumns{$TicketColumn} ) { 1509 $LayoutObject->Block( 1510 Name => 'Record' . $TicketColumn, 1511 Data => { %Article, %UserInfo }, 1512 ); 1513 1514 next TICKETCOLUMN; 1515 } 1516 1517 if ( $TicketColumn eq 'CreatedBy' ) { 1518 1519 my %TicketCreatedByInfo = $UserObject->GetUserData( 1520 UserID => $Article{CreateBy}, 1521 ); 1522 1523 $LayoutObject->Block( 1524 Name => 'RecordTicketCreatedBy', 1525 Data => \%TicketCreatedByInfo, 1526 ); 1527 next TICKETCOLUMN; 1528 } 1529 1530 # escalation column 1531 my %EscalationData; 1532 if ( $TicketColumn eq 'EscalationTime' ) { 1533 $EscalationData{EscalationTime} = $Article{EscalationTime}; 1534 $EscalationData{EscalationDestinationDate} = $Article{EscalationDestinationDate}; 1535 1536 $EscalationData{EscalationTimeHuman} = $LayoutObject->CustomerAge( 1537 Age => $EscalationData{EscalationTime}, 1538 TimeShowAlwaysLong => 1, 1539 Space => ' ', 1540 ) || '-'; 1541 $EscalationData{EscalationTimeWorkingTime} = $LayoutObject->CustomerAge( 1542 Age => $EscalationData{EscalationTimeWorkingTime}, 1543 TimeShowAlwaysLong => 1, 1544 Space => ' ', 1545 ); 1546 if ( 1547 defined $Article{EscalationTime} 1548 && $Article{EscalationTime} < 60 * 60 * 1 1549 ) 1550 { 1551 $EscalationData{EscalationClass} = 'Warning'; 1552 } 1553 $LayoutObject->Block( 1554 Name => 'RecordEscalationTime', 1555 Data => {%EscalationData}, 1556 ); 1557 next TICKETCOLUMN; 1558 } 1559 1560 my $BlockType = ''; 1561 my $CSSClass = ''; 1562 if ( $TicketColumn eq 'EscalationSolutionTime' ) { 1563 $BlockType = 'Escalation'; 1564 $DataValue = $LayoutObject->CustomerAge( 1565 Age => $Article{SolutionTime} || 0, 1566 TimeShowAlwaysLong => 1, 1567 Space => ' ', 1568 ); 1569 if ( defined $Article{SolutionTime} && $Article{SolutionTime} < 60 * 60 * 1 ) { 1570 $CSSClass = 'Warning'; 1571 } 1572 } 1573 elsif ( $TicketColumn eq 'EscalationResponseTime' ) { 1574 $BlockType = 'Escalation'; 1575 $DataValue = $LayoutObject->CustomerAge( 1576 Age => $Article{FirstResponseTime} || 0, 1577 TimeShowAlwaysLong => 1, 1578 Space => ' ', 1579 ); 1580 if ( 1581 defined $Article{FirstResponseTime} 1582 && $Article{FirstResponseTime} < 60 * 60 * 1 1583 ) 1584 { 1585 $CSSClass = 'Warning'; 1586 } 1587 } 1588 elsif ( $TicketColumn eq 'EscalationUpdateTime' ) { 1589 $BlockType = 'Escalation'; 1590 $DataValue = $LayoutObject->CustomerAge( 1591 Age => $Article{UpdateTime} || 0, 1592 TimeShowAlwaysLong => 1, 1593 Space => ' ', 1594 ); 1595 if ( defined $Article{UpdateTime} && $Article{UpdateTime} < 60 * 60 * 1 ) { 1596 $CSSClass = 'Warning'; 1597 } 1598 } 1599 elsif ( $TicketColumn eq 'PendingTime' ) { 1600 $BlockType = 'Escalation'; 1601 $DataValue = $LayoutObject->CustomerAge( 1602 Age => $Article{'UntilTime'}, 1603 Space => ' ' 1604 ); 1605 if ( defined $Article{UntilTime} && $Article{UntilTime} < -1 ) { 1606 $CSSClass = 'Warning'; 1607 } 1608 } 1609 elsif ( 1610 $TicketColumn eq 'State' 1611 || $TicketColumn eq 'Lock' 1612 || $TicketColumn eq 'Priority' 1613 ) 1614 { 1615 $BlockType = 'Translatable'; 1616 $DataValue = $Article{$TicketColumn} || $UserInfo{$TicketColumn}; 1617 } 1618 elsif ( $TicketColumn eq 'Created' || $TicketColumn eq 'Changed' ) { 1619 $BlockType = 'Time'; 1620 $DataValue = $Article{$TicketColumn} || $UserInfo{$TicketColumn}; 1621 } 1622 elsif ( $TicketColumn eq 'Responsible' ) { 1623 1624 my %ResponsibleInfo = $UserObject->GetUserData( 1625 UserID => $Article{ResponsibleID}, 1626 ); 1627 1628 $DataValue = $ResponsibleInfo{'UserFullname'}; 1629 } 1630 else { 1631 $DataValue = $Article{$TicketColumn} 1632 || $UserInfo{$TicketColumn} 1633 || $CustomerInfo{$TicketColumn}; 1634 1635 # If value is in date format, change block type to 'Time' so it can be localized. See bug#14542. 1636 if ( 1637 defined $DataValue 1638 && $DataValue =~ /^\d\d\d\d-(\d|\d\d)-(\d|\d\d)\s(\d|\d\d):(\d|\d\d):(\d|\d\d)/ 1639 ) 1640 { 1641 $BlockType = 'Time'; 1642 } 1643 } 1644 1645 $LayoutObject->Block( 1646 Name => "RecordTicketColumn$BlockType", 1647 Data => { 1648 GenericValue => $DataValue || '-', 1649 Class => $CSSClass || '', 1650 }, 1651 ); 1652 } 1653 1654 # dynamic fields 1655 else { 1656 1657 # cycle trough the activated dynamic fields for this screen 1658 1659 my $DynamicFieldConfig; 1660 my $DFColumn = $TicketColumn; 1661 $DFColumn =~ s/DynamicField_//g; 1662 DYNAMICFIELD: 1663 for my $DFConfig ( @{ $Self->{DynamicField} } ) { 1664 next DYNAMICFIELD if !IsHashRefWithData($DFConfig); 1665 next DYNAMICFIELD if $DFConfig->{Name} ne $DFColumn; 1666 1667 $DynamicFieldConfig = $DFConfig; 1668 last DYNAMICFIELD; 1669 } 1670 next TICKETCOLUMN if !IsHashRefWithData($DynamicFieldConfig); 1671 1672 # get field value 1673 my $Value = $DynamicFieldBackendObject->ValueGet( 1674 DynamicFieldConfig => $DynamicFieldConfig, 1675 ObjectID => $Article{TicketID}, 1676 ); 1677 1678 my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender( 1679 DynamicFieldConfig => $DynamicFieldConfig, 1680 Value => $Value, 1681 ValueMaxChars => 20, 1682 LayoutObject => $LayoutObject, 1683 ); 1684 1685 $LayoutObject->Block( 1686 Name => 'RecordDynamicField', 1687 Data => { 1688 Value => $ValueStrg->{Value}, 1689 Title => $ValueStrg->{Title}, 1690 }, 1691 ); 1692 1693 if ( $ValueStrg->{Link} ) { 1694 $LayoutObject->Block( 1695 Name => 'RecordDynamicFieldLink', 1696 Data => { 1697 Value => $ValueStrg->{Value}, 1698 Title => $ValueStrg->{Title}, 1699 Link => $ValueStrg->{Link}, 1700 $DynamicFieldConfig->{Name} => $ValueStrg->{Title}, 1701 }, 1702 ); 1703 } 1704 else { 1705 $LayoutObject->Block( 1706 Name => 'RecordDynamicFieldPlain', 1707 Data => { 1708 Value => $ValueStrg->{Value}, 1709 Title => $ValueStrg->{Title}, 1710 }, 1711 ); 1712 } 1713 1714 # example of dynamic fields order customization 1715 $LayoutObject->Block( 1716 Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name}, 1717 Data => { 1718 Value => $ValueStrg->{Value}, 1719 Title => $ValueStrg->{Title}, 1720 }, 1721 ); 1722 1723 if ( $ValueStrg->{Link} ) { 1724 $LayoutObject->Block( 1725 Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name} . '_Link', 1726 Data => { 1727 Value => $ValueStrg->{Value}, 1728 Title => $ValueStrg->{Title}, 1729 Link => $ValueStrg->{Link}, 1730 $DynamicFieldConfig->{Name} => $ValueStrg->{Title}, 1731 }, 1732 ); 1733 } 1734 else { 1735 $LayoutObject->Block( 1736 Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name} . '_Plain', 1737 Data => { 1738 Value => $ValueStrg->{Value}, 1739 Title => $ValueStrg->{Title}, 1740 }, 1741 ); 1742 } 1743 } 1744 } 1745 1746 # add action items as js 1747 if ( $Article{ActionItems} ) { 1748 1749 # replace TT directives from string with values 1750 for my $ActionItem ( @{ $Article{ActionItems} } ) { 1751 $ActionItem->{Link} = $LayoutObject->Output( 1752 Template => $ActionItem->{Link}, 1753 Data => { 1754 TicketID => $Article{TicketID}, 1755 }, 1756 ); 1757 } 1758 1759 $ActionRowTickets{ $Article{TicketID} } = $LayoutObject->JSONEncode( Data => $Article{ActionItems} ); 1760 } 1761 } 1762 1763 # send data to JS 1764 $LayoutObject->AddJSData( 1765 Key => 'ActionRowTickets', 1766 Value => \%ActionRowTickets, 1767 ); 1768 1769 # set column filter form, to correctly fill the column filters is necessary to pass each 1770 # overview some information in the AJAX call, for example the fixed Filters or NavBarFilters 1771 # and also other values like the Queue in AgentTicketQueue, otherwise the filters will be 1772 # filled with default restrictions, resulting in more options than the ones that the 1773 # available tickets should provide, see Bug#9902 1774 if ( IsHashRefWithData( $Param{ColumnFilterForm} ) ) { 1775 $LayoutObject->Block( 1776 Name => 'DocumentColumnFilterForm', 1777 Data => {}, 1778 ); 1779 1780 # Add UseSubQueues param if available. 1781 if ( $Param{UseSubQueues} ) { 1782 $Param{ColumnFilterForm}->{UseSubQueues} = $Param{UseSubQueues}; 1783 } 1784 1785 for my $Element ( sort keys %{ $Param{ColumnFilterForm} } ) { 1786 $LayoutObject->Block( 1787 Name => 'DocumentColumnFilterFormElement', 1788 Data => { 1789 ElementName => $Element, 1790 ElementValue => $Param{ColumnFilterForm}->{$Element}, 1791 }, 1792 ); 1793 } 1794 } 1795 1796 # use template 1797 my $Output = $LayoutObject->Output( 1798 TemplateFile => 'AgentTicketOverviewSmall', 1799 Data => { 1800 %Param, 1801 Type => $Self->{ViewType}, 1802 }, 1803 ); 1804 1805 return $Output; 1806} 1807 1808sub _GetColumnValues { 1809 my ( $Self, %Param ) = @_; 1810 1811 return if !IsStringWithData( $Param{HeaderColumn} ); 1812 1813 my $HeaderColumn = $Param{HeaderColumn}; 1814 my %ColumnFilterValues; 1815 my $TicketIDs; 1816 1817 if ( IsArrayRefWithData( $Param{OriginalTicketIDs} ) ) { 1818 $TicketIDs = $Param{OriginalTicketIDs}; 1819 } 1820 1821 if ( $HeaderColumn !~ m/^DynamicField_/ ) { 1822 my $FunctionName = $HeaderColumn . 'FilterValuesGet'; 1823 if ( $HeaderColumn eq 'CustomerID' ) { 1824 $FunctionName = 'CustomerFilterValuesGet'; 1825 } 1826 $ColumnFilterValues{$HeaderColumn} = $Kernel::OM->Get('Kernel::System::Ticket::ColumnFilter')->$FunctionName( 1827 TicketIDs => $TicketIDs, 1828 HeaderColumn => $HeaderColumn, 1829 UserID => $Self->{UserID}, 1830 ); 1831 } 1832 else { 1833 DYNAMICFIELD: 1834 for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) { 1835 next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig); 1836 my $FieldName = 'DynamicField_' . $DynamicFieldConfig->{Name}; 1837 next DYNAMICFIELD if $FieldName ne $HeaderColumn; 1838 1839 # get dynamic field backend object 1840 my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend'); 1841 my $IsFiltrable = $DynamicFieldBackendObject->HasBehavior( 1842 DynamicFieldConfig => $DynamicFieldConfig, 1843 Behavior => 'IsFiltrable', 1844 ); 1845 next DYNAMICFIELD if !$IsFiltrable; 1846 $Self->{ValidFilterableColumns}->{$HeaderColumn} = $IsFiltrable; 1847 if ( IsArrayRefWithData($TicketIDs) ) { 1848 1849 # get the historical values for the field 1850 $ColumnFilterValues{$HeaderColumn} = $DynamicFieldBackendObject->ColumnFilterValuesGet( 1851 DynamicFieldConfig => $DynamicFieldConfig, 1852 LayoutObject => $Kernel::OM->Get('Kernel::Output::HTML::Layout'), 1853 TicketIDs => $TicketIDs, 1854 ); 1855 } 1856 else { 1857 1858 # get PossibleValues 1859 $ColumnFilterValues{$HeaderColumn} = $DynamicFieldBackendObject->PossibleValuesGet( 1860 DynamicFieldConfig => $DynamicFieldConfig, 1861 ); 1862 } 1863 last DYNAMICFIELD; 1864 } 1865 } 1866 1867 return \%ColumnFilterValues; 1868} 1869 1870sub _InitialColumnFilter { 1871 my ( $Self, %Param ) = @_; 1872 1873 return if !$Param{ColumnName}; 1874 return if !$Self->{ValidFilterableColumns}->{ $Param{ColumnName} }; 1875 1876 # get layout object 1877 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 1878 1879 my $Label = $Param{Label} || $Param{ColumnName}; 1880 $Label = $LayoutObject->{LanguageObject}->Translate($Label); 1881 1882 # set fixed values 1883 my $Data = [ 1884 { 1885 Key => '', 1886 Value => uc $Label, 1887 }, 1888 ]; 1889 1890 # define if column filter values should be translatable 1891 my $TranslationOption = 0; 1892 1893 if ( 1894 $Param{ColumnName} eq 'State' 1895 || $Param{ColumnName} eq 'Lock' 1896 || $Param{ColumnName} eq 'Priority' 1897 ) 1898 { 1899 $TranslationOption = 1; 1900 } 1901 1902 my $Class = 'ColumnFilter'; 1903 if ( $Param{Css} ) { 1904 $Class .= ' ' . $Param{Css}; 1905 } 1906 1907 # build select HTML 1908 my $ColumnFilterHTML = $LayoutObject->BuildSelection( 1909 Name => 'ColumnFilter' . $Param{ColumnName}, 1910 Data => $Data, 1911 Class => $Class, 1912 Translation => $TranslationOption, 1913 SelectedID => '', 1914 ); 1915 return $ColumnFilterHTML; 1916} 1917 1918sub FilterContent { 1919 my ( $Self, %Param ) = @_; 1920 1921 return if !$Param{HeaderColumn}; 1922 1923 my $HeaderColumn = $Param{HeaderColumn}; 1924 1925 # get column values for to build the filters later 1926 my $ColumnValues = $Self->_GetColumnValues( 1927 OriginalTicketIDs => $Param{OriginalTicketIDs}, 1928 HeaderColumn => $HeaderColumn, 1929 ); 1930 1931 my $SelectedValue = ''; 1932 my $SelectedColumn = $HeaderColumn; 1933 if ( $HeaderColumn eq 'CustomerUserID' ) { 1934 $SelectedColumn = 'CustomerUserLogin'; 1935 } 1936 elsif ( $HeaderColumn eq 'CustomerID' ) { 1937 $SelectedColumn = 'CustomerID'; 1938 } 1939 elsif ( $HeaderColumn !~ m{ \A DynamicField_ }xms ) { 1940 $SelectedColumn .= 'IDs'; 1941 } 1942 1943 my $LabelColumn = $HeaderColumn; 1944 if ( $LabelColumn =~ m{ \A DynamicField_ }xms ) { 1945 1946 my $DynamicFieldConfig; 1947 $LabelColumn =~ s{\A DynamicField_ }{}xms; 1948 1949 DYNAMICFIELD: 1950 for my $DFConfig ( @{ $Self->{DynamicField} } ) { 1951 next DYNAMICFIELD if !IsHashRefWithData($DFConfig); 1952 next DYNAMICFIELD if $DFConfig->{Name} ne $LabelColumn; 1953 1954 $DynamicFieldConfig = $DFConfig; 1955 last DYNAMICFIELD; 1956 } 1957 if ( IsHashRefWithData($DynamicFieldConfig) ) { 1958 $LabelColumn = $DynamicFieldConfig->{Label}; 1959 } 1960 } 1961 1962 if ( $SelectedColumn && $Self->{StoredFilters}->{$SelectedColumn} ) { 1963 1964 if ( IsArrayRefWithData( $Self->{StoredFilters}->{$SelectedColumn} ) ) { 1965 $SelectedValue = $Self->{StoredFilters}->{$SelectedColumn}->[0]; 1966 } 1967 elsif ( IsHashRefWithData( $Self->{StoredFilters}->{$SelectedColumn} ) ) { 1968 $SelectedValue = $Self->{StoredFilters}->{$SelectedColumn}->{Equals}; 1969 } 1970 } 1971 1972 # variable to save the filter's HTML code 1973 my $ColumnFilterJSON = $Self->_ColumnFilterJSON( 1974 ColumnName => $HeaderColumn, 1975 Label => $LabelColumn, 1976 ColumnValues => $ColumnValues->{$HeaderColumn}, 1977 SelectedValue => $SelectedValue, 1978 ); 1979 1980 return $ColumnFilterJSON; 1981} 1982 1983# =head2 _ColumnFilterJSON() 1984 1985# creates a JSON select filter for column header 1986 1987# my $ColumnFilterJSON = $TicketOverviewSmallObject->_ColumnFilterJSON( 1988# ColumnName => 'Queue', 1989# Label => 'Queue', 1990# ColumnValues => { 1991# 1 => 'PostMaster', 1992# 2 => 'Junk', 1993# }, 1994# SelectedValue '1', 1995# ); 1996 1997# =cut 1998 1999sub _ColumnFilterJSON { 2000 my ( $Self, %Param ) = @_; 2001 2002 if ( 2003 !$Self->{AvailableFilterableColumns}->{ $Param{ColumnName} } && 2004 !$Self->{AvailableFilterableColumns}->{ $Param{ColumnName} . 'IDs' } 2005 ) 2006 { 2007 return; 2008 } 2009 2010 # get layout object 2011 my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout'); 2012 2013 my $Label = $Param{Label}; 2014 $Label =~ s{ \A DynamicField_ }{}gxms; 2015 $Label = $LayoutObject->{LanguageObject}->Translate($Label); 2016 2017 # set fixed values 2018 my $Data = [ 2019 { 2020 Key => 'DeleteFilter', 2021 Value => uc $Label, 2022 }, 2023 { 2024 Key => '-', 2025 Value => '-', 2026 Disabled => 1, 2027 }, 2028 ]; 2029 2030 if ( $Param{ColumnValues} && ref $Param{ColumnValues} eq 'HASH' ) { 2031 2032 my %Values = %{ $Param{ColumnValues} }; 2033 2034 # Keys must be link encoded for dynamic fields because they are added to URL during filtering 2035 # and can contain characters like '&', ';', etc. 2036 # See bug#14497 - https://bugs.otrs.org/show_bug.cgi?id=14497. 2037 my $Encoding = ( $Param{ColumnName} =~ m/^DynamicField_/ ) ? 1 : 0; 2038 2039 # Set possible values. 2040 for my $ValueKey ( sort { lc $Values{$a} cmp lc $Values{$b} } keys %Values ) { 2041 push @{$Data}, { 2042 Key => $Encoding ? $LayoutObject->LinkEncode($ValueKey) : $ValueKey, 2043 Value => $Values{$ValueKey}, 2044 }; 2045 } 2046 } 2047 2048 # define if column filter values should be translatable 2049 my $TranslationOption = 0; 2050 2051 if ( 2052 $Param{ColumnName} eq 'State' 2053 || $Param{ColumnName} eq 'Lock' 2054 || $Param{ColumnName} eq 'Priority' 2055 ) 2056 { 2057 $TranslationOption = 1; 2058 } 2059 2060 # build select HTML 2061 my $JSON = $LayoutObject->BuildSelectionJSON( 2062 [ 2063 { 2064 Name => 'ColumnFilter' . $Param{ColumnName}, 2065 Data => $Data, 2066 Class => 'ColumnFilter', 2067 Sort => 'AlphanumericKey', 2068 TreeView => 1, 2069 SelectedID => $Param{SelectedValue}, 2070 Translation => $TranslationOption, 2071 AutoComplete => 'off', 2072 }, 2073 ], 2074 ); 2075 2076 return $JSON; 2077} 2078 2079sub _DefaultColumnSort { 2080 2081 my %DefaultColumns = ( 2082 TicketNumber => 100, 2083 Age => 110, 2084 Changed => 111, 2085 PendingTime => 112, 2086 EscalationTime => 113, 2087 EscalationSolutionTime => 114, 2088 EscalationResponseTime => 115, 2089 EscalationUpdateTime => 116, 2090 Sender => 120, 2091 Title => 122, 2092 Subject => 124, 2093 State => 130, 2094 Lock => 140, 2095 Queue => 150, 2096 Owner => 160, 2097 Responsible => 161, 2098 CustomerID => 170, 2099 CustomerName => 171, 2100 CustomerUserID => 172, 2101 Type => 180, 2102 Service => 191, 2103 SLA => 192, 2104 Priority => 193, 2105 ); 2106 2107 # dynamic fields can not be on the DefaultColumns sorting hash 2108 # when comparing 2 dynamic fields sorting must be alphabetical 2109 if ( !$DefaultColumns{$a} && !$DefaultColumns{$b} ) { 2110 return $a cmp $b; 2111 } 2112 2113 # when a dynamic field is compared to a ticket attribute it must be higher 2114 elsif ( !$DefaultColumns{$a} ) { 2115 return 1; 2116 } 2117 2118 # when a ticket attribute is compared to a dynamic field it must be lower 2119 elsif ( !$DefaultColumns{$b} ) { 2120 return -1; 2121 } 2122 2123 # otherwise do a numerical comparison with the ticket attributes 2124 return $DefaultColumns{$a} <=> $DefaultColumns{$b}; 2125} 2126 21271; 2128