Files
scripts/Perl OTRS/Kernel/Output/HTML/TicketOverview/Small.pm
2024-10-14 00:08:40 +02:00

2118 lines
78 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::TicketOverview::Small;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
use Kernel::Language qw(Translatable);
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::Language',
'Kernel::System::Log',
'Kernel::Output::HTML::Layout',
'Kernel::System::CustomerUser',
'Kernel::System::Group',
'Kernel::System::User',
'Kernel::System::JSON',
'Kernel::System::DynamicField',
'Kernel::System::Ticket::ColumnFilter',
'Kernel::System::DynamicField::Backend',
'Kernel::System::Ticket',
'Kernel::System::Ticket::Article',
'Kernel::System::Main',
);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = \%Param;
bless( $Self, $Type );
# get UserID param
$Self->{UserID} = $Param{UserID} || die "Got no UserID!";
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# set pref for columns key
$Self->{PrefKeyColumns} = 'UserFilterColumnsEnabled' . '-' . $Self->{Action};
# load backend config
my $BackendConfigKey = 'Ticket::Frontend::' . $Self->{Action};
$Self->{Config} = $ConfigObject->Get($BackendConfigKey);
my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
UserID => $Self->{UserID},
);
# get JSON object
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
# set stored filters if present
my $StoredFiltersKey = 'UserStoredFilterColumns-' . $Self->{Action};
if ( $Preferences{$StoredFiltersKey} ) {
my $StoredFilters = $JSONObject->Decode(
Data => $Preferences{$StoredFiltersKey},
);
$Self->{StoredFilters} = $StoredFilters;
}
# get the configured dyanmic fields from the Small Overview setting as a basis
my %DefaultDynamicFields = %{ $ConfigObject->Get("Ticket::Frontend::OverviewSmall")->{DynamicField} || {} };
my %DefaultColumns = map { 'DynamicField_' . $_ => $DefaultDynamicFields{$_} } sort keys %DefaultDynamicFields;
# take general settings (Frontend::Agent) if not defined for the screen
$Self->{Config}->{DefaultColumns} //= $ConfigObject->Get('DefaultOverviewColumns');
# check for default settings specific for this screen, should overide the dynamic fields
%DefaultColumns = ( %DefaultColumns, %{ $Self->{Config}->{DefaultColumns} || {} } );
# configure columns
my @ColumnsAvailable = grep { $DefaultColumns{$_} ne '0' } sort keys %DefaultColumns;
my @ColumnsEnabled = grep { $DefaultColumns{$_} eq '2' } sort _DefaultColumnSort keys %DefaultColumns;
# if preference settings are available, take them
if ( $Preferences{ $Self->{PrefKeyColumns} } ) {
my $ColumnsEnabled = $JSONObject->Decode(
Data => $Preferences{ $Self->{PrefKeyColumns} },
);
# remove duplicate columns
my %UniqueColumns;
my @ColumnsEnabledAux;
for my $Column ( @{$ColumnsEnabled} ) {
if ( !$UniqueColumns{$Column} ) {
push @ColumnsEnabledAux, $Column;
}
$UniqueColumns{$Column} = 1;
}
# set filtered column list
@ColumnsEnabled = @ColumnsEnabledAux;
}
# always set TicketNumber
if ( !grep { $_ eq 'TicketNumber' } @ColumnsEnabled ) {
unshift @ColumnsEnabled, 'TicketNumber';
}
$Self->{ColumnsEnabled} = \@ColumnsEnabled;
$Self->{ColumnsAvailable} = \@ColumnsAvailable;
{
# loop through all the dynamic fields to get the ones that should be shown
DYNAMICFIELDNAME:
for my $DynamicFieldName (@ColumnsEnabled) {
next DYNAMICFIELDNAME if $DynamicFieldName !~ m{ DynamicField_ }xms;
# remove dynamic field prefix
my $FieldName = $DynamicFieldName;
$FieldName =~ s/DynamicField_//gi;
$Self->{DynamicFieldFilter}->{$FieldName} = 1;
}
}
# get the dynamic fields for this screen
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $Self->{DynamicFieldFilter} || {},
);
# hash with all valid sortable columns (taken from TicketSearch)
# SortBy => 'Age', # Created|Owner|Responsible|CustomerID|State|TicketNumber|Queue
# |Priority|Type|Lock|Title|Service|Changed|SLA|PendingTime|EscalationTime
# | EscalationUpdateTime|EscalationResponseTime|EscalationSolutionTime
$Self->{ValidSortableColumns} = {
'Age' => 1,
'Created' => 1,
'Owner' => 1,
'Responsible' => 1,
'CustomerID' => 1,
'State' => 1,
'TicketNumber' => 1,
'Queue' => 1,
'Priority' => 1,
'Type' => 1,
'Lock' => 1,
'Title' => 1,
'Service' => 1,
'Changed' => 1,
'SLA' => 1,
'PendingTime' => 1,
'EscalationTime' => 1,
'EscalationUpdateTime' => 1,
'EscalationResponseTime' => 1,
'EscalationSolutionTime' => 1,
};
$Self->{AvailableFilterableColumns} = {
'Owner' => 1,
'Responsible' => 1,
'CustomerID' => 1,
'CustomerUserID' => 1,
'State' => 1,
'Queue' => 1,
'Priority' => 1,
'Type' => 1,
'Lock' => 1,
'Service' => 1,
'SLA' => 1,
};
# remove queue from filters on AgentTicketQueue
if ( $Self->{Action} eq 'AgentTicketQueue' ) {
delete $Self->{AvailableFilterableColumns}->{Queue};
}
# remove service from filters on AgentTicketService
if ( $Self->{Action} eq 'AgentTicketService' ) {
delete $Self->{AvailableFilterableColumns}->{Service};
}
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get filterable dynamic fields
# cycle trough the activated dynamic fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsFiltrable = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsFiltrable',
);
# if the dynamic field is filterable add it to the AvailableFilterableColumns hash
if ($IsFiltrable) {
$Self->{AvailableFilterableColumns}->{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = 1;
}
}
# get sortable dynamic fields
# cycle trough the activated dynamic fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsSortable = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsSortable',
);
# if the dynamic field is sortable add it to the ValidSortableColumns hash
if ($IsSortable) {
$Self->{ValidSortableColumns}->{ 'DynamicField_' . $DynamicFieldConfig->{Name} } = 1;
}
}
return $Self;
}
sub ActionRow {
my ( $Self, %Param ) = @_;
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# check if bulk feature is enabled
my $BulkFeature = 0;
if ( $Param{Bulk} && $ConfigObject->Get('Ticket::Frontend::BulkFeature') ) {
my @Groups;
if ( $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') ) {
@Groups = @{ $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') };
}
if ( !@Groups ) {
$BulkFeature = 1;
}
else {
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
GROUP:
for my $Group (@Groups) {
my $HasPermission = $GroupObject->PermissionCheck(
UserID => $Self->{UserID},
GroupName => $Group,
Type => 'rw',
);
if ($HasPermission) {
$BulkFeature = 1;
last GROUP;
}
}
}
}
$LayoutObject->Block(
Name => 'DocumentActionRow',
Data => \%Param,
);
if ($BulkFeature) {
$LayoutObject->Block(
Name => 'DocumentActionRowBulk',
Data => {
%Param,
Name => Translatable('Bulk'),
},
);
}
# check if there was a column filter and no results, and print a link to back
if ( scalar @{ $Param{TicketIDs} } == 0 && $Param{LastColumnFilter} ) {
$LayoutObject->Block(
Name => 'DocumentActionRowLastColumnFilter',
Data => {
%Param,
},
);
}
my %ColumnTranslations;
my $LanguageObject = $Kernel::OM->Get('Kernel::Language');
# add translations for the allocation lists for regular columns
my $Columns = $Self->{Config}->{DefaultColumns} || $ConfigObject->Get('DefaultOverviewColumns') || {};
if ( $Columns && IsHashRefWithData($Columns) ) {
COLUMN:
for my $Column ( sort keys %{$Columns} ) {
# dynamic fields will be translated in the next block
next COLUMN if $Column =~ m{ \A DynamicField_ }xms;
my $TranslatedWord = $Column;
if ( $Column eq 'EscalationTime' ) {
$TranslatedWord = Translatable('Service Time');
}
elsif ( $Column eq 'EscalationResponseTime' ) {
$TranslatedWord = Translatable('First Response Time');
}
elsif ( $Column eq 'EscalationSolutionTime' ) {
$TranslatedWord = Translatable('Solution Time');
}
elsif ( $Column eq 'EscalationUpdateTime' ) {
$TranslatedWord = Translatable('Update Time');
}
elsif ( $Column eq 'PendingTime' ) {
$TranslatedWord = Translatable('Pending till');
}
elsif ( $Column eq 'CustomerCompanyName' ) {
$TranslatedWord = Translatable('Customer Name');
}
elsif ( $Column eq 'CustomerID' ) {
$TranslatedWord = Translatable('Customer ID');
}
elsif ( $Column eq 'CustomerName' ) {
$TranslatedWord = Translatable('Customer User Name');
}
elsif ( $Column eq 'CustomerUserID' ) {
$TranslatedWord = Translatable('Customer User ID');
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'Column' . $Column,
Value => $LanguageObject->Translate($TranslatedWord),
);
}
}
# add translations for the allocation lists for dynamic field columns
my $ColumnsDynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 0,
ObjectType => ['Ticket'],
);
if ( $ColumnsDynamicField && IsArrayRefWithData($ColumnsDynamicField) ) {
my $Counter = 0;
DYNAMICFIELD:
for my $DynamicField ( sort @{$ColumnsDynamicField} ) {
next DYNAMICFIELD if !$DynamicField;
$Counter++;
# send data to JS
$LayoutObject->AddJSData(
Key => 'ColumnDynamicField_' . $DynamicField->{Name},
Value => $LanguageObject->Translate( $DynamicField->{Label} ),
);
}
}
my $Output = $LayoutObject->Output(
TemplateFile => 'AgentTicketOverviewSmall',
Data => \%Param,
);
return $Output;
}
sub SortOrderBar {
my ( $Self, %Param ) = @_;
return '';
}
sub Run {
my ( $Self, %Param ) = @_;
# If $Param{EnableColumnFilters} is not sent, we want to disable all filters
# for the current screen. We localize the setting for this sub and change it
# after that, if needed. The original value will be restored after this function.
local $Self->{AvailableFilterableColumns} = $Self->{AvailableFilterableColumns};
if ( !$Param{EnableColumnFilters} ) {
$Self->{AvailableFilterableColumns} = {}; # disable all column filters
}
for my $Item (qw(TicketIDs PageShown StartHit)) {
if ( !$Param{$Item} ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Item!",
);
return;
}
}
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# check if bulk feature is enabled
my $BulkFeature = 0;
if ( $Param{Bulk} && $ConfigObject->Get('Ticket::Frontend::BulkFeature') ) {
my @Groups;
if ( $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') ) {
@Groups = @{ $ConfigObject->Get('Ticket::Frontend::BulkFeatureGroup') };
}
if ( !@Groups ) {
$BulkFeature = 1;
}
else {
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
GROUP:
for my $Group (@Groups) {
my $HasPermission = $GroupObject->PermissionCheck(
UserID => $Self->{UserID},
GroupName => $Group,
Type => 'rw',
);
if ($HasPermission) {
$BulkFeature = 1;
last GROUP;
}
}
}
}
my $Counter = 0;
my @ArticleBox;
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
# Get extended ticket data if columns are used, save performance. See bug#14748.
my %ExtendedColumnsHash = (
FirstResponse => 1,
FirstResponseInMin => 1,
FirstResponseDiffInMin => 1,
SolutionInMin => 1,
SolutionDiffInMin => 1,
FirstLock => 1,
);
my $Extended = 0;
if ( grep { $ExtendedColumnsHash{$_} } @{ $Self->{ColumnsEnabled} } ) {
$Extended = 1;
}
for my $TicketID ( @{ $Param{TicketIDs} } ) {
$Counter++;
if ( $Counter >= $Param{StartHit} && $Counter < ( $Param{PageShown} + $Param{StartHit} ) ) {
# Get last customer article.
my @Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => 'customer',
OnlyLast => 1,
);
# If the ticket has no customer article, get the last agent article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
SenderType => 'agent',
OnlyLast => 1,
);
}
# Finally, if everything failed, get latest article.
if ( !@Articles ) {
@Articles = $ArticleObject->ArticleList(
TicketID => $TicketID,
OnlyLast => 1,
);
}
my %Article;
for my $Article (@Articles) {
%Article = $ArticleObject->BackendForArticle( %{$Article} )->ArticleGet(
%{$Article},
DynamicFields => 0,
);
}
# Get ticket data.
my %Ticket = $TicketObject->TicketGet(
TicketID => $TicketID,
Extended => $Extended,
DynamicFields => 0,
);
%Article = ( %Article, %Ticket );
# Get channel specific fields.
if ( $Article{ArticleID} ) {
my %ArticleFields = $LayoutObject->ArticleFields(
TicketID => $TicketID,
ArticleID => $Article{ArticleID},
);
FIELD:
for my $FieldKey (qw(Sender Subject)) {
next FIELD if !defined $ArticleFields{$FieldKey}->{Value};
$Article{$FieldKey} = $ArticleFields{$FieldKey}->{Realname} // $ArticleFields{$FieldKey}->{Value};
}
}
# Fallback for tickets without articles: get at least basic ticket data.
if ( !%Article ) {
%Article = %Ticket;
if ( !$Article{Title} ) {
$Article{Title} = $LayoutObject->{LanguageObject}->Translate(
'This ticket has no title or subject'
);
}
$Article{Subject} = $Article{Title};
}
# show ticket create time in small view
$Article{Created} = $Ticket{Created};
# create human age
$Article{Age} = $LayoutObject->CustomerAge(
Age => $Article{Age},
Space => ' ',
);
# get ACL restrictions
my %PossibleActions;
my $Counter = 0;
# get all registered Actions
if ( ref $ConfigObject->Get('Frontend::Module') eq 'HASH' ) {
my %Actions = %{ $ConfigObject->Get('Frontend::Module') };
# only use those Actions that stats with AgentTicket
%PossibleActions = map { ++$Counter => $_ }
grep { substr( $_, 0, length 'AgentTicket' ) eq 'AgentTicket' }
sort keys %Actions;
}
my $ACL = $TicketObject->TicketAcl(
Data => \%PossibleActions,
Action => $Self->{Action},
TicketID => $Article{TicketID},
ReturnType => 'Action',
ReturnSubType => '-',
UserID => $Self->{UserID},
);
my %AclAction = %PossibleActions;
if ($ACL) {
%AclAction = $TicketObject->TicketAclActionData();
}
# run ticket pre menu modules
my @ActionItems;
if ( ref $ConfigObject->Get('Ticket::Frontend::PreMenuModule') eq 'HASH' ) {
my %Menus = %{ $ConfigObject->Get('Ticket::Frontend::PreMenuModule') };
my @Items;
MENU:
for my $Menu ( sort keys %Menus ) {
# load module
if ( !$Kernel::OM->Get('Kernel::System::Main')->Require( $Menus{$Menu}->{Module} ) ) {
return $LayoutObject->FatalError();
}
my $Object = $Menus{$Menu}->{Module}->new(
%{$Self},
TicketID => $Article{TicketID},
);
# run module
my $Item = $Object->Run(
%Param,
Ticket => \%Article,
ACL => \%AclAction,
Config => $Menus{$Menu},
);
next MENU if !$Item;
next MENU if ref $Item ne 'HASH';
# add session id if needed
if ( !$LayoutObject->{SessionIDCookie} && $Item->{Link} ) {
$Item->{Link}
.= ';'
. $LayoutObject->{SessionName} . '='
. $LayoutObject->{SessionID};
}
# create id
$Item->{ID} = $Item->{Name};
$Item->{ID} =~ s/(\s|&|;)//ig;
my $Output;
if ( $Item->{Block} ) {
$LayoutObject->Block(
Name => $Item->{Block},
Data => $Item,
);
$Output = $LayoutObject->Output(
TemplateFile => 'AgentTicketOverviewSmall',
Data => $Item,
);
}
else {
$Output = '<li id="'
. $Item->{ID}
. '"><a href="#" title="'
. $LayoutObject->{LanguageObject}->Translate( $Item->{Description} )
. '">'
. $LayoutObject->{LanguageObject}->Translate( $Item->{Name} )
. '</a></li>';
}
$Output =~ s/\n+//g;
$Output =~ s/\s+/ /g;
$Output =~ s/<\!--.+?-->//g;
push @ActionItems, {
HTML => $Output,
ID => $Item->{ID},
Link => $LayoutObject->{Baselink} . $Item->{Link},
Target => $Item->{Target},
PopupType => $Item->{PopupType},
Description => $Item->{Description},
};
$Article{ActionItems} = \@ActionItems;
}
}
push @ArticleBox, \%Article;
}
}
# check if sysconfig is a hash reference
if ( IsArrayRefWithData( $Self->{ColumnsEnabled} ) ) {
# check if column is really filterable
COLUMNNAME:
for my $ColumnName ( @{ $Self->{ColumnsEnabled} } ) {
next COLUMNNAME if !grep { $_ eq $ColumnName } @{ $Self->{ColumnsEnabled} };
next COLUMNNAME if !$Self->{AvailableFilterableColumns}->{$ColumnName};
$Self->{ValidFilterableColumns}->{$ColumnName} = 1;
}
}
my $ColumnValues = $Self->_GetColumnValues(
OriginalTicketIDs => $Param{OriginalTicketIDs},
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'LinkPage',
Value => $Param{LinkPage},
);
$LayoutObject->Block(
Name => 'DocumentContent',
Data => \%Param,
);
# array to save the column names to do the query
my @Col = @{ $Self->{ColumnsEnabled} };
# define special ticket columns
my %SpecialColumns = (
TicketNumber => 1,
Owner => 1,
Responsible => 1,
CustomerID => 1,
);
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
$Param{OrderBy} = $Param{OrderBy} || 'Up';
my $TicketData = scalar @ArticleBox;
if ($TicketData) {
$LayoutObject->Block( Name => 'OverviewTable' );
$LayoutObject->Block( Name => 'TableHeader' );
if ($BulkFeature) {
$LayoutObject->Block(
Name => 'GeneralOverviewHeader',
);
$LayoutObject->Block(
Name => 'BulkNavBar',
Data => \%Param,
);
}
# meta items
my @TicketMetaItems = $LayoutObject->TicketMetaItemsCount();
for my $Item (@TicketMetaItems) {
$LayoutObject->Block(
Name => 'GeneralOverviewHeader',
);
my $CSS = '';
my $OrderBy = $Param{OrderBy};
my $Link;
my $Title = $LayoutObject->{LanguageObject}->Translate($Item);
if ( $Param{SortBy} && ( $Param{SortBy} eq $Item ) ) {
my $TitleDesc;
# Change order for sorting column.
$OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up';
if ( $OrderBy eq 'Down' ) {
$CSS .= ' SortAscendingLarge';
$TitleDesc = Translatable('sorted ascending');
}
else {
$CSS .= ' SortDescendingLarge';
$TitleDesc = Translatable('sorted descending');
}
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
$Title .= ', ' . $TitleDesc;
}
$LayoutObject->Block(
Name => 'OverviewNavBarPageFlag',
Data => {
CSS => $CSS,
Title => $Title,
},
);
if ( $Item eq 'New Article' ) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageFlagEmpty',
Data => {
Name => $Item,
}
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageFlagLink',
Data => {
%Param,
Name => $Item,
CSS => $CSS,
OrderBy => $OrderBy,
Title => $Title,
},
);
}
}
my $CSS = '';
# show special ticket columns, if needed
COLUMN:
for my $Column (@Col) {
$LayoutObject->Block(
Name => 'GeneralOverviewHeader',
);
$CSS = $Column;
my $Title = $LayoutObject->{LanguageObject}->Translate($Column);
my $OrderBy = $Param{OrderBy};
# output overall block so TicketNumber as well as other columns can be ordered
$LayoutObject->Block(
Name => 'OverviewNavBarPageTicketHeader',
Data => {},
);
if ( $SpecialColumns{$Column} ) {
if ( $Param{SortBy} && ( $Param{SortBy} eq $Column ) ) {
my $TitleDesc;
# Change order for sorting column.
$OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up';
if ( $OrderBy eq 'Down' ) {
$CSS .= ' SortAscendingLarge';
$TitleDesc = Translatable('sorted ascending');
}
else {
$CSS .= ' SortDescendingLarge';
$TitleDesc = Translatable('sorted descending');
}
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
$Title .= ', ' . $TitleDesc;
}
# translate the column name to write it in the current language
my $TranslatedWord;
if ( $Column eq 'CustomerID' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer ID');
}
else {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate($Column);
}
my $FilterTitle = $TranslatedWord;
my $FilterTitleDesc = Translatable('filter not active');
if (
$Self->{StoredFilters} &&
(
$Self->{StoredFilters}->{$Column} ||
$Self->{StoredFilters}->{ $Column . 'IDs' }
)
)
{
$CSS .= ' FilterActive';
$FilterTitleDesc = Translatable('filter active');
}
$FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc);
$FilterTitle .= ', ' . $FilterTitleDesc;
$LayoutObject->Block(
Name =>
$Column eq 'TicketNumber'
? 'OverviewNavBarPageTicketNumber'
: 'OverviewNavBarPageColumn',
Data => {
%Param,
OrderBy => $OrderBy,
ColumnName => $Column || '',
CSS => $CSS || '',
ColumnNameTranslated => $TranslatedWord || $Column,
Title => $Title,
},
);
# verify if column is filterable and sortable
if (
$Self->{ValidFilterableColumns}->{$Column}
&& $Self->{ValidSortableColumns}->{$Column}
)
{
my $Css;
if ( $Column eq 'CustomerID' || $Column eq 'Owner' || $Column eq 'Responsible' ) {
$Css .= ' Hidden';
}
# variable to save the filter's html code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $Column,
Label => $Column,
ColumnValues => $ColumnValues->{$Column},
SelectedValue => $Param{GetColumnFilter}->{$Column} || '',
Css => $Css,
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnFilterLink',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
ColumnFilterStrg => $ColumnFilterHTML,
OrderBy => $OrderBy,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
if ( $Column eq 'CustomerID' ) {
$LayoutObject->Block(
Name =>
'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerIDSearch',
Data => {},
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'CustomerIDAutocomplete',
Value => {
'QueryDelay' => 100,
'MaxResultsDisplayed' => 20,
'MinQueryLength' => 2,
},
);
}
elsif ( $Column eq 'Owner' || $Column eq 'Responsible' ) {
$LayoutObject->Block(
Name => 'ContentLargeTicketGenericHeaderColumnFilterLinkUserSearch',
Data => {},
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'UserAutocomplete',
Value => {
'QueryDelay' => 100,
'MaxResultsDisplayed' => 20,
'MinQueryLength' => 2,
},
);
}
}
# verify if column is filterable
elsif ( $Self->{ValidFilterableColumns}->{$Column} ) {
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $Column,
Label => $Column,
ColumnValues => $ColumnValues->{$Column},
SelectedValue => $Param{GetColumnFilter}->{$Column} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnFilter',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
ColumnFilterStrg => $ColumnFilterHTML,
OrderBy => $OrderBy,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
}
# verify if column is sortable
elsif ( $Self->{ValidSortableColumns}->{$Column} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnLink',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
OrderBy => $OrderBy,
Title => $Title,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnEmpty',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
Title => $Title,
},
);
}
next COLUMN;
}
elsif ( $Column !~ m{\A DynamicField_}xms ) {
if ( $Param{SortBy} && ( $Param{SortBy} eq $Column ) ) {
my $TitleDesc;
# Change order for sorting column.
$OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up';
if ( $OrderBy eq 'Down' ) {
$CSS .= ' SortAscendingLarge';
$TitleDesc = Translatable('sorted ascending');
}
else {
$CSS .= ' SortDescendingLarge';
$TitleDesc = Translatable('sorted descending');
}
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
$Title .= ', ' . $TitleDesc;
}
# translate the column name to write it in the current language
my $TranslatedWord;
if ( $Column eq 'EscalationTime' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Service Time');
}
elsif ( $Column eq 'EscalationResponseTime' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('First Response Time');
}
elsif ( $Column eq 'EscalationSolutionTime' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Solution Time');
}
elsif ( $Column eq 'EscalationUpdateTime' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Update Time');
}
elsif ( $Column eq 'PendingTime' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Pending till');
}
elsif ( $Column eq 'CustomerCompanyName' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer Name');
}
elsif ( $Column eq 'CustomerName' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User Name');
}
elsif ( $Column eq 'CustomerUserID' ) {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User ID');
}
else {
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate($Column);
}
my $FilterTitle = $TranslatedWord;
my $FilterTitleDesc = Translatable('filter not active');
if ( $Self->{StoredFilters} && $Self->{StoredFilters}->{ $Column . 'IDs' } ) {
$CSS .= ' FilterActive';
$FilterTitleDesc = Translatable('filter active');
}
$FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc);
$FilterTitle .= ', ' . $FilterTitleDesc;
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumn',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
},
);
# verify if column is filterable and sortable
if (
$Self->{ValidFilterableColumns}->{$Column}
&& $Self->{ValidSortableColumns}->{$Column}
)
{
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $Column,
Label => $Column,
ColumnValues => $ColumnValues->{$Column},
SelectedValue => $Param{GetColumnFilter}->{$Column} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnFilterLink',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
ColumnFilterStrg => $ColumnFilterHTML,
OrderBy => $OrderBy,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
}
# verify if column is just filterable
elsif ( $Self->{ValidFilterableColumns}->{$Column} ) {
my $Css;
if ( $Column eq 'CustomerUserID' ) {
$Css = 'Hidden';
}
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $Column,
Label => $Column,
ColumnValues => $ColumnValues->{$Column},
SelectedValue => $Param{GetColumnFilter}->{$Column} || '',
Css => $Css,
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnFilter',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
ColumnFilterStrg => $ColumnFilterHTML,
OrderBy => $OrderBy,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
if ( $Column eq 'CustomerUserID' ) {
$LayoutObject->Block(
Name =>
'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerUserSearch',
Data => {},
);
# send data to JS
$LayoutObject->AddJSData(
Key => 'CustomerUserAutocomplete',
Value => {
'QueryDelay' => 100,
'MaxResultsDisplayed' => 20,
'MinQueryLength' => 2,
},
);
}
}
# verify if column is sortable
elsif ( $Self->{ValidSortableColumns}->{$Column} ) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnLink',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
OrderBy => $OrderBy,
Title => $Title,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageColumnEmpty',
Data => {
%Param,
ColumnName => $Column,
CSS => $CSS,
ColumnNameTranslated => $TranslatedWord || $Column,
Title => $Title,
},
);
}
}
# show the DFs
else {
my $DynamicFieldConfig;
my $DFColumn = $Column;
$DFColumn =~ s/DynamicField_//g;
DYNAMICFIELD:
for my $DFConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DFConfig);
next DYNAMICFIELD if $DFConfig->{Name} ne $DFColumn;
$DynamicFieldConfig = $DFConfig;
last DYNAMICFIELD;
}
next COLUMN if !IsHashRefWithData($DynamicFieldConfig);
my $Label = $DynamicFieldConfig->{Label};
$Title = $Label;
my $FilterTitle = $Label;
# get field sortable condition
my $IsSortable = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsSortable',
);
if ($IsSortable) {
my $CSS = 'DynamicField_' . $DynamicFieldConfig->{Name};
if (
$Param{SortBy}
&& ( $Param{SortBy} eq ( 'DynamicField_' . $DynamicFieldConfig->{Name} ) )
)
{
my $TitleDesc;
# Change order for sorting column.
$OrderBy = $OrderBy eq 'Up' ? 'Down' : 'Up';
if ( $OrderBy eq 'Down' ) {
$CSS .= ' SortAscendingLarge';
$TitleDesc = Translatable('sorted ascending');
}
else {
$CSS .= ' SortDescendingLarge';
$TitleDesc = Translatable('sorted descending');
}
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
$Title .= ', ' . $TitleDesc;
}
my $FilterTitleDesc = Translatable('filter not active');
if ( $Self->{StoredFilters} && $Self->{StoredFilters}->{$Column} ) {
$CSS .= ' FilterActive';
$FilterTitleDesc = Translatable('filter active');
}
$FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc);
$FilterTitle .= ', ' . $FilterTitleDesc;
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField',
Data => {
%Param,
CSS => $CSS,
},
);
my $DynamicFieldName = 'DynamicField_' . $DynamicFieldConfig->{Name};
if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $DynamicFieldName,
Label => $Label,
ColumnValues => $ColumnValues->{$DynamicFieldName},
SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldFiltrableSortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
ColumnFilterStrg => $ColumnFilterHTML,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldSortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
Title => $Title,
},
);
}
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name},
Data => {
%Param,
CSS => $CSS,
},
);
if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $DynamicFieldName,
Label => $Label,
ColumnValues => $ColumnValues->{$DynamicFieldName},
SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField'
. $DynamicFieldConfig->{Name}
. '_FiltrableSortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
ColumnFilterStrg => $ColumnFilterHTML,
Title => $Title,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_'
. $DynamicFieldConfig->{Name}
. '_Sortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
Title => $Title,
},
);
}
}
else {
my $DynamicFieldName = 'DynamicField_' . $DynamicFieldConfig->{Name};
my $CSS = $DynamicFieldName;
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField',
Data => {
%Param,
CSS => $CSS,
},
);
if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $DynamicFieldName,
Label => $Label,
ColumnValues => $ColumnValues->{$DynamicFieldName},
SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldFiltrableNotSortable',
Data => {
%Param,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
ColumnFilterStrg => $ColumnFilterHTML,
Title => $Title,
FilterTitle => $FilterTitle,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldNotSortable',
Data => {
%Param,
Label => $Label,
Title => $Title,
},
);
}
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name},
Data => {
%Param,
},
);
if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
# variable to save the filter's HTML code
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
ColumnName => $DynamicFieldName,
Label => $Label,
ColumnValues => $ColumnValues->{$DynamicFieldName},
SelectedValue => $Param{GetColumnFilter}->{$DynamicFieldName} || '',
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_'
. $DynamicFieldConfig->{Name}
. '_FiltrableNotSortable',
Data => {
%Param,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
ColumnFilterStrg => $ColumnFilterHTML,
Title => $Title,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_'
. $DynamicFieldConfig->{Name}
. '_NotSortable',
Data => {
%Param,
Label => $Label,
Title => $Title,
},
);
}
}
}
}
$LayoutObject->Block( Name => 'TableBody' );
}
else {
$LayoutObject->Block( Name => 'NoTicketFound' );
}
my %ActionRowTickets;
for my $ArticleRef (@ArticleBox) {
# get last customer article
my %Article = %{$ArticleRef};
# escalation human times
if ( $Article{EscalationTime} ) {
$Article{EscalationTimeHuman} = $LayoutObject->CustomerAge(
Age => $Article{EscalationTime},
TimeShowAlwaysLong => 1,
Space => ' ',
);
$Article{EscalationTimeWorkingTime} = $LayoutObject->CustomerAge(
Age => $Article{EscalationTimeWorkingTime},
TimeShowAlwaysLong => 1,
Space => ' ',
);
}
# customer info
my %CustomerInfo;
if ( $Param{Config}->{CustomerInfo} ) {
if ( $Article{CustomerUserID} ) {
# get customer user object
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
$Article{CustomerName} = $CustomerUserObject->CustomerName(
UserLogin => $Article{CustomerUserID},
);
%CustomerInfo = $CustomerUserObject->CustomerUserDataGet(
User => $Article{CustomerUserID},
);
INFOKEY:
for my $InfoKey ( sort keys %CustomerInfo ) {
next INFOKEY if $InfoKey =~ m{\ACustomer}xms;
$CustomerInfo{ 'Customer' . $InfoKey } = $CustomerInfo{$InfoKey};
}
}
}
# get user object
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
# user info
my %UserInfo = $UserObject->GetUserData(
UserID => $Article{OwnerID},
);
# Responsible info.
my %ResponsibleInfo = $UserObject->GetUserData(
UserID => $Article{ResponsibleID},
);
$UserInfo{ResponsibleInfo} = \%ResponsibleInfo;
$LayoutObject->Block(
Name => 'Record',
Data => { %Article, %UserInfo },
);
# check if bulk feature is enabled
if ($BulkFeature) {
$LayoutObject->Block(
Name => 'GeneralOverviewRow',
);
$LayoutObject->Block(
Name => Translatable('Bulk'),
Data => { %Article, %UserInfo },
);
}
# show ticket flags
my @TicketMetaItems = $LayoutObject->TicketMetaItems(
Ticket => \%Article,
);
for my $Item (@TicketMetaItems) {
$LayoutObject->Block(
Name => 'GeneralOverviewRow',
);
$LayoutObject->Block(
Name => 'ContentLargeTicketGenericRowMeta',
Data => $Item,
);
if ($Item) {
$LayoutObject->Block(
Name => 'ContentLargeTicketGenericRowMetaImage',
Data => $Item,
);
}
}
# save column content
my $DataValue;
# show all needed columns
TICKETCOLUMN:
for my $TicketColumn (@Col) {
$LayoutObject->Block(
Name => 'GeneralOverviewRow',
);
if ( $TicketColumn !~ m{\A DynamicField_}xms ) {
$LayoutObject->Block(
Name => 'RecordTicketData',
Data => {},
);
if ( $SpecialColumns{$TicketColumn} ) {
$LayoutObject->Block(
Name => 'Record' . $TicketColumn,
Data => { %Article, %UserInfo },
);
next TICKETCOLUMN;
}
if ( $TicketColumn eq 'CreatedBy' ) {
my %TicketCreatedByInfo = $UserObject->GetUserData(
UserID => $Article{CreateBy},
);
$LayoutObject->Block(
Name => 'RecordTicketCreatedBy',
Data => \%TicketCreatedByInfo,
);
next TICKETCOLUMN;
}
# escalation column
my %EscalationData;
if ( $TicketColumn eq 'EscalationTime' ) {
$EscalationData{EscalationTime} = $Article{EscalationTime};
$EscalationData{EscalationDestinationDate} = $Article{EscalationDestinationDate};
$EscalationData{EscalationTimeHuman} = $LayoutObject->CustomerAge(
Age => $EscalationData{EscalationTime},
TimeShowAlwaysLong => 1,
Space => ' ',
) || '-';
$EscalationData{EscalationTimeWorkingTime} = $LayoutObject->CustomerAge(
Age => $EscalationData{EscalationTimeWorkingTime},
TimeShowAlwaysLong => 1,
Space => ' ',
);
if (
defined $Article{EscalationTime}
&& $Article{EscalationTime} < 60 * 60 * 1
)
{
$EscalationData{EscalationClass} = 'Warning';
}
$LayoutObject->Block(
Name => 'RecordEscalationTime',
Data => {%EscalationData},
);
next TICKETCOLUMN;
}
my $BlockType = '';
my $CSSClass = '';
if ( $TicketColumn eq 'EscalationSolutionTime' ) {
$BlockType = 'Escalation';
$DataValue = $LayoutObject->CustomerAge(
Age => $Article{SolutionTime} || 0,
TimeShowAlwaysLong => 1,
Space => ' ',
);
if ( defined $Article{SolutionTime} && $Article{SolutionTime} < 60 * 60 * 1 ) {
$CSSClass = 'Warning';
}
}
elsif ( $TicketColumn eq 'EscalationResponseTime' ) {
$BlockType = 'Escalation';
$DataValue = $LayoutObject->CustomerAge(
Age => $Article{FirstResponseTime} || 0,
TimeShowAlwaysLong => 1,
Space => ' ',
);
if (
defined $Article{FirstResponseTime}
&& $Article{FirstResponseTime} < 60 * 60 * 1
)
{
$CSSClass = 'Warning';
}
}
elsif ( $TicketColumn eq 'EscalationUpdateTime' ) {
$BlockType = 'Escalation';
$DataValue = $LayoutObject->CustomerAge(
Age => $Article{UpdateTime} || 0,
TimeShowAlwaysLong => 1,
Space => ' ',
);
if ( defined $Article{UpdateTime} && $Article{UpdateTime} < 60 * 60 * 1 ) {
$CSSClass = 'Warning';
}
}
elsif ( $TicketColumn eq 'PendingTime' ) {
$BlockType = 'Escalation';
$DataValue = $LayoutObject->CustomerAge(
Age => $Article{'UntilTime'},
Space => ' '
);
if ( defined $Article{UntilTime} && $Article{UntilTime} < -1 ) {
$CSSClass = 'Warning';
}
}
elsif (
$TicketColumn eq 'State'
|| $TicketColumn eq 'Lock'
|| $TicketColumn eq 'Priority'
)
{
$BlockType = 'Translatable';
$DataValue = $Article{$TicketColumn} || $UserInfo{$TicketColumn};
}
elsif ( $TicketColumn eq 'Created' || $TicketColumn eq 'Changed' ) {
$BlockType = 'Time';
$DataValue = $Article{$TicketColumn} || $UserInfo{$TicketColumn};
}
elsif ( $TicketColumn eq 'Responsible' ) {
my %ResponsibleInfo = $UserObject->GetUserData(
UserID => $Article{ResponsibleID},
);
$DataValue = $ResponsibleInfo{'UserFullname'};
}
else {
$DataValue = $Article{$TicketColumn}
|| $UserInfo{$TicketColumn}
|| $CustomerInfo{$TicketColumn};
# If value is in date format, change block type to 'Time' so it can be localized. See bug#14542.
if ( $DataValue =~ /^\d\d\d\d-(\d|\d\d)-(\d|\d\d)\s(\d|\d\d):(\d|\d\d):(\d|\d\d)/ ) {
$BlockType = 'Time';
}
}
$LayoutObject->Block(
Name => "RecordTicketColumn$BlockType",
Data => {
GenericValue => $DataValue || '-',
Class => $CSSClass || '',
},
);
}
# dynamic fields
else {
# cycle trough the activated dynamic fields for this screen
my $DynamicFieldConfig;
my $DFColumn = $TicketColumn;
$DFColumn =~ s/DynamicField_//g;
DYNAMICFIELD:
for my $DFConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DFConfig);
next DYNAMICFIELD if $DFConfig->{Name} ne $DFColumn;
$DynamicFieldConfig = $DFConfig;
last DYNAMICFIELD;
}
next TICKETCOLUMN if !IsHashRefWithData($DynamicFieldConfig);
# get field value
my $Value = $DynamicFieldBackendObject->ValueGet(
DynamicFieldConfig => $DynamicFieldConfig,
ObjectID => $Article{TicketID},
);
my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Value,
ValueMaxChars => 20,
LayoutObject => $LayoutObject,
);
$LayoutObject->Block(
Name => 'RecordDynamicField',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
if ( $ValueStrg->{Link} ) {
$LayoutObject->Block(
Name => 'RecordDynamicFieldLink',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
Link => $ValueStrg->{Link},
$DynamicFieldConfig->{Name} => $ValueStrg->{Title},
},
);
}
else {
$LayoutObject->Block(
Name => 'RecordDynamicFieldPlain',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
}
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name},
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
if ( $ValueStrg->{Link} ) {
$LayoutObject->Block(
Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name} . '_Link',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
Link => $ValueStrg->{Link},
$DynamicFieldConfig->{Name} => $ValueStrg->{Title},
},
);
}
else {
$LayoutObject->Block(
Name => 'RecordDynamicField_' . $DynamicFieldConfig->{Name} . '_Plain',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
}
}
}
# add action items as js
if ( $Article{ActionItems} ) {
# replace TT directives from string with values
for my $ActionItem ( @{ $Article{ActionItems} } ) {
$ActionItem->{Link} = $LayoutObject->Output(
Template => $ActionItem->{Link},
Data => {
TicketID => $Article{TicketID},
},
);
}
$ActionRowTickets{ $Article{TicketID} } = $LayoutObject->JSONEncode( Data => $Article{ActionItems} );
}
}
# send data to JS
$LayoutObject->AddJSData(
Key => 'ActionRowTickets',
Value => \%ActionRowTickets,
);
# set column filter form, to correctly fill the column filters is necessary to pass each
# overview some information in the AJAX call, for example the fixed Filters or NavBarFilters
# and also other values like the Queue in AgentTicketQueue, otherwise the filters will be
# filled with default restrictions, resulting in more options than the ones that the
# available tickets should provide, see Bug#9902
if ( IsHashRefWithData( $Param{ColumnFilterForm} ) ) {
$LayoutObject->Block(
Name => 'DocumentColumnFilterForm',
Data => {},
);
for my $Element ( sort keys %{ $Param{ColumnFilterForm} } ) {
$LayoutObject->Block(
Name => 'DocumentColumnFilterFormElement',
Data => {
ElementName => $Element,
ElementValue => $Param{ColumnFilterForm}->{$Element},
},
);
}
}
# use template
my $Output = $LayoutObject->Output(
TemplateFile => 'AgentTicketOverviewSmall',
Data => {
%Param,
Type => $Self->{ViewType},
},
);
return $Output;
}
sub _GetColumnValues {
my ( $Self, %Param ) = @_;
return if !IsStringWithData( $Param{HeaderColumn} );
my $HeaderColumn = $Param{HeaderColumn};
my %ColumnFilterValues;
my $TicketIDs;
if ( IsArrayRefWithData( $Param{OriginalTicketIDs} ) ) {
$TicketIDs = $Param{OriginalTicketIDs};
}
if ( $HeaderColumn !~ m/^DynamicField_/ ) {
my $FunctionName = $HeaderColumn . 'FilterValuesGet';
if ( $HeaderColumn eq 'CustomerID' ) {
$FunctionName = 'CustomerFilterValuesGet';
}
$ColumnFilterValues{$HeaderColumn} = $Kernel::OM->Get('Kernel::System::Ticket::ColumnFilter')->$FunctionName(
TicketIDs => $TicketIDs,
HeaderColumn => $HeaderColumn,
UserID => $Self->{UserID},
);
}
else {
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $FieldName = 'DynamicField_' . $DynamicFieldConfig->{Name};
next DYNAMICFIELD if $FieldName ne $HeaderColumn;
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
my $IsFiltrable = $DynamicFieldBackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsFiltrable',
);
next DYNAMICFIELD if !$IsFiltrable;
$Self->{ValidFilterableColumns}->{$HeaderColumn} = $IsFiltrable;
if ( IsArrayRefWithData($TicketIDs) ) {
# get the historical values for the field
$ColumnFilterValues{$HeaderColumn} = $DynamicFieldBackendObject->ColumnFilterValuesGet(
DynamicFieldConfig => $DynamicFieldConfig,
LayoutObject => $Kernel::OM->Get('Kernel::Output::HTML::Layout'),
TicketIDs => $TicketIDs,
);
}
else {
# get PossibleValues
$ColumnFilterValues{$HeaderColumn} = $DynamicFieldBackendObject->PossibleValuesGet(
DynamicFieldConfig => $DynamicFieldConfig,
);
}
last DYNAMICFIELD;
}
}
return \%ColumnFilterValues;
}
sub _InitialColumnFilter {
my ( $Self, %Param ) = @_;
return if !$Param{ColumnName};
return if !$Self->{ValidFilterableColumns}->{ $Param{ColumnName} };
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $Label = $Param{Label} || $Param{ColumnName};
$Label = $LayoutObject->{LanguageObject}->Translate($Label);
# set fixed values
my $Data = [
{
Key => '',
Value => uc $Label,
},
];
# define if column filter values should be translatable
my $TranslationOption = 0;
if (
$Param{ColumnName} eq 'State'
|| $Param{ColumnName} eq 'Lock'
|| $Param{ColumnName} eq 'Priority'
)
{
$TranslationOption = 1;
}
my $Class = 'ColumnFilter';
if ( $Param{Css} ) {
$Class .= ' ' . $Param{Css};
}
# build select HTML
my $ColumnFilterHTML = $LayoutObject->BuildSelection(
Name => 'ColumnFilter' . $Param{ColumnName},
Data => $Data,
Class => $Class,
Translation => $TranslationOption,
SelectedID => '',
);
return $ColumnFilterHTML;
}
sub FilterContent {
my ( $Self, %Param ) = @_;
return if !$Param{HeaderColumn};
my $HeaderColumn = $Param{HeaderColumn};
# get column values for to build the filters later
my $ColumnValues = $Self->_GetColumnValues(
OriginalTicketIDs => $Param{OriginalTicketIDs},
HeaderColumn => $HeaderColumn,
);
my $SelectedValue = '';
my $SelectedColumn = $HeaderColumn;
if ( $HeaderColumn eq 'CustomerUserID' ) {
$SelectedColumn = 'CustomerUserLogin';
}
if ( $HeaderColumn eq 'CustomerID' ) {
$SelectedColumn = 'CustomerID';
}
elsif ( $HeaderColumn !~ m{ \A DynamicField_ }xms ) {
$SelectedColumn .= 'IDs';
}
my $LabelColumn = $HeaderColumn;
if ( $LabelColumn =~ m{ \A DynamicField_ }xms ) {
my $DynamicFieldConfig;
$LabelColumn =~ s{\A DynamicField_ }{}xms;
DYNAMICFIELD:
for my $DFConfig ( @{ $Self->{DynamicField} } ) {
next DYNAMICFIELD if !IsHashRefWithData($DFConfig);
next DYNAMICFIELD if $DFConfig->{Name} ne $LabelColumn;
$DynamicFieldConfig = $DFConfig;
last DYNAMICFIELD;
}
if ( IsHashRefWithData($DynamicFieldConfig) ) {
$LabelColumn = $DynamicFieldConfig->{Label};
}
}
if ( $SelectedColumn && $Self->{StoredFilters}->{$SelectedColumn} ) {
if ( IsArrayRefWithData( $Self->{StoredFilters}->{$SelectedColumn} ) ) {
$SelectedValue = $Self->{StoredFilters}->{$SelectedColumn}->[0];
}
elsif ( IsHashRefWithData( $Self->{StoredFilters}->{$SelectedColumn} ) ) {
$SelectedValue = $Self->{StoredFilters}->{$SelectedColumn}->{Equals};
}
}
# variable to save the filter's HTML code
my $ColumnFilterJSON = $Self->_ColumnFilterJSON(
ColumnName => $HeaderColumn,
Label => $LabelColumn,
ColumnValues => $ColumnValues->{$HeaderColumn},
SelectedValue => $SelectedValue,
);
return $ColumnFilterJSON;
}
# =head2 _ColumnFilterJSON()
# creates a JSON select filter for column header
# my $ColumnFilterJSON = $TicketOverviewSmallObject->_ColumnFilterJSON(
# ColumnName => 'Queue',
# Label => 'Queue',
# ColumnValues => {
# 1 => 'PostMaster',
# 2 => 'Junk',
# },
# SelectedValue '1',
# );
# =cut
sub _ColumnFilterJSON {
my ( $Self, %Param ) = @_;
if (
!$Self->{AvailableFilterableColumns}->{ $Param{ColumnName} } &&
!$Self->{AvailableFilterableColumns}->{ $Param{ColumnName} . 'IDs' }
)
{
return;
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $Label = $Param{Label};
$Label =~ s{ \A DynamicField_ }{}gxms;
$Label = $LayoutObject->{LanguageObject}->Translate($Label);
# set fixed values
my $Data = [
{
Key => 'DeleteFilter',
Value => uc $Label,
},
{
Key => '-',
Value => '-',
Disabled => 1,
},
];
if ( $Param{ColumnValues} && ref $Param{ColumnValues} eq 'HASH' ) {
my %Values = %{ $Param{ColumnValues} };
# Keys must be link encoded for dynamic fields because they are added to URL during filtering
# and can contain characters like '&', ';', etc.
# See bug#14497 - https://bugs.otrs.org/show_bug.cgi?id=14497.
my $Encoding = ( $Param{ColumnName} =~ m/^DynamicField_/ ) ? 1 : 0;
# Set possible values.
for my $ValueKey ( sort { lc $Values{$a} cmp lc $Values{$b} } keys %Values ) {
push @{$Data}, {
Key => $Encoding ? $LayoutObject->LinkEncode($ValueKey) : $ValueKey,
Value => $Values{$ValueKey},
};
}
}
# define if column filter values should be translatable
my $TranslationOption = 0;
if (
$Param{ColumnName} eq 'State'
|| $Param{ColumnName} eq 'Lock'
|| $Param{ColumnName} eq 'Priority'
)
{
$TranslationOption = 1;
}
# build select HTML
my $JSON = $LayoutObject->BuildSelectionJSON(
[
{
Name => 'ColumnFilter' . $Param{ColumnName},
Data => $Data,
Class => 'ColumnFilter',
Sort => 'AlphanumericKey',
TreeView => 1,
SelectedID => $Param{SelectedValue},
Translation => $TranslationOption,
AutoComplete => 'off',
},
],
);
return $JSON;
}
sub _DefaultColumnSort {
my %DefaultColumns = (
TicketNumber => 100,
Age => 110,
Changed => 111,
PendingTime => 112,
EscalationTime => 113,
EscalationSolutionTime => 114,
EscalationResponseTime => 115,
EscalationUpdateTime => 116,
Sender => 120,
Title => 122,
Subject => 124,
State => 130,
Lock => 140,
Queue => 150,
Owner => 160,
Responsible => 161,
CustomerID => 170,
CustomerName => 171,
CustomerUserID => 172,
Type => 180,
Service => 191,
SLA => 192,
Priority => 193,
);
# dynamic fields can not be on the DefaultColumns sorting hash
# when comparing 2 dynamic fields sorting must be alphabetical
if ( !$DefaultColumns{$a} && !$DefaultColumns{$b} ) {
return $a cmp $b;
}
# when a dynamic field is compared to a ticket attribute it must be higher
elsif ( !$DefaultColumns{$a} ) {
return 1;
}
# when a ticket attribute is compared to a dynamic field it must be lower
elsif ( !$DefaultColumns{$b} ) {
return -1;
}
# otherwise do a numerical comparison with the ticket attributes
return $DefaultColumns{$a} <=> $DefaultColumns{$b};
}
1;