2612 lines
93 KiB
Perl
2612 lines
93 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::Dashboard::TicketGeneric;
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Kernel::System::VariableCheck qw(:all);
|
|
use Kernel::Language qw(Translatable);
|
|
|
|
our $ObjectManagerDisabled = 1;
|
|
|
|
sub new {
|
|
my ( $Type, %Param ) = @_;
|
|
|
|
# allocate new hash for object
|
|
my $Self = {%Param};
|
|
bless( $Self, $Type );
|
|
|
|
# get needed parameters
|
|
for my $Needed (qw(Config Name UserID)) {
|
|
die "Got no $Needed!" if ( !$Self->{$Needed} );
|
|
}
|
|
|
|
# get param object
|
|
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
|
|
|
my $RemoveFilters = $ParamObject->GetParam( Param => 'RemoveFilters' ) || $Param{RemoveFilters} || 0;
|
|
|
|
# get sorting params
|
|
for my $Item (qw(SortBy OrderBy)) {
|
|
$Self->{$Item} = $ParamObject->GetParam( Param => $Item ) || $Param{$Item};
|
|
}
|
|
|
|
# Get add filters param.
|
|
$Self->{AddFilters} = $ParamObject->GetParam( Param => 'AddFilters' ) || $Param{AddFilters} || 0;
|
|
$Self->{TabAction} = $ParamObject->GetParam( Param => 'TabAction' ) || $Param{TabAction} || 0;
|
|
|
|
# Get previous sorting column.
|
|
$Self->{SortingColumn} = $ParamObject->GetParam( Param => 'SortingColumn' ) || $Param{SortingColumn};
|
|
|
|
# set filter settings
|
|
for my $Item (qw(ColumnFilter GetColumnFilter GetColumnFilterSelect)) {
|
|
$Self->{$Item} = $Param{$Item};
|
|
}
|
|
|
|
# save column filters
|
|
$Self->{PrefKeyColumnFilters} = 'UserDashboardTicketGenericColumnFilters' . $Self->{Name};
|
|
$Self->{PrefKeyColumnFiltersRealKeys} = 'UserDashboardTicketGenericColumnFiltersRealKeys' . $Self->{Name};
|
|
|
|
# get needed objects
|
|
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
|
|
|
if ($RemoveFilters) {
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $Self->{PrefKeyColumnFilters},
|
|
Value => '',
|
|
);
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $Self->{PrefKeyColumnFiltersRealKeys},
|
|
Value => '',
|
|
);
|
|
}
|
|
|
|
# just in case new filter values arrive
|
|
elsif (
|
|
IsHashRefWithData( $Self->{GetColumnFilter} )
|
|
&& IsHashRefWithData( $Self->{GetColumnFilterSelect} )
|
|
&& IsHashRefWithData( $Self->{ColumnFilter} )
|
|
)
|
|
{
|
|
|
|
if ( !$ConfigObject->Get('DemoSystem') ) {
|
|
|
|
# check if the user has filter preferences for this widget
|
|
my %Preferences = $UserObject->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
my $ColumnPrefValues;
|
|
if ( $Preferences{ $Self->{PrefKeyColumnFilters} } ) {
|
|
$ColumnPrefValues = $JSONObject->Decode(
|
|
Data => $Preferences{ $Self->{PrefKeyColumnFilters} },
|
|
);
|
|
}
|
|
|
|
PREFVALUES:
|
|
for my $Column ( sort keys %{ $Self->{GetColumnFilterSelect} } ) {
|
|
if ( $Self->{GetColumnFilterSelect}->{$Column} eq 'DeleteFilter' ) {
|
|
delete $ColumnPrefValues->{$Column};
|
|
next PREFVALUES;
|
|
}
|
|
$ColumnPrefValues->{$Column} = $Self->{GetColumnFilterSelect}->{$Column};
|
|
}
|
|
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $Self->{PrefKeyColumnFilters},
|
|
Value => $JSONObject->Encode( Data => $ColumnPrefValues ),
|
|
);
|
|
|
|
# save real key's name
|
|
my $ColumnPrefRealKeysValues;
|
|
if ( $Preferences{ $Self->{PrefKeyColumnFiltersRealKeys} } ) {
|
|
$ColumnPrefRealKeysValues = $JSONObject->Decode(
|
|
Data => $Preferences{ $Self->{PrefKeyColumnFiltersRealKeys} },
|
|
);
|
|
}
|
|
REALKEYVALUES:
|
|
for my $Column ( sort keys %{ $Self->{ColumnFilter} } ) {
|
|
next REALKEYVALUES if !$Column;
|
|
|
|
my $DeleteFilter = 0;
|
|
if ( IsArrayRefWithData( $Self->{ColumnFilter}->{$Column} ) ) {
|
|
if ( grep { $_ eq 'DeleteFilter' } @{ $Self->{ColumnFilter}->{$Column} } ) {
|
|
$DeleteFilter = 1;
|
|
}
|
|
}
|
|
elsif ( IsHashRefWithData( $Self->{ColumnFilter}->{$Column} ) ) {
|
|
|
|
if (
|
|
grep { $Self->{ColumnFilter}->{$Column}->{$_} eq 'DeleteFilter' }
|
|
keys %{ $Self->{ColumnFilter}->{$Column} }
|
|
)
|
|
{
|
|
$DeleteFilter = 1;
|
|
}
|
|
}
|
|
|
|
if ($DeleteFilter) {
|
|
delete $ColumnPrefRealKeysValues->{$Column};
|
|
delete $Self->{ColumnFilter}->{$Column};
|
|
next REALKEYVALUES;
|
|
}
|
|
$ColumnPrefRealKeysValues->{$Column} = $Self->{ColumnFilter}->{$Column};
|
|
}
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $Self->{PrefKeyColumnFiltersRealKeys},
|
|
Value => $JSONObject->Encode( Data => $ColumnPrefRealKeysValues ),
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
# check if the user has filter preferences for this widget
|
|
my %Preferences = $UserObject->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# get column names from Preferences
|
|
my $PreferencesColumnFilters;
|
|
if ( $Preferences{ $Self->{PrefKeyColumnFilters} } ) {
|
|
$PreferencesColumnFilters = $JSONObject->Decode(
|
|
Data => $Preferences{ $Self->{PrefKeyColumnFilters} },
|
|
);
|
|
}
|
|
|
|
if ($PreferencesColumnFilters) {
|
|
$Self->{GetColumnFilterSelect} = $PreferencesColumnFilters;
|
|
my @ColumnFilters = keys %{$PreferencesColumnFilters}; ## no critic
|
|
for my $Field (@ColumnFilters) {
|
|
$Self->{GetColumnFilter}->{ $Field . $Self->{Name} } = $PreferencesColumnFilters->{$Field};
|
|
}
|
|
}
|
|
|
|
# get column real names from Preferences
|
|
my $PreferencesColumnFiltersRealKeys;
|
|
if ( $Preferences{ $Self->{PrefKeyColumnFiltersRealKeys} } ) {
|
|
$PreferencesColumnFiltersRealKeys = $JSONObject->Decode(
|
|
Data => $Preferences{ $Self->{PrefKeyColumnFiltersRealKeys} },
|
|
);
|
|
}
|
|
|
|
if ($PreferencesColumnFiltersRealKeys) {
|
|
my @ColumnFiltersReal = keys %{$PreferencesColumnFiltersRealKeys}; ## no critic
|
|
for my $Field (@ColumnFiltersReal) {
|
|
$Self->{ColumnFilter}->{$Field} = $PreferencesColumnFiltersRealKeys->{$Field};
|
|
}
|
|
}
|
|
|
|
# get current filter
|
|
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
|
my $PreferencesKey = 'UserDashboardTicketGenericFilter' . $Self->{Name};
|
|
my $AdditionalPreferencesKey = 'UserDashboardTicketGenericAdditionalFilter' . $Self->{Name};
|
|
if ( $Self->{Name} eq $Name ) {
|
|
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
|
|
$Self->{AdditionalFilter} = $ParamObject->GetParam( Param => 'AdditionalFilter' ) || '';
|
|
}
|
|
|
|
# Remember the selected filter in the session.
|
|
if ( $Self->{Filter} ) {
|
|
|
|
# update session
|
|
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
|
SessionID => $Self->{SessionID},
|
|
Key => $PreferencesKey,
|
|
Value => $Self->{Filter},
|
|
);
|
|
|
|
# update preferences
|
|
if ( !$ConfigObject->Get('DemoSystem') ) {
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $PreferencesKey,
|
|
Value => $Self->{Filter},
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
$Self->{Filter} = $Self->{$PreferencesKey} || $Self->{Config}->{Filter} || 'All';
|
|
}
|
|
|
|
# The additional filter are at the moment only relevant for the customer user information center.
|
|
if ( $Self->{Action} eq 'AgentCustomerUserInformationCenter' ) {
|
|
|
|
# Remember the selected filter in the session.
|
|
if ( $Self->{AdditionalFilter} ) {
|
|
|
|
# update session
|
|
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
|
|
SessionID => $Self->{SessionID},
|
|
Key => $AdditionalPreferencesKey,
|
|
Value => $Self->{AdditionalFilter},
|
|
);
|
|
|
|
# update preferences
|
|
if ( !$ConfigObject->Get('DemoSystem') ) {
|
|
$UserObject->SetPreferences(
|
|
UserID => $Self->{UserID},
|
|
Key => $AdditionalPreferencesKey,
|
|
Value => $Self->{AdditionalFilter},
|
|
);
|
|
}
|
|
}
|
|
else {
|
|
$Self->{AdditionalFilter}
|
|
= $Self->{$AdditionalPreferencesKey} || $Self->{Config}->{AdditionalFilter} || 'AssignedToCustomerUser';
|
|
}
|
|
}
|
|
|
|
$Self->{PrefKeyShown} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
|
$Self->{PrefKeyColumns} = 'UserDashboardPref' . $Self->{Name} . '-Columns';
|
|
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyShown} }
|
|
|| $Self->{Config}->{Limit};
|
|
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
|
|
|
# define filterable columns
|
|
$Self->{ValidFilterableColumns} = {
|
|
'Owner' => 1,
|
|
'Responsible' => 1,
|
|
'CustomerID' => 1,
|
|
'CustomerUserID' => 1,
|
|
'State' => 1,
|
|
'Queue' => 1,
|
|
'Priority' => 1,
|
|
'Type' => 1,
|
|
'Lock' => 1,
|
|
'Service' => 1,
|
|
'SLA' => 1,
|
|
};
|
|
|
|
# hash with all valid sortable columns (taken from TicketSearch)
|
|
# SortBy => 'Age', # Created|Owner|Responsible|CustomerID|State|TicketNumber|Queue
|
|
# |Priority|Type|Lock|Title|Service|SLA|Changed|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,
|
|
};
|
|
|
|
# remove CustomerID if Customer Information Center
|
|
if ( $Self->{Action} eq 'AgentCustomerInformationCenter' ) {
|
|
delete $Self->{ColumnFilter}->{CustomerID};
|
|
delete $Self->{GetColumnFilter}->{CustomerID};
|
|
delete $Self->{GetColumnFilterSelect}->{CustomerID};
|
|
delete $Self->{ValidFilterableColumns}->{CustomerID};
|
|
delete $Self->{ValidSortableColumns}->{CustomerID};
|
|
}
|
|
elsif (
|
|
$Self->{Action} eq 'AgentCustomerUserInformationCenter'
|
|
&& $Self->{AdditionalFilter} eq 'AssignedToCustomerUser'
|
|
)
|
|
{
|
|
|
|
for my $DeleteColumnFilter (qw(CustomerUserLogin CustomerUserLoginRaw)) {
|
|
delete $Self->{ColumnFilter}->{$DeleteColumnFilter};
|
|
delete $Self->{GetColumnFilter}->{$DeleteColumnFilter};
|
|
}
|
|
delete $Self->{GetColumnFilter}->{CustomerUserID};
|
|
delete $Self->{GetColumnFilterSelect}->{CustomerUserID};
|
|
delete $Self->{ValidFilterableColumns}->{CustomerUserID};
|
|
}
|
|
|
|
$Self->{UseTicketService} = $ConfigObject->Get('Ticket::Service') || 0;
|
|
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
|
|
# get process management configuration
|
|
$Self->{ProcessManagementProcessID}
|
|
= $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementProcessID');
|
|
$Self->{ProcessManagementActivityID}
|
|
= $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementActivityID');
|
|
|
|
# get the list of processes in the system
|
|
my $ProcessListHash = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process')->ProcessList(
|
|
ProcessState => [ 'Active', 'FadeAway', 'Inactive' ],
|
|
Interface => 'all',
|
|
Silent => 1,
|
|
);
|
|
|
|
# use only the process EntityIDs
|
|
@{ $Self->{ProcessList} } = sort keys %{$ProcessListHash};
|
|
}
|
|
|
|
return $Self;
|
|
}
|
|
|
|
sub Preferences {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# configure columns
|
|
my @ColumnsEnabled;
|
|
my @ColumnsAvailable;
|
|
my @ColumnsAvailableNotEnabled;
|
|
|
|
# check for default settings
|
|
if (
|
|
$Self->{Config}->{DefaultColumns}
|
|
&& IsHashRefWithData( $Self->{Config}->{DefaultColumns} )
|
|
)
|
|
{
|
|
@ColumnsAvailable = grep { $Self->{Config}->{DefaultColumns}->{$_} }
|
|
keys %{ $Self->{Config}->{DefaultColumns} };
|
|
@ColumnsEnabled = grep { $Self->{Config}->{DefaultColumns}->{$_} eq '2' }
|
|
sort { $Self->_DefaultColumnSort() } keys %{ $Self->{Config}->{DefaultColumns} };
|
|
}
|
|
|
|
# check if the user has filter preferences for this widget
|
|
my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# get JSON object
|
|
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
|
|
|
|
# if preference settings are available, take them
|
|
if ( $Preferences{ $Self->{PrefKeyColumns} } ) {
|
|
|
|
my $ColumnsEnabled = $JSONObject->Decode(
|
|
Data => $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyColumns} },
|
|
);
|
|
|
|
@ColumnsEnabled = grep { $ColumnsEnabled->{Columns}->{$_} == 1 }
|
|
keys %{ $ColumnsEnabled->{Columns} };
|
|
|
|
if ( $ColumnsEnabled->{Order} && @{ $ColumnsEnabled->{Order} } ) {
|
|
@ColumnsEnabled = @{ $ColumnsEnabled->{Order} };
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
my %Columns;
|
|
for my $ColumnName ( sort { $a cmp $b } @ColumnsAvailable ) {
|
|
$Columns{Columns}->{$ColumnName} = ( grep { $ColumnName eq $_ } @ColumnsEnabled ) ? 1 : 0;
|
|
if ( !grep { $_ eq $ColumnName } @ColumnsEnabled ) {
|
|
push @ColumnsAvailableNotEnabled, $ColumnName;
|
|
}
|
|
}
|
|
|
|
# remove CustomerID if Customer Information Center
|
|
if ( $Self->{Action} eq 'AgentCustomerInformationCenter' ) {
|
|
delete $Columns{Columns}->{CustomerID};
|
|
@ColumnsEnabled = grep { $_ ne 'CustomerID' } @ColumnsEnabled;
|
|
@ColumnsAvailableNotEnabled = grep { $_ ne 'CustomerID' } @ColumnsAvailableNotEnabled;
|
|
}
|
|
|
|
my @Params = (
|
|
{
|
|
Desc => Translatable('Shown Tickets'),
|
|
Name => $Self->{PrefKeyShown},
|
|
Block => 'Option',
|
|
Data => {
|
|
5 => ' 5',
|
|
10 => '10',
|
|
15 => '15',
|
|
20 => '20',
|
|
25 => '25',
|
|
50 => '50',
|
|
},
|
|
SelectedID => $Self->{PageShown},
|
|
Translation => 0,
|
|
},
|
|
{
|
|
Desc => Translatable('Shown Columns'),
|
|
Name => $Self->{PrefKeyColumns},
|
|
Block => 'AllocationList',
|
|
Columns => $JSONObject->Encode( Data => \%Columns ),
|
|
ColumnsEnabled => $JSONObject->Encode( Data => \@ColumnsEnabled ),
|
|
ColumnsAvailable => $JSONObject->Encode( Data => \@ColumnsAvailableNotEnabled ),
|
|
Translation => 1,
|
|
},
|
|
);
|
|
|
|
return @Params;
|
|
}
|
|
|
|
sub Config {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# check if frontend module of link is used
|
|
if ( $Self->{Config}->{Link} && $Self->{Config}->{Link} =~ /Action=(.+?)([&;].+?|)$/ ) {
|
|
my $Action = $1;
|
|
if ( !$Kernel::OM->Get('Kernel::Config')->Get('Frontend::Module')->{$Action} ) {
|
|
$Self->{Config}->{Link} = '';
|
|
}
|
|
}
|
|
|
|
return (
|
|
%{ $Self->{Config} },
|
|
|
|
# Don't cache this globally as it contains JS that is not inside of the HTML.
|
|
CacheTTL => undef,
|
|
CacheKey => undef,
|
|
);
|
|
}
|
|
|
|
sub FilterContent {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
return if !$Param{FilterColumn};
|
|
|
|
my $TicketIDs;
|
|
my $HeaderColumn = $Param{FilterColumn};
|
|
my @OriginalViewableTickets;
|
|
|
|
if (
|
|
$Kernel::OM->Get('Kernel::Config')->Get('OnlyValuesOnTicket')
|
|
|| $HeaderColumn eq 'CustomerID'
|
|
|| $HeaderColumn eq 'CustomerUserID'
|
|
)
|
|
{
|
|
my %SearchParams = $Self->_SearchParamsGet(%Param);
|
|
my %TicketSearch = %{ $SearchParams{TicketSearch} };
|
|
my %TicketSearchSummary = %{ $SearchParams{TicketSearchSummary} };
|
|
|
|
# add process management search terms
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
$TicketSearch{ 'DynamicField_' . $Self->{ProcessManagementProcessID} } = {
|
|
Like => $Self->{ProcessList},
|
|
};
|
|
}
|
|
|
|
# Add the additional filter to the ticket search param.
|
|
if ( $Self->{AdditionalFilter} ) {
|
|
%TicketSearch = (
|
|
%TicketSearch,
|
|
%{ $TicketSearchSummary{ $Self->{AdditionalFilter} } },
|
|
);
|
|
}
|
|
|
|
if ( !$Self->{Config}->{IsProcessWidget} || IsArrayRefWithData( $Self->{ProcessList} ) ) {
|
|
@OriginalViewableTickets = $Kernel::OM->Get('Kernel::System::Ticket')->TicketSearch(
|
|
%TicketSearch,
|
|
%{ $TicketSearchSummary{ $Self->{Filter} } },
|
|
Result => 'ARRAY',
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( $HeaderColumn =~ m/^DynamicField_/ && !defined $Self->{DynamicField} ) {
|
|
|
|
# get the dynamic fields for this screen
|
|
$Self->{DynamicField} = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
|
Valid => 0,
|
|
ObjectType => ['Ticket'],
|
|
);
|
|
}
|
|
|
|
# get column values for to build the filters later
|
|
my $ColumnValues = $Self->_GetColumnValues(
|
|
OriginalTicketIDs => \@OriginalViewableTickets,
|
|
HeaderColumn => $HeaderColumn,
|
|
);
|
|
|
|
# make sure that even a value of 0 is passed as a Selected value, e.g. Unchecked value of a
|
|
# check-box dynamic field.
|
|
my $SelectedValue = defined $Self->{GetColumnFilter}->{ $HeaderColumn . $Self->{Name} }
|
|
? $Self->{GetColumnFilter}->{ $HeaderColumn . $Self->{Name} }
|
|
: '';
|
|
|
|
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};
|
|
}
|
|
}
|
|
|
|
# variable to save the filter's HTML code
|
|
my $ColumnFilterJSON = $Self->_ColumnFilterJSON(
|
|
ColumnName => $HeaderColumn,
|
|
Label => $LabelColumn,
|
|
ColumnValues => $ColumnValues->{$HeaderColumn},
|
|
SelectedValue => $SelectedValue,
|
|
DashboardName => $Self->{Name},
|
|
);
|
|
|
|
return $ColumnFilterJSON;
|
|
|
|
}
|
|
|
|
sub Run {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %SearchParams = $Self->_SearchParamsGet(%Param);
|
|
my @Columns = @{ $SearchParams{Columns} };
|
|
my %TicketSearch = %{ $SearchParams{TicketSearch} };
|
|
my %TicketSearchSummary = %{ $SearchParams{TicketSearchSummary} };
|
|
|
|
# Add the additional filter to the ticket search param.
|
|
if ( $Self->{AdditionalFilter} ) {
|
|
%TicketSearch = (
|
|
%TicketSearch,
|
|
%{ $TicketSearchSummary{ $Self->{AdditionalFilter} } },
|
|
);
|
|
}
|
|
|
|
my $CacheKey = join '-', $Self->{Name}, $Self->{Action}, $Self->{PageShown}, $Self->{StartHit}, $Self->{UserID};
|
|
my $CacheColumns = join(
|
|
',',
|
|
map { $_ . '=>' . $Self->{GetColumnFilterSelect}->{$_} } sort keys %{ $Self->{GetColumnFilterSelect} }
|
|
);
|
|
$CacheKey .= '-' . $CacheColumns if $CacheColumns;
|
|
|
|
# If SortBy parameter is not defined, set to value from %TicketSearch, otherwise set to default value 'Age'.
|
|
if ( !defined $Self->{SortBy} ) {
|
|
if ( defined $TicketSearch{SortBy} && $Self->{ValidSortableColumns}->{ $TicketSearch{SortBy} } ) {
|
|
$Self->{SortBy} = $TicketSearch{SortBy};
|
|
}
|
|
else {
|
|
$Self->{SortBy} = 'Age';
|
|
}
|
|
}
|
|
|
|
$CacheKey .= '-' . $Self->{SortBy} if defined $Self->{SortBy};
|
|
|
|
# Set OrderBy parameter to the search.
|
|
my $IsCacheForUse = 0;
|
|
if ( $Self->{OrderBy} ) {
|
|
if (
|
|
$Self->{AddFilters}
|
|
|| $Self->{TabAction}
|
|
|| !defined $Self->{SortingColumn}
|
|
|| $Self->{SortingColumn} ne $Self->{SortBy}
|
|
)
|
|
{
|
|
$TicketSearch{OrderBy} = $Self->{OrderBy};
|
|
$IsCacheForUse = 1;
|
|
}
|
|
else {
|
|
$TicketSearch{OrderBy} = $Self->{OrderBy} eq 'Up' ? 'Down' : 'Up';
|
|
}
|
|
}
|
|
|
|
# Set order for blocks.
|
|
$TicketSearch{OrderBy} = $TicketSearch{OrderBy} || 'Down';
|
|
|
|
# Set previous sorting column parameter for all columns.
|
|
$Param{SortingColumn} = $Self->{SortBy};
|
|
|
|
$CacheKey .= '-' . $TicketSearch{OrderBy} if defined $TicketSearch{OrderBy};
|
|
|
|
# CustomerInformationCenter shows data per CustomerID
|
|
if ( $Param{CustomerID} ) {
|
|
$CacheKey .= '-' . $Param{CustomerID};
|
|
}
|
|
|
|
# CustomerUserInformationCenter shows data per CustomerUserID
|
|
if ( $Param{CustomerUserID} ) {
|
|
$CacheKey .= '-' . $Param{CustomerUserID};
|
|
}
|
|
|
|
# Add the additional filter always to the cache key, if a additional filter exists.
|
|
if ( $Self->{AdditionalFilter} ) {
|
|
$CacheKey .= '-' . $Self->{AdditionalFilter};
|
|
}
|
|
|
|
# get cache object
|
|
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
|
|
|
# check cache
|
|
my $TicketIDs = $CacheObject->Get(
|
|
Type => 'Dashboard',
|
|
Key => $CacheKey . '-' . $Self->{Filter} . '-List',
|
|
);
|
|
|
|
# find and show ticket list
|
|
my $CacheUsed = 1;
|
|
|
|
# get ticket object
|
|
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
|
|
|
if ( !$TicketIDs ) {
|
|
|
|
# quote all CustomerIDs
|
|
if ( $TicketSearch{CustomerID} ) {
|
|
$TicketSearch{CustomerID} = $Kernel::OM->Get('Kernel::System::DB')->QueryStringEscape(
|
|
QueryString => $TicketSearch{CustomerID},
|
|
);
|
|
}
|
|
|
|
# add sort by parameter to the search
|
|
if ( !defined $TicketSearch{SortBy} || !$Self->{ValidSortableColumns}->{ $TicketSearch{SortBy} } ) {
|
|
if ( $Self->{SortBy} && $Self->{ValidSortableColumns}->{ $Self->{SortBy} } ) {
|
|
$TicketSearch{SortBy} = $Self->{SortBy};
|
|
}
|
|
else {
|
|
$TicketSearch{SortBy} = 'Age';
|
|
}
|
|
}
|
|
|
|
$CacheUsed = $IsCacheForUse ? 1 : 0;
|
|
|
|
# add process management search terms
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
$TicketSearch{ 'DynamicField_' . $Self->{ProcessManagementProcessID} } = {
|
|
Like => $Self->{ProcessList},
|
|
};
|
|
}
|
|
|
|
my @TicketIDsArray;
|
|
if ( !$Self->{Config}->{IsProcessWidget} || IsArrayRefWithData( $Self->{ProcessList} ) ) {
|
|
|
|
# Copy original column filter.
|
|
my %ColumnFilter = %{ $Self->{ColumnFilter} || {} };
|
|
|
|
# Change filter name accordingly.
|
|
my $Filter;
|
|
if ( $Self->{Filter} eq 'MyQueues' ) {
|
|
$Filter = 'QueueIDs';
|
|
}
|
|
elsif ( $Self->{Filter} eq 'MyServices' ) {
|
|
$Filter = 'ServiceIDs';
|
|
|
|
if ( $ColumnFilter{QueueIDs} ) {
|
|
$TicketSearchSummary{ $Self->{Filter} }->{QueueIDs} = $ColumnFilter{QueueIDs};
|
|
}
|
|
}
|
|
elsif ( $Self->{Filter} eq 'Responsible' ) {
|
|
$Filter = 'ResponsibleIDs';
|
|
}
|
|
elsif ( $Self->{Filter} eq 'Locked' ) {
|
|
$Filter = 'LockIDs';
|
|
}
|
|
|
|
# Handle cases for filter columns to preserve filter value in other tab actions.
|
|
if ( $ColumnFilter{LockIDs} ) {
|
|
$TicketSearchSummary{ $Self->{Filter} }->{LockIDs} = $ColumnFilter{LockIDs};
|
|
}
|
|
elsif ( $ColumnFilter{OwnerIDs} ) {
|
|
$TicketSearchSummary{ $Self->{Filter} }->{OwnerIDs} = $ColumnFilter{OwnerIDs};
|
|
}
|
|
|
|
# Filter is used and is not in user prefered values, show no results.
|
|
# See bug#12808 ( https://bugs.otrs.org/show_bug.cgi?id=12808 ).
|
|
if (
|
|
$Filter
|
|
&& IsArrayRefWithData( $TicketSearchSummary{ $Self->{Filter} }->{$Filter} )
|
|
&& IsArrayRefWithData( $ColumnFilter{$Filter} )
|
|
&& !grep { $ColumnFilter{$Filter}->[0] == $_ } @{ $TicketSearchSummary{ $Self->{Filter} }->{$Filter} }
|
|
)
|
|
{
|
|
@TicketIDsArray = ();
|
|
}
|
|
|
|
# Execute search.
|
|
else {
|
|
@TicketIDsArray = $TicketObject->TicketSearch(
|
|
Result => 'ARRAY',
|
|
%TicketSearch,
|
|
%{ $TicketSearchSummary{ $Self->{Filter} } },
|
|
%ColumnFilter,
|
|
Limit => $Self->{PageShown} + $Self->{StartHit} - 1,
|
|
);
|
|
}
|
|
}
|
|
$TicketIDs = \@TicketIDsArray;
|
|
}
|
|
|
|
# check cache
|
|
my $Summary = $CacheObject->Get(
|
|
Type => 'Dashboard',
|
|
Key => $CacheKey . '-Summary',
|
|
);
|
|
|
|
# If no cache or new list result, do count lookup.
|
|
if ( !$Summary || !$CacheUsed ) {
|
|
|
|
# Define the summary types for which no count is needed, because we have no output.
|
|
my %LookupNoCountSummaryType = (
|
|
AssignedToCustomerUser => 1,
|
|
AccessibleForCustomerUser => 1,
|
|
);
|
|
|
|
TYPE:
|
|
for my $Type ( sort keys %TicketSearchSummary ) {
|
|
next TYPE if $LookupNoCountSummaryType{$Type};
|
|
next TYPE if !$TicketSearchSummary{$Type};
|
|
|
|
# Copy original column filter.
|
|
my %ColumnFilter = %{ $Self->{ColumnFilter} || {} };
|
|
|
|
# Loop through all column filter elements.
|
|
for my $Element ( sort keys %ColumnFilter ) {
|
|
|
|
# Verify if current column filter element is already present in the ticket search
|
|
# summary, to delete it from the column filter hash.
|
|
if ( $Self->{AdditionalFilter} && $TicketSearchSummary{ $Self->{AdditionalFilter} }->{$Element} ) {
|
|
delete $ColumnFilter{$Element};
|
|
}
|
|
}
|
|
|
|
# add process management search terms
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
$TicketSearch{ 'DynamicField_' . $Self->{ProcessManagementProcessID} } = {
|
|
Like => $Self->{ProcessList},
|
|
};
|
|
}
|
|
|
|
$Summary->{$Type} = 0;
|
|
|
|
if ( !$Self->{Config}->{IsProcessWidget} || IsArrayRefWithData( $Self->{ProcessList} ) ) {
|
|
|
|
# Change filter name accordingly.
|
|
my $Filter;
|
|
if ( $Type eq 'MyQueues' ) {
|
|
$Filter = 'QueueIDs';
|
|
}
|
|
elsif ( $Type eq 'MyServices' ) {
|
|
$Filter = 'ServiceIDs';
|
|
|
|
if ( $ColumnFilter{QueueIDs} ) {
|
|
$TicketSearchSummary{$Type}->{QueueIDs} = $ColumnFilter{QueueIDs};
|
|
}
|
|
}
|
|
elsif ( $Type eq 'Responsible' ) {
|
|
$Filter = 'ResponsibleIDs';
|
|
}
|
|
elsif ( $Type eq 'MyLocks' ) {
|
|
$Filter = 'LockIDs';
|
|
}
|
|
|
|
# Handle cases for filter columns to preserve filter value in other tab actions.
|
|
if ( $ColumnFilter{LockIDs} ) {
|
|
$TicketSearchSummary{$Type}->{LockIDs} = $ColumnFilter{LockIDs};
|
|
}
|
|
elsif ( $ColumnFilter{OwnerIDs} ) {
|
|
$TicketSearchSummary{ $Self->{Filter} }->{OwnerIDs} = $ColumnFilter{OwnerIDs};
|
|
}
|
|
|
|
# Filter is used and is not in user prefered values, show no results.
|
|
# See bug#12808 ( https://bugs.otrs.org/show_bug.cgi?id=12808 ).
|
|
if (
|
|
$Filter
|
|
&& IsArrayRefWithData( $TicketSearchSummary{$Type}->{$Filter} )
|
|
&& IsArrayRefWithData( $ColumnFilter{$Filter} )
|
|
&& !grep { $ColumnFilter{$Filter}->[0] == $_ } @{ $TicketSearchSummary{$Type}->{$Filter} }
|
|
)
|
|
{
|
|
$Summary->{$Type} = 0;
|
|
}
|
|
|
|
# Execute search.
|
|
else {
|
|
$Summary->{$Type} = $TicketObject->TicketSearch(
|
|
Result => 'COUNT',
|
|
%TicketSearch,
|
|
%{ $TicketSearchSummary{$Type} },
|
|
%ColumnFilter,
|
|
) || 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# set cache
|
|
if ( !$CacheUsed && $Self->{Config}->{CacheTTLLocal} ) {
|
|
$CacheObject->Set(
|
|
Type => 'Dashboard',
|
|
Key => $CacheKey . '-Summary',
|
|
Value => $Summary,
|
|
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
|
);
|
|
$CacheObject->Set(
|
|
Type => 'Dashboard',
|
|
Key => $CacheKey . '-' . $Self->{Filter} . '-List',
|
|
Value => $TicketIDs,
|
|
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
|
);
|
|
}
|
|
|
|
# Set the css class for the selected filter and additional filter.
|
|
$Summary->{ $Self->{Filter} . '::Selected' } = 'Selected';
|
|
if ( $Self->{AdditionalFilter} ) {
|
|
$Summary->{ $Self->{AdditionalFilter} . '::Selected' } = 'Selected';
|
|
}
|
|
|
|
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
|
|
|
# get filter ticket counts
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilter',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
|
|
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
|
|
|
# show only AssignedToCustomerUser if we have the filter
|
|
if ( $TicketSearchSummary{AssignedToCustomerUser} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterAssignedToCustomerUser',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show only locked if we have the filter
|
|
if ( $TicketSearchSummary{AccessibleForCustomerUser} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterAccessibleForCustomerUser',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show also watcher if feature is enabled and there is a watcher filter
|
|
if ( $ConfigObject->Get('Ticket::Watcher') && $TicketSearchSummary{Watcher} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterWatcher',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show also responsible if feature is enabled and there is a responsible filter
|
|
if ( $ConfigObject->Get('Ticket::Responsible') && $TicketSearchSummary{Responsible} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterResponsible',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show only my queues if we have the filter
|
|
if ( $TicketSearchSummary{MyQueues} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterMyQueues',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show only my services if we have the filter
|
|
if ( $TicketSearchSummary{MyServices} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterMyServices',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# show only locked if we have the filter
|
|
if ( $TicketSearchSummary{Locked} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterLocked',
|
|
Data => {
|
|
%Param,
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
},
|
|
);
|
|
}
|
|
|
|
# add page nav bar
|
|
my $Total = $Summary->{ $Self->{Filter} } || 0;
|
|
|
|
my %GetColumnFilter = $Self->{GetColumnFilter} ? %{ $Self->{GetColumnFilter} } : ();
|
|
|
|
my $ColumnFilterLink = '';
|
|
COLUMNNAME:
|
|
for my $ColumnName ( sort keys %GetColumnFilter ) {
|
|
next COLUMNNAME if !$ColumnName;
|
|
next COLUMNNAME if !$GetColumnFilter{$ColumnName};
|
|
$ColumnFilterLink
|
|
.= ';' . $LayoutObject->Ascii2Html( Text => 'ColumnFilter' . $ColumnName )
|
|
. '=' . $LayoutObject->Ascii2Html( Text => $GetColumnFilter{$ColumnName} );
|
|
}
|
|
|
|
my $LinkPage =
|
|
'Subaction=Element;Name=' . $Self->{Name}
|
|
. ';Filter=' . $Self->{Filter}
|
|
. ';AdditionalFilter=' . ( $Self->{AdditionalFilter} || '' )
|
|
. ';SortBy=' . ( $Self->{SortBy} || '' )
|
|
. ';OrderBy=' . ( $TicketSearch{OrderBy} || '' )
|
|
. $ColumnFilterLink
|
|
. ';';
|
|
|
|
if ( $Param{CustomerID} ) {
|
|
$LinkPage .= "CustomerID=$Param{CustomerID};";
|
|
}
|
|
if ( $Param{CustomerUserID} ) {
|
|
$LinkPage .= "CustomerUserID=$Param{CustomerUserID};";
|
|
}
|
|
|
|
my %PageNav = $LayoutObject->PageNavBar(
|
|
StartHit => $Self->{StartHit},
|
|
PageShown => $Self->{PageShown},
|
|
AllHits => $Total || 1,
|
|
Action => 'Action=' . $LayoutObject->{Action},
|
|
Link => $LinkPage,
|
|
AJAXReplace => 'Dashboard' . $Self->{Name},
|
|
IDPrefix => 'Dashboard' . $Self->{Name},
|
|
AJAX => $Param{AJAX},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericFilterNavBar',
|
|
Data => {
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%PageNav,
|
|
},
|
|
);
|
|
|
|
# show table header
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeader',
|
|
Data => {},
|
|
);
|
|
|
|
# define which meta items will be shown
|
|
my @MetaItems = $LayoutObject->TicketMetaItemsCount();
|
|
|
|
# remove (-) from name for JS config
|
|
my $WidgetName = $Self->{Name};
|
|
$WidgetName =~ s{-}{}g;
|
|
|
|
# show non-labeled table headers
|
|
my $CSS = '';
|
|
|
|
# Send data to JS for init container.
|
|
$LayoutObject->AddJSData(
|
|
Key => 'InitContainerDashboard' . $Self->{Name},
|
|
Value => {
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
},
|
|
);
|
|
|
|
for my $Item (@MetaItems) {
|
|
$CSS = '';
|
|
my $Title = $LayoutObject->{LanguageObject}->Translate($Item);
|
|
|
|
# set title description
|
|
if ( $Self->{SortBy} && $Self->{SortBy} eq $Item ) {
|
|
my $TitleDesc = '';
|
|
if ( $TicketSearch{OrderBy} eq 'Down' ) {
|
|
$CSS .= ' SortDescendingLarge';
|
|
$TitleDesc = Translatable('sorted descending');
|
|
}
|
|
else {
|
|
$CSS .= ' SortAscendingLarge';
|
|
$TitleDesc = Translatable('sorted ascending');
|
|
}
|
|
|
|
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
|
|
$Title .= ', ' . $TitleDesc;
|
|
}
|
|
|
|
# add surrounding container
|
|
$LayoutObject->Block(
|
|
Name => 'GeneralOverviewHeader',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderMeta',
|
|
Data => {
|
|
CSS => $CSS,
|
|
HeaderColumnName => $Item,
|
|
Title => $Title,
|
|
},
|
|
);
|
|
|
|
if ( $Item eq 'New Article' ) {
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderMetaEmpty',
|
|
Data => {
|
|
HeaderColumnName => $Item,
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'HeaderMeta' . $WidgetName,
|
|
Value => {
|
|
Name => $Self->{Name},
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
HeaderColumnName => $Item,
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderMetaLink',
|
|
Data => {
|
|
%Param,
|
|
Name => $Self->{Name},
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
HeaderColumnName => $Item,
|
|
Title => $Title,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'HeaderColumn' . $WidgetName,
|
|
Value => \@Columns
|
|
);
|
|
|
|
# show all needed headers
|
|
HEADERCOLUMN:
|
|
for my $HeaderColumn (@Columns) {
|
|
|
|
# skip CustomerID if Customer Information Center
|
|
if ( $Self->{Action} eq 'AgentCustomerInformationCenter' && $HeaderColumn eq 'CustomerID' ) {
|
|
next HEADERCOLUMN;
|
|
}
|
|
|
|
if ( $HeaderColumn !~ m{\A DynamicField_}xms ) {
|
|
|
|
$CSS = '';
|
|
my $Title = $LayoutObject->{LanguageObject}->Translate($HeaderColumn);
|
|
|
|
# Set title description.
|
|
if ( $Self->{SortBy} && $Self->{SortBy} eq $HeaderColumn ) {
|
|
my $TitleDesc = '';
|
|
if ( $TicketSearch{OrderBy} eq 'Down' ) {
|
|
$CSS .= ' SortDescendingLarge';
|
|
$TitleDesc = Translatable('sorted descending');
|
|
}
|
|
else {
|
|
$CSS .= ' SortAscendingLarge';
|
|
$TitleDesc = Translatable('sorted ascending');
|
|
}
|
|
|
|
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
|
|
$Title .= ', ' . $TitleDesc;
|
|
}
|
|
|
|
# translate the column name to write it in the current language
|
|
my $TranslatedWord;
|
|
if ( $HeaderColumn eq 'EscalationTime' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Service Time');
|
|
}
|
|
elsif ( $HeaderColumn eq 'EscalationResponseTime' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('First Response Time');
|
|
}
|
|
elsif ( $HeaderColumn eq 'EscalationSolutionTime' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Solution Time');
|
|
}
|
|
elsif ( $HeaderColumn eq 'EscalationUpdateTime' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Update Time');
|
|
}
|
|
elsif ( $HeaderColumn eq 'PendingTime' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Pending till');
|
|
}
|
|
elsif ( $HeaderColumn eq 'CustomerCompanyName' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer Name');
|
|
}
|
|
elsif ( $HeaderColumn eq 'CustomerID' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer ID');
|
|
}
|
|
elsif ( $HeaderColumn eq 'CustomerName' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User Name');
|
|
}
|
|
elsif ( $HeaderColumn eq 'CustomerUserID' ) {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate('Customer User ID');
|
|
}
|
|
else {
|
|
$TranslatedWord = $LayoutObject->{LanguageObject}->Translate($HeaderColumn);
|
|
}
|
|
|
|
# add surrounding container
|
|
$LayoutObject->Block(
|
|
Name => 'GeneralOverviewHeader',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderTicketHeader',
|
|
Data => {},
|
|
);
|
|
|
|
if ( $HeaderColumn eq 'TicketNumber' ) {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'TicketNumberColumn' . $WidgetName,
|
|
Value => {
|
|
Name => $Self->{Name},
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderTicketNumberColumn',
|
|
Data => {
|
|
%Param,
|
|
CSS => $CSS || '',
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
},
|
|
);
|
|
next HEADERCOLUMN;
|
|
}
|
|
|
|
my $FilterTitle = $TranslatedWord;
|
|
my $FilterTitleDesc = Translatable('filter not active');
|
|
if ( $Self->{GetColumnFilterSelect} && $Self->{GetColumnFilterSelect}->{$HeaderColumn} ) {
|
|
$CSS .= ' FilterActive';
|
|
$FilterTitleDesc = Translatable('filter active');
|
|
}
|
|
$FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc);
|
|
$FilterTitle .= ', ' . $FilterTitleDesc;
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumn',
|
|
Data => {
|
|
HeaderColumnName => $HeaderColumn || '',
|
|
HeaderNameTranslated => $TranslatedWord || $HeaderColumn,
|
|
CSS => $CSS || '',
|
|
},
|
|
);
|
|
|
|
# verify if column is filterable and sortable
|
|
if (
|
|
$Self->{ValidSortableColumns}->{$HeaderColumn}
|
|
&& $Self->{ValidFilterableColumns}->{$HeaderColumn}
|
|
)
|
|
{
|
|
|
|
my $Css;
|
|
if (
|
|
$HeaderColumn eq 'CustomerID'
|
|
|| $HeaderColumn eq 'Responsible'
|
|
|| $HeaderColumn eq 'Owner'
|
|
)
|
|
{
|
|
$Css = 'Hidden';
|
|
}
|
|
|
|
# variable to save the filter's HTML code
|
|
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
|
|
ColumnName => $HeaderColumn,
|
|
Css => $Css,
|
|
);
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnFilterSort' . $HeaderColumn . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $HeaderColumn,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilterLink',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $HeaderColumn,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedWord || $HeaderColumn,
|
|
ColumnFilterStrg => $ColumnFilterHTML,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
FilterTitle => $FilterTitle,
|
|
},
|
|
);
|
|
|
|
if ( $HeaderColumn eq 'CustomerID' ) {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'CustomerIDAutocomplete',
|
|
Value => {
|
|
QueryDelay => 100,
|
|
MaxResultsDisplayed => 20,
|
|
MinQueryLength => 2,
|
|
},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerIDSearch',
|
|
Data => {},
|
|
);
|
|
}
|
|
|
|
elsif ( $HeaderColumn eq 'Responsible' || $HeaderColumn eq 'Owner' ) {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'UserAutocomplete',
|
|
Value => {
|
|
QueryDelay => 100,
|
|
MaxResultsDisplayed => 20,
|
|
MinQueryLength => 2,
|
|
},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilterLinkUserSearch',
|
|
Data => {},
|
|
);
|
|
}
|
|
}
|
|
|
|
# verify if column is just filterable
|
|
elsif ( $Self->{ValidFilterableColumns}->{$HeaderColumn} ) {
|
|
|
|
my $Css;
|
|
if ( $HeaderColumn eq 'CustomerUserID' ) {
|
|
$Css = 'Hidden';
|
|
}
|
|
|
|
# variable to save the filter's HTML code
|
|
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
|
|
ColumnName => $HeaderColumn,
|
|
Css => $Css,
|
|
);
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnFilter' . $HeaderColumn . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $HeaderColumn,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilter',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $HeaderColumn,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedWord || $HeaderColumn,
|
|
ColumnFilterStrg => $ColumnFilterHTML,
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
FilterTitle => $FilterTitle,
|
|
},
|
|
);
|
|
|
|
if ( $HeaderColumn eq 'CustomerUserID' ) {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'CustomerUserAutocomplete',
|
|
Value => {
|
|
QueryDelay => 100,
|
|
MaxResultsDisplayed => 20,
|
|
MinQueryLength => 2,
|
|
},
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilterLinkCustomerUserSearch',
|
|
Data => {},
|
|
);
|
|
}
|
|
}
|
|
|
|
# verify if column is just sortable
|
|
elsif ( $Self->{ValidSortableColumns}->{$HeaderColumn} ) {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnSortable' . $HeaderColumn . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $HeaderColumn,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || $HeaderColumn,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnLink',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $HeaderColumn,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedWord || $HeaderColumn,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortBy => $Self->{SortBy} || $HeaderColumn,
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnEmpty',
|
|
Data => {
|
|
%Param,
|
|
HeaderNameTranslated => $TranslatedWord || $HeaderColumn,
|
|
HeaderColumnName => $HeaderColumn,
|
|
CSS => $CSS,
|
|
Title => $Title,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# Dynamic fields
|
|
else {
|
|
my $DynamicFieldConfig;
|
|
my $DFColumn = $HeaderColumn;
|
|
$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 HEADERCOLUMN if !IsHashRefWithData($DynamicFieldConfig);
|
|
|
|
my $Label = $DynamicFieldConfig->{Label};
|
|
|
|
my $TranslatedLabel = $LayoutObject->{LanguageObject}->Translate($Label);
|
|
|
|
my $DynamicFieldName = 'DynamicField_' . $DynamicFieldConfig->{Name};
|
|
|
|
my $CSS = '';
|
|
my $FilterTitle = $Label;
|
|
my $FilterTitleDesc = Translatable('filter not active');
|
|
if ( $Self->{GetColumnFilterSelect} && defined $Self->{GetColumnFilterSelect}->{$DynamicFieldName} ) {
|
|
$CSS .= 'FilterActive ';
|
|
$FilterTitleDesc = Translatable('filter active');
|
|
}
|
|
$FilterTitleDesc = $LayoutObject->{LanguageObject}->Translate($FilterTitleDesc);
|
|
$FilterTitle .= ', ' . $FilterTitleDesc;
|
|
|
|
# get field sortable condition
|
|
my $IsSortable = $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsSortable',
|
|
);
|
|
|
|
# set title
|
|
my $Title = $Label;
|
|
|
|
# add surrounding container
|
|
$LayoutObject->Block(
|
|
Name => 'GeneralOverviewHeader',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderTicketHeader',
|
|
Data => {},
|
|
);
|
|
|
|
if ($IsSortable) {
|
|
my $TitleDesc = '';
|
|
if (
|
|
$Self->{SortBy}
|
|
&& ( $Self->{SortBy} eq ( 'DynamicField_' . $DynamicFieldConfig->{Name} ) )
|
|
)
|
|
{
|
|
if ( $TicketSearch{OrderBy} eq 'Down' ) {
|
|
$CSS .= ' SortDescendingLarge';
|
|
$TitleDesc = Translatable('sorted descending');
|
|
}
|
|
else {
|
|
$CSS .= ' SortAscendingLarge';
|
|
$TitleDesc = Translatable('sorted ascending');
|
|
}
|
|
|
|
$TitleDesc = $LayoutObject->{LanguageObject}->Translate($TitleDesc);
|
|
$Title .= ', ' . $TitleDesc;
|
|
}
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumn',
|
|
Data => {
|
|
HeaderColumnName => $DynamicFieldName || '',
|
|
CSS => $CSS || '',
|
|
},
|
|
);
|
|
|
|
# check if the dynamic field is sortable and filterable (sortable check was made before)
|
|
if ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
|
|
|
|
# variable to save the filter's HTML code
|
|
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
|
|
ColumnName => $DynamicFieldName,
|
|
Label => $Label,
|
|
);
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnFilterSort' . $DynamicFieldName . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $DynamicFieldName,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
# output sortable and filterable dynamic field
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilterLink',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $DynamicFieldName,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedLabel || $DynamicFieldName,
|
|
ColumnFilterStrg => $ColumnFilterHTML,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
FilterTitle => $FilterTitle,
|
|
},
|
|
);
|
|
}
|
|
|
|
# otherwise the dynamic field is only sortable (sortable check was made before)
|
|
else {
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnSortable' . $DynamicFieldName . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $DynamicFieldName,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || $DynamicFieldName,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
# output sortable dynamic field
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnLink',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $DynamicFieldName,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedLabel || $DynamicFieldName,
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortBy => $Self->{SortBy} || $DynamicFieldName,
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
FilterTitle => $FilterTitle,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
# if the dynamic field was not sortable (check was made and fail before)
|
|
# it might be filterable
|
|
elsif ( $Self->{ValidFilterableColumns}->{$DynamicFieldName} ) {
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumn',
|
|
Data => {
|
|
HeaderColumnName => $DynamicFieldName || '',
|
|
CSS => $CSS || '',
|
|
Title => $Title,
|
|
},
|
|
);
|
|
|
|
# variable to save the filter's HTML code
|
|
my $ColumnFilterHTML = $Self->_InitialColumnFilter(
|
|
ColumnName => $DynamicFieldName,
|
|
Label => $Label,
|
|
);
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'ColumnFilter' . $DynamicFieldName . $WidgetName,
|
|
Value => {
|
|
HeaderColumnName => $DynamicFieldName,
|
|
Name => $Self->{Name},
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
},
|
|
);
|
|
|
|
# output filterable (not sortable) dynamic field
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnFilter',
|
|
Data => {
|
|
%Param,
|
|
HeaderColumnName => $DynamicFieldName,
|
|
CSS => $CSS,
|
|
HeaderNameTranslated => $TranslatedLabel || $DynamicFieldName,
|
|
ColumnFilterStrg => $ColumnFilterHTML,
|
|
Name => $Self->{Name},
|
|
Title => $Title,
|
|
FilterTitle => $FilterTitle,
|
|
},
|
|
);
|
|
}
|
|
|
|
# otherwise the field is not filterable and not sortable
|
|
else {
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumn',
|
|
Data => {
|
|
HeaderColumnName => $DynamicFieldName || '',
|
|
CSS => $CSS || '',
|
|
},
|
|
);
|
|
|
|
# output plain dynamic field header (not filterable, not sortable)
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericHeaderColumnEmpty',
|
|
Data => {
|
|
%Param,
|
|
HeaderNameTranslated => $TranslatedLabel || $DynamicFieldName,
|
|
HeaderColumnName => $DynamicFieldName,
|
|
CSS => $CSS,
|
|
Title => $Title,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
# show tickets
|
|
my $Count = 0;
|
|
TICKETID:
|
|
for my $TicketID ( @{$TicketIDs} ) {
|
|
$Count++;
|
|
next TICKETID if $Count < $Self->{StartHit};
|
|
my %Ticket = $TicketObject->TicketGet(
|
|
TicketID => $TicketID,
|
|
UserID => $Self->{UserID},
|
|
DynamicFields => 0,
|
|
Silent => 1
|
|
);
|
|
|
|
next TICKETID if !%Ticket;
|
|
|
|
# set a default title if ticket has no title
|
|
if ( !$Ticket{Title} ) {
|
|
$Ticket{Title} = $LayoutObject->{LanguageObject}->Translate(
|
|
'This ticket has no title or subject'
|
|
);
|
|
}
|
|
|
|
my $WholeTitle = $Ticket{Title} || '';
|
|
$Ticket{Title} = $TicketObject->TicketSubjectClean(
|
|
TicketNumber => $Ticket{TicketNumber},
|
|
Subject => $Ticket{Title},
|
|
);
|
|
|
|
# create human age
|
|
if ( $Self->{Config}->{Time} ne 'Age' ) {
|
|
$Ticket{Time} = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{ $Self->{Config}->{Time} },
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
);
|
|
}
|
|
else {
|
|
$Ticket{Time} = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{ $Self->{Config}->{Time} },
|
|
Space => ' ',
|
|
);
|
|
}
|
|
|
|
# show ticket
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericRow',
|
|
Data => \%Ticket,
|
|
);
|
|
|
|
# show ticket flags
|
|
my @TicketMetaItems = $LayoutObject->TicketMetaItems(
|
|
Ticket => \%Ticket,
|
|
);
|
|
for my $Item (@TicketMetaItems) {
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'GeneralOverviewRow',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericRowMeta',
|
|
Data => {},
|
|
);
|
|
if ($Item) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericRowMetaImage',
|
|
Data => $Item,
|
|
);
|
|
}
|
|
}
|
|
|
|
# save column content
|
|
my $DataValue;
|
|
|
|
# get needed objects
|
|
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
|
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
|
|
|
# show all needed columns
|
|
COLUMN:
|
|
for my $Column (@Columns) {
|
|
|
|
# skip CustomerID if Customer Information Center
|
|
if ( $Self->{Action} eq 'AgentCustomerInformationCenter' && $Column eq 'CustomerID' ) {
|
|
next COLUMN;
|
|
}
|
|
|
|
if ( $Column !~ m{\A DynamicField_}xms ) {
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'GeneralOverviewRow',
|
|
);
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericTicketColumn',
|
|
Data => {},
|
|
);
|
|
|
|
my $BlockType = '';
|
|
my $CSSClass = '';
|
|
|
|
if ( $Column eq 'TicketNumber' ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericTicketNumber',
|
|
Data => {
|
|
%Ticket,
|
|
Title => $Ticket{Title},
|
|
},
|
|
);
|
|
next COLUMN;
|
|
}
|
|
elsif ( $Column eq 'EscalationTime' ) {
|
|
my %EscalationData;
|
|
$EscalationData{EscalationTime} = $Ticket{EscalationTime};
|
|
$EscalationData{EscalationDestinationDate} = $Ticket{EscalationDestinationDate};
|
|
|
|
$EscalationData{EscalationTimeHuman} = $LayoutObject->CustomerAge(
|
|
Age => $EscalationData{EscalationTime},
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
) || '-';
|
|
$EscalationData{EscalationTimeWorkingTime} = $LayoutObject->CustomerAge(
|
|
Age => $EscalationData{EscalationTimeWorkingTime},
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
);
|
|
if ( defined $Ticket{EscalationTime} && $Ticket{EscalationTime} < 60 * 60 * 1 ) {
|
|
$EscalationData{EscalationClass} = 'Warning';
|
|
}
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericEscalationTime',
|
|
Data => {%EscalationData},
|
|
);
|
|
next COLUMN;
|
|
}
|
|
elsif ( $Column eq 'Age' ) {
|
|
$DataValue = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{Age},
|
|
Space => ' ',
|
|
);
|
|
}
|
|
elsif ( $Column eq 'EscalationSolutionTime' ) {
|
|
$BlockType = 'Escalation';
|
|
$DataValue = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{SolutionTime} || 0,
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
);
|
|
if ( defined $Ticket{SolutionTime} && $Ticket{SolutionTime} < 60 * 60 * 1 ) {
|
|
$CSSClass = 'Warning';
|
|
}
|
|
}
|
|
elsif ( $Column eq 'EscalationResponseTime' ) {
|
|
$BlockType = 'Escalation';
|
|
$DataValue = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{FirstResponseTime} || 0,
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
);
|
|
if (
|
|
defined $Ticket{FirstResponseTime}
|
|
&& $Ticket{FirstResponseTime} < 60 * 60 * 1
|
|
)
|
|
{
|
|
$CSSClass = 'Warning';
|
|
}
|
|
}
|
|
elsif ( $Column eq 'EscalationUpdateTime' ) {
|
|
$BlockType = 'Escalation';
|
|
$DataValue = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{UpdateTime} || 0,
|
|
TimeShowAlwaysLong => 1,
|
|
Space => ' ',
|
|
);
|
|
if ( defined $Ticket{UpdateTime} && $Ticket{UpdateTime} < 60 * 60 * 1 ) {
|
|
$CSSClass = 'Warning';
|
|
}
|
|
}
|
|
elsif ( $Column eq 'PendingTime' ) {
|
|
$BlockType = 'Escalation';
|
|
$DataValue = $LayoutObject->CustomerAge(
|
|
Age => $Ticket{'UntilTime'},
|
|
Space => ' ',
|
|
);
|
|
if ( defined $Ticket{UntilTime} && $Ticket{UntilTime} < -1 ) {
|
|
$CSSClass = 'Warning';
|
|
}
|
|
}
|
|
elsif ( $Column eq 'Owner' ) {
|
|
|
|
# get owner info
|
|
my %OwnerInfo = $UserObject->GetUserData(
|
|
UserID => $Ticket{OwnerID},
|
|
);
|
|
$DataValue = $OwnerInfo{'UserFullname'};
|
|
}
|
|
elsif ( $Column eq 'Responsible' ) {
|
|
|
|
# get responsible info
|
|
my %ResponsibleInfo = $UserObject->GetUserData(
|
|
UserID => $Ticket{ResponsibleID},
|
|
);
|
|
$DataValue = $ResponsibleInfo{'UserFullname'};
|
|
}
|
|
elsif (
|
|
$Column eq 'State'
|
|
|| $Column eq 'Lock'
|
|
|| $Column eq 'Priority'
|
|
)
|
|
{
|
|
$BlockType = 'Translatable';
|
|
$DataValue = $Ticket{$Column};
|
|
}
|
|
elsif ( $Column eq 'Created' || $Column eq 'Changed' ) {
|
|
$BlockType = 'Time';
|
|
$DataValue = $Ticket{$Column};
|
|
}
|
|
elsif ( $Column eq 'CustomerName' ) {
|
|
|
|
# get customer name
|
|
my $CustomerName;
|
|
if ( $Ticket{CustomerUserID} ) {
|
|
$CustomerName = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerName(
|
|
UserLogin => $Ticket{CustomerUserID},
|
|
);
|
|
}
|
|
$DataValue = $CustomerName;
|
|
}
|
|
elsif ( $Column eq 'CustomerCompanyName' ) {
|
|
my %CustomerCompanyData;
|
|
if ( $Ticket{CustomerID} ) {
|
|
%CustomerCompanyData = $Kernel::OM->Get('Kernel::System::CustomerCompany')->CustomerCompanyGet(
|
|
CustomerID => $Ticket{CustomerID},
|
|
);
|
|
}
|
|
$DataValue = $CustomerCompanyData{CustomerCompanyName};
|
|
}
|
|
else {
|
|
$DataValue = $Ticket{$Column};
|
|
}
|
|
|
|
if ( $Column eq 'Title' ) {
|
|
$LayoutObject->Block(
|
|
Name => "ContentLargeTicketTitle",
|
|
Data => {
|
|
Title => "$DataValue " || '',
|
|
WholeTitle => $WholeTitle,
|
|
Class => $CSSClass || '',
|
|
},
|
|
);
|
|
|
|
}
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => "ContentLargeTicketGenericColumn$BlockType",
|
|
Data => {
|
|
GenericValue => $DataValue || '-',
|
|
Class => $CSSClass || '',
|
|
},
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
# Dynamic fields
|
|
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);
|
|
|
|
# get field value
|
|
my $Value = $BackendObject->ValueGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
ObjectID => $TicketID,
|
|
);
|
|
|
|
my $ValueStrg = $BackendObject->DisplayValueRender(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Value => $Value,
|
|
ValueMaxChars => 20,
|
|
LayoutObject => $LayoutObject,
|
|
);
|
|
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericDynamicField',
|
|
Data => {
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
|
|
if ( $ValueStrg->{Link} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericDynamicFieldLink',
|
|
Data => {
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
Link => $ValueStrg->{Link},
|
|
$DynamicFieldConfig->{Name} => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
}
|
|
else {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericDynamicFieldPlain',
|
|
Data => {
|
|
Value => $ValueStrg->{Value},
|
|
Title => $ValueStrg->{Title},
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# show "none" if no ticket is available
|
|
if ( !$TicketIDs || !@{$TicketIDs} ) {
|
|
$LayoutObject->Block(
|
|
Name => 'ContentLargeTicketGenericNone',
|
|
Data => {},
|
|
);
|
|
}
|
|
|
|
# check for refresh time
|
|
my $Refresh = '';
|
|
if ( $Self->{UserRefreshTime} ) {
|
|
$Refresh = 60 * $Self->{UserRefreshTime};
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'WidgetRefresh' . $WidgetName,
|
|
Value => {
|
|
Name => $Self->{Name},
|
|
NameHTML => $WidgetName,
|
|
RefreshTime => $Refresh,
|
|
CustomerID => $Param{CustomerID},
|
|
CustomerUserID => $Param{CustomerUserID},
|
|
},
|
|
);
|
|
}
|
|
|
|
# check for active filters and add a 'remove filters' button to the widget header
|
|
my $FilterActive = 0;
|
|
if ( $Self->{GetColumnFilterSelect} && IsHashRefWithData( $Self->{GetColumnFilterSelect} ) ) {
|
|
$FilterActive = 1;
|
|
}
|
|
|
|
# send data to JS
|
|
$LayoutObject->AddJSData(
|
|
Key => 'WidgetContainer' . $WidgetName,
|
|
Value => {
|
|
Name => $Self->{Name},
|
|
CustomerID => $Param{CustomerID},
|
|
CustomerUserID => $Param{CustomerUserID},
|
|
FilterActive => $FilterActive,
|
|
SortBy => $Self->{SortBy} || 'Age',
|
|
OrderBy => $TicketSearch{OrderBy},
|
|
SortingColumn => $Param{SortingColumn},
|
|
},
|
|
);
|
|
|
|
my $Content = $LayoutObject->Output(
|
|
TemplateFile => 'AgentDashboardTicketGeneric',
|
|
Data => {
|
|
%{ $Self->{Config} },
|
|
Name => $Self->{Name},
|
|
%{$Summary},
|
|
FilterValue => $Self->{Filter},
|
|
AdditionalFilter => $Self->{AdditionalFilter},
|
|
CustomerID => $Self->{CustomerID},
|
|
CustomerUserID => $Self->{CustomerUserID},
|
|
},
|
|
AJAX => $Param{AJAX},
|
|
);
|
|
|
|
return $Content;
|
|
}
|
|
|
|
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} . $Self->{Name},
|
|
Data => $Data,
|
|
Class => $Class,
|
|
Translation => $TranslationOption,
|
|
SelectedID => '',
|
|
);
|
|
return $ColumnFilterHTML;
|
|
}
|
|
|
|
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 $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
|
|
|
my $IsFiltrable = $BackendObject->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} = $BackendObject->ColumnFilterValuesGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
LayoutObject => $Kernel::OM->Get('Kernel::Output::HTML::Layout'),
|
|
TicketIDs => $TicketIDs,
|
|
);
|
|
}
|
|
else {
|
|
|
|
# get PossibleValues
|
|
$ColumnFilterValues{$HeaderColumn} = $BackendObject->PossibleValuesGet(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
);
|
|
}
|
|
last DYNAMICFIELD;
|
|
}
|
|
}
|
|
|
|
return \%ColumnFilterValues;
|
|
}
|
|
|
|
# =over
|
|
|
|
# =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 ) = @_;
|
|
|
|
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};
|
|
$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} . $Param{DashboardName},
|
|
Data => $Data,
|
|
Class => 'ColumnFilter',
|
|
Sort => 'AlphanumericKey',
|
|
TreeView => 1,
|
|
SelectedID => $Param{SelectedValue},
|
|
Translation => $TranslationOption,
|
|
AutoComplete => 'off',
|
|
},
|
|
],
|
|
);
|
|
|
|
return $JSON;
|
|
}
|
|
|
|
sub _SearchParamsGet {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
# get all search base attributes
|
|
my %TicketSearch;
|
|
my %DynamicFieldsParameters;
|
|
my @Params = split /;/, $Self->{Config}->{Attributes};
|
|
|
|
# read user preferences and config to get columns that
|
|
# should be shown in the dashboard widget (the preferences
|
|
# have precedence)
|
|
my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# get column names from Preferences
|
|
my $PreferencesColumn = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
|
|
Data => $Preferences{ $Self->{PrefKeyColumns} },
|
|
);
|
|
|
|
# check for default settings
|
|
my @Columns;
|
|
if ( $Self->{Config}->{DefaultColumns} && IsHashRefWithData( $Self->{Config}->{DefaultColumns} ) ) {
|
|
@Columns = grep { $Self->{Config}->{DefaultColumns}->{$_} eq '2' }
|
|
sort { $Self->_DefaultColumnSort() } keys %{ $Self->{Config}->{DefaultColumns} };
|
|
}
|
|
if ($PreferencesColumn) {
|
|
if ( $PreferencesColumn->{Columns} && %{ $PreferencesColumn->{Columns} } ) {
|
|
@Columns = grep {
|
|
defined $PreferencesColumn->{Columns}->{$_}
|
|
&& $PreferencesColumn->{Columns}->{$_} eq '1'
|
|
} sort { $Self->_DefaultColumnSort() } keys %{ $Self->{Config}->{DefaultColumns} };
|
|
}
|
|
if ( $PreferencesColumn->{Order} && @{ $PreferencesColumn->{Order} } ) {
|
|
@Columns = @{ $PreferencesColumn->{Order} };
|
|
}
|
|
|
|
# remove duplicate columns
|
|
my %UniqueColumns;
|
|
my @ColumnsEnabledAux;
|
|
|
|
for my $Column (@Columns) {
|
|
if ( !$UniqueColumns{$Column} ) {
|
|
push @ColumnsEnabledAux, $Column;
|
|
}
|
|
$UniqueColumns{$Column} = 1;
|
|
}
|
|
|
|
# set filtered column list
|
|
@Columns = @ColumnsEnabledAux;
|
|
}
|
|
|
|
# always set TicketNumber
|
|
if ( !grep { $_ eq 'TicketNumber' } @Columns ) {
|
|
unshift @Columns, 'TicketNumber';
|
|
}
|
|
|
|
# also always set ProcessID and ActivityID (for process widgets)
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
|
|
my @AlwaysColumns = (
|
|
'DynamicField_' . $Self->{ProcessManagementProcessID},
|
|
'DynamicField_' . $Self->{ProcessManagementActivityID},
|
|
);
|
|
my $Resort;
|
|
for my $AlwaysColumn (@AlwaysColumns) {
|
|
if ( !grep { $_ eq $AlwaysColumn } @Columns ) {
|
|
push @Columns, $AlwaysColumn;
|
|
$Resort = 1;
|
|
}
|
|
}
|
|
if ($Resort) {
|
|
@Columns = sort { $Self->_DefaultColumnSort() } @Columns;
|
|
}
|
|
}
|
|
|
|
{
|
|
|
|
# loop through all the dynamic fields to get the ones that should be shown
|
|
DYNAMICFIELDNAME:
|
|
for my $DynamicFieldName (@Columns) {
|
|
|
|
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} || {},
|
|
);
|
|
|
|
# get dynamic field backend object
|
|
my $BackendObject = $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 = $BackendObject->HasBehavior(
|
|
DynamicFieldConfig => $DynamicFieldConfig,
|
|
Behavior => 'IsFiltrable',
|
|
);
|
|
|
|
# if the dynamic field is filterable add it to the ValidFilterableColumns hash
|
|
if ($IsFiltrable) {
|
|
$Self->{ValidFilterableColumns}->{ '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 = $BackendObject->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;
|
|
}
|
|
}
|
|
|
|
# get queue object
|
|
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
|
|
|
|
STRING:
|
|
for my $String (@Params) {
|
|
next STRING if !$String;
|
|
my ( $Key, $Value ) = split /=/, $String;
|
|
|
|
if ( $Key eq 'CustomerID' ) {
|
|
$Key = "CustomerIDRaw";
|
|
}
|
|
|
|
# push ARRAYREF attributes directly in an ARRAYREF
|
|
if (
|
|
$Key
|
|
=~ /^(StateType|StateTypeIDs|Queues|QueueIDs|Types|TypeIDs|States|StateIDs|Priorities|PriorityIDs|Services|ServiceIDs|SLAs|SLAIDs|Locks|LockIDs|OwnerIDs|ResponsibleIDs|WatchUserIDs|ArchiveFlags|CreatedUserIDs|CreatedTypes|CreatedTypeIDs|CreatedPriorities|CreatedPriorityIDs|CreatedStates|CreatedStateIDs|CreatedQueues|CreatedQueueIDs)$/
|
|
)
|
|
{
|
|
if ( $Value =~ m{,}smx ) {
|
|
push @{ $TicketSearch{$Key} }, split( /,/, $Value );
|
|
}
|
|
else {
|
|
push @{ $TicketSearch{$Key} }, $Value;
|
|
}
|
|
}
|
|
|
|
# check if parameter is a dynamic field and capture dynamic field name (with DynamicField_)
|
|
# in $1 and the Operator in $2
|
|
# possible Dynamic Fields options include:
|
|
# DynamicField_NameX_Equals=123;
|
|
# DynamicField_NameX_Like=value*;
|
|
# DynamicField_NameX_GreaterThan=2001-01-01 01:01:01;
|
|
# DynamicField_NameX_GreaterThanEquals=2001-01-01 01:01:01;
|
|
# DynamicField_NameX_SmallerThan=2002-02-02 02:02:02;
|
|
# DynamicField_NameX_SmallerThanEquals=2002-02-02 02:02:02;
|
|
elsif ( $Key =~ m{\A (DynamicField_.+?) _ (.+?) \z}sxm ) {
|
|
|
|
# prevent adding ProcessManagement search parameters (for ProcessWidget)
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
next STRING if $2 eq $Self->{ProcessManagementProcessID};
|
|
next STRING if $2 eq $Self->{ProcessManagementActivityID};
|
|
}
|
|
|
|
push @{ $DynamicFieldsParameters{$1}->{$2} }, $Value;
|
|
}
|
|
|
|
elsif ( !defined $TicketSearch{$Key} ) {
|
|
|
|
# change sort by, if needed
|
|
if (
|
|
$Key eq 'SortBy'
|
|
&& $Self->{SortBy}
|
|
&& $Self->{ValidSortableColumns}->{ $Self->{SortBy} }
|
|
)
|
|
{
|
|
$Value = $Self->{SortBy};
|
|
}
|
|
elsif ( $Key eq 'SortBy' && !$Self->{ValidSortableColumns}->{$Value} ) {
|
|
$Value = 'Age';
|
|
}
|
|
$TicketSearch{$Key} = $Value;
|
|
}
|
|
elsif ( !ref $TicketSearch{$Key} ) {
|
|
my $ValueTmp = $TicketSearch{$Key};
|
|
$TicketSearch{$Key} = [$ValueTmp];
|
|
push @{ $TicketSearch{$Key} }, $Value;
|
|
}
|
|
else {
|
|
push @{ $TicketSearch{$Key} }, $Value;
|
|
}
|
|
}
|
|
%TicketSearch = (
|
|
%TicketSearch,
|
|
%DynamicFieldsParameters,
|
|
Permission => $Self->{Config}->{Permission} || 'ro',
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
# CustomerInformationCenter shows data per CustomerID
|
|
if ( $Param{CustomerID} ) {
|
|
$TicketSearch{CustomerIDRaw} = $Param{CustomerID};
|
|
}
|
|
|
|
# define filter attributes
|
|
my @MyQueues = $QueueObject->GetAllCustomQueues(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
if ( !@MyQueues ) {
|
|
@MyQueues = (999_999);
|
|
}
|
|
|
|
# get all queues the agent is allowed to see (for my services)
|
|
my %ViewableQueues = $QueueObject->GetAllQueues(
|
|
UserID => $Self->{UserID},
|
|
Type => 'ro',
|
|
);
|
|
my @ViewableQueueIDs = sort keys %ViewableQueues;
|
|
if ( !@ViewableQueueIDs ) {
|
|
@ViewableQueueIDs = (999_999);
|
|
}
|
|
|
|
# get the custom services from agent preferences
|
|
# set the service ids to an array of non existing service ids (0)
|
|
my @MyServiceIDs = (0);
|
|
if ( $Self->{UseTicketService} ) {
|
|
@MyServiceIDs = $Kernel::OM->Get('Kernel::System::Service')->GetAllCustomServices(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
|
|
if ( !defined $MyServiceIDs[0] ) {
|
|
@MyServiceIDs = (0);
|
|
}
|
|
}
|
|
|
|
my %LockList = $Kernel::OM->Get('Kernel::System::Lock')->LockList(
|
|
UserID => $Self->{UserID},
|
|
);
|
|
my %LockName2ID = reverse %LockList;
|
|
|
|
my %TicketSearchSummary = (
|
|
Locked => {
|
|
OwnerIDs => $TicketSearch{OwnerIDs} // [ $Self->{UserID}, ],
|
|
LockIDs => [ $LockName2ID{lock}, $LockName2ID{tmp_lock} ],
|
|
},
|
|
Watcher => {
|
|
WatchUserIDs => [ $Self->{UserID}, ],
|
|
LockIDs => $TicketSearch{LockIDs} // undef,
|
|
},
|
|
Responsible => {
|
|
ResponsibleIDs => $TicketSearch{ResponsibleIDs} // [ $Self->{UserID}, ],
|
|
LockIDs => $TicketSearch{LockIDs} // undef,
|
|
},
|
|
MyQueues => {
|
|
QueueIDs => \@MyQueues,
|
|
LockIDs => $TicketSearch{LockIDs} // undef,
|
|
},
|
|
MyServices => {
|
|
QueueIDs => \@ViewableQueueIDs,
|
|
ServiceIDs => \@MyServiceIDs,
|
|
LockIDs => $TicketSearch{LockIDs} // undef,
|
|
},
|
|
All => {
|
|
OwnerIDs => $TicketSearch{OwnerIDs} // undef,
|
|
LockIDs => $TicketSearch{LockIDs} // undef,
|
|
},
|
|
);
|
|
|
|
if ( $Self->{Action} eq 'AgentCustomerUserInformationCenter' ) {
|
|
|
|
# Add filters for assigend and accessible tickets for the customer user information center as a
|
|
# additional filter together with the other filters. One of them must be always active.
|
|
%TicketSearchSummary = (
|
|
AssignedToCustomerUser => {
|
|
CustomerUserLoginRaw => $Param{CustomerUserID} // undef,
|
|
},
|
|
AccessibleForCustomerUser => {
|
|
CustomerUserID => $Param{CustomerUserID} // undef,
|
|
},
|
|
%TicketSearchSummary,
|
|
);
|
|
}
|
|
|
|
if ( defined $TicketSearch{LockIDs} || defined $TicketSearch{Locks} ) {
|
|
delete $TicketSearchSummary{Locked};
|
|
}
|
|
|
|
if ( defined $TicketSearch{WatchUserIDs} ) {
|
|
delete $TicketSearchSummary{Watcher};
|
|
}
|
|
|
|
if ( defined $TicketSearch{ResponsibleIDs} ) {
|
|
delete $TicketSearchSummary{Responsible};
|
|
}
|
|
|
|
if ( defined $TicketSearch{QueueIDs} || defined $TicketSearch{Queues} ) {
|
|
delete $TicketSearchSummary{MyQueues};
|
|
delete $TicketSearchSummary{MyServices}->{QueueIDs};
|
|
}
|
|
|
|
if ( !$Self->{UseTicketService} ) {
|
|
delete $TicketSearchSummary{MyServices};
|
|
}
|
|
|
|
return (
|
|
Columns => \@Columns,
|
|
TicketSearch => \%TicketSearch,
|
|
TicketSearchSummary => \%TicketSearchSummary,
|
|
);
|
|
}
|
|
|
|
sub _DefaultColumnSort {
|
|
my ( $Self, %Param ) = @_;
|
|
|
|
my %DefaultColumns = (
|
|
TicketNumber => 100,
|
|
Age => 110,
|
|
Changed => 111,
|
|
PendingTime => 112,
|
|
EscalationTime => 113,
|
|
EscalationSolutionTime => 114,
|
|
EscalationResponseTime => 115,
|
|
EscalationUpdateTime => 116,
|
|
Title => 120,
|
|
State => 130,
|
|
Lock => 140,
|
|
Queue => 150,
|
|
Owner => 160,
|
|
Responsible => 161,
|
|
CustomerID => 170,
|
|
CustomerName => 171,
|
|
CustomerUserID => 172,
|
|
Type => 180,
|
|
Service => 191,
|
|
SLA => 192,
|
|
Priority => 193,
|
|
);
|
|
|
|
# set default order of ProcessManagement columns (for process widgets)
|
|
if ( $Self->{Config}->{IsProcessWidget} ) {
|
|
$DefaultColumns{"DynamicField_$Self->{ProcessManagementProcessID}"} = 101;
|
|
$DefaultColumns{"DynamicField_$Self->{ProcessManagementActivityID}"} = 102;
|
|
}
|
|
|
|
# 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;
|