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

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;