Files
scripts/Perl OTRS/Kernel/Modules/CustomerTicketOverview.pm
2024-10-14 00:08:40 +02:00

823 lines
27 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::Modules::CustomerTicketOverview;
## nofilter(TidyAll::Plugin::OTRS::Perl::DBObject)
use strict;
use warnings;
our $ObjectManagerDisabled = 1;
use Kernel::System::VariableCheck qw(:all);
use Kernel::Language qw(Translatable);
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
# disable output of customer company tickets
my $DisableCompanyTickets = $ConfigObject->Get('Ticket::Frontend::CustomerDisableCompanyTicketAccess');
# check subaction
if ( !$Self->{Subaction} ) {
return $LayoutObject->Redirect(
OP => 'Action=CustomerTicketOverview;Subaction=MyTickets',
);
}
elsif ( $Self->{Subaction} eq 'CompanyTickets' && $DisableCompanyTickets ) {
return $LayoutObject->CustomerNoPermission( WithHeader => 'yes' );
}
# check needed CustomerID
if ( !$Self->{UserCustomerID} ) {
my $Output = $LayoutObject->CustomerHeader(
Title => Translatable('Error'),
);
$Output .= $LayoutObject->CustomerError(
Message => Translatable('Need CustomerID!'),
);
$Output .= $LayoutObject->CustomerFooter();
return $Output;
}
$Kernel::OM->Get('Kernel::System::AuthSession')->UpdateSessionID(
SessionID => $Self->{SessionID},
Key => 'LastScreenOverview',
Value => $Self->{RequestedURL},
);
my $SortBy = $ParamObject->GetParam( Param => 'SortBy' ) || 'Age';
my $OrderByCurrent = $ParamObject->GetParam( Param => 'OrderBy' ) || 'Down';
# filter definition
my %Filters = (
MyTickets => {
All => {
Name => 'All',
Prio => 1000,
Search => {
CustomerUserLoginRaw => $Self->{UserID},
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
Open => {
Name => 'Open',
Prio => 1100,
Search => {
CustomerUserLoginRaw => $Self->{UserID},
StateType => 'Open',
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
Closed => {
Name => 'Closed',
Prio => 1200,
Search => {
CustomerUserLoginRaw => $Self->{UserID},
StateType => 'Closed',
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
},
);
my $UserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
# Add filter for customer company if the company tickets are not disabled.
if ( !$DisableCompanyTickets ) {
my @CustomerIDs;
my %AccessibleCustomers = $Kernel::OM->Get('Kernel::System::CustomerGroup')->GroupContextCustomers(
CustomerUserID => $Self->{UserID},
);
# Show customer companies as additional filter selection.
if ( $Self->{Subaction} eq 'CompanyTickets' && scalar keys %AccessibleCustomers > 1 ) {
@CustomerIDs = $ParamObject->GetArray( Param => 'CustomerIDs' );
$Param{CustomerIDStrg} = $LayoutObject->BuildSelection(
Data => \%AccessibleCustomers,
Name => 'CustomerIDs',
Multiple => 1,
Size => 1,
SelectedID => \@CustomerIDs,
Class => 'Modernize',
);
# Remember the active customer ID filter.
$Param{CustomerIDs} = '';
for my $CustomerID (@CustomerIDs) {
$Param{CustomerIDs} .= ';CustomerIDs=' . $LayoutObject->LinkEncode($CustomerID);
}
}
else {
# Default behavior - use all available customer IDs.
@CustomerIDs = sort keys %AccessibleCustomers;
}
$Filters{CompanyTickets} = {
All => {
Name => 'All',
Prio => 1000,
Search => {
CustomerIDRaw => \@CustomerIDs,
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
Open => {
Name => 'Open',
Prio => 1100,
Search => {
CustomerIDRaw => \@CustomerIDs,
StateType => 'Open',
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
Closed => {
Name => 'Closed',
Prio => 1200,
Search => {
CustomerIDRaw => \@CustomerIDs,
StateType => 'Closed',
OrderBy => $OrderByCurrent,
SortBy => $SortBy,
CustomerUserID => $Self->{UserID},
Permission => 'ro',
},
},
};
}
my $FilterCurrent = $ParamObject->GetParam( Param => 'Filter' ) || 'Open';
# check if filter is valid
if ( !$Filters{ $Self->{Subaction} }->{$FilterCurrent} ) {
my $Output = $LayoutObject->CustomerHeader(
Title => Translatable('Error'),
);
$Output .= $LayoutObject->CustomerError(
Message => $LayoutObject->{LanguageObject}->Translate( 'Invalid Filter: %s!', $FilterCurrent ),
);
$Output .= $LayoutObject->CustomerFooter();
return $Output;
}
# check if archive search is allowed, otherwise search for all tickets
my %SearchInArchive;
if (
$ConfigObject->Get('Ticket::ArchiveSystem')
&& !$ConfigObject->Get('Ticket::CustomerArchiveSystem')
)
{
$SearchInArchive{ArchiveFlags} = [ 'y', 'n' ];
}
my %NavBarFilter;
my $Counter = 0;
my $AllTickets = 0;
my $AllTicketsTotal = 0;
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
for my $Filter ( sort keys %{ $Filters{ $Self->{Subaction} } } ) {
$Counter++;
my $Count = $TicketObject->TicketSearch(
%{ $Filters{ $Self->{Subaction} }->{$Filter}->{Search} },
%SearchInArchive,
Result => 'COUNT',
) || 0;
my $ClassA = '';
if ( $Filter eq $FilterCurrent ) {
$ClassA = 'Selected';
$AllTickets = $Count;
}
if ( $Filter eq 'All' ) {
$AllTicketsTotal = $Count;
}
$NavBarFilter{ $Filters{ $Self->{Subaction} }->{$Filter}->{Prio} } = {
%{ $Filters{ $Self->{Subaction} }->{$Filter} },
Count => $Count,
Filter => $Filter,
ClassA => $ClassA,
};
}
my $StartHit = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
my $PageShown = $Self->{UserShowTickets} || 1;
if ( !$AllTicketsTotal ) {
$LayoutObject->Block(
Name => 'Empty',
);
my $CustomTexts = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverviewCustomEmptyText');
if ( ref $CustomTexts eq 'HASH' ) {
$LayoutObject->Block(
Name => 'EmptyCustom',
Data => $CustomTexts,
);
# only show button, if frontend module for NewTicket is registered
# and button text is configured
if (
ref $ConfigObject->Get('CustomerFrontend::Module')->{CustomerTicketMessage}
eq 'HASH'
&& defined $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverviewCustomEmptyText')
->{Button}
)
{
$LayoutObject->Block(
Name => 'EmptyCustomButton',
Data => $CustomTexts,
);
}
}
else {
$LayoutObject->Block(
Name => 'EmptyDefault',
);
# only show button, if frontend module for NewTicket is registered
if (
ref $ConfigObject->Get('CustomerFrontend::Module')->{CustomerTicketMessage}
eq 'HASH'
)
{
$LayoutObject->Block(
Name => 'EmptyDefaultButton',
);
}
}
}
else {
# create & return output
my $Link = 'SortBy=' . $LayoutObject->Ascii2Html( Text => $SortBy )
. ';OrderBy=' . $LayoutObject->Ascii2Html( Text => $OrderByCurrent )
. ';Filter=' . $LayoutObject->Ascii2Html( Text => $FilterCurrent )
. ';Subaction=' . $LayoutObject->Ascii2Html( Text => $Self->{Subaction} )
. ';';
my %PageNav = $LayoutObject->PageNavBar(
Limit => 10000,
StartHit => $StartHit,
PageShown => $PageShown,
AllHits => $AllTickets,
Action => 'Action=CustomerTicketOverview',
Link => $Link,
IDPrefix => 'CustomerTicketOverview',
);
my $OrderBy = 'Down';
if ( $OrderByCurrent eq 'Down' ) {
$OrderBy = 'Up';
}
my $Sort = '';
my $StateSort = '';
my $TicketSort = '';
my $TitleSort = '';
my $AgeSort = '';
my $QueueSort = '';
my $OwnerSort = '';
# this sets the opposite to the $OrderBy
if ( $OrderBy eq 'Down' ) {
$Sort = 'SortAscending';
}
if ( $OrderBy eq 'Up' ) {
$Sort = 'SortDescending';
}
if ( $SortBy eq 'State' ) {
$StateSort = $Sort;
}
elsif ( $SortBy eq 'Ticket' ) {
$TicketSort = $Sort;
}
elsif ( $SortBy eq 'Title' ) {
$TitleSort = $Sort;
}
elsif ( $SortBy eq 'Age' ) {
$AgeSort = $Sort;
}
elsif ( $SortBy eq 'Queue' ) {
$QueueSort = $Sort;
}
elsif ( $SortBy eq 'Owner' ) {
$OwnerSort = $Sort;
}
$LayoutObject->Block(
Name => 'Filled',
Data => {
%Param,
%PageNav,
OrderBy => $OrderBy,
StateSort => $StateSort,
TicketSort => $TicketSort,
TitleSort => $TitleSort,
AgeSort => $AgeSort,
Filter => $FilterCurrent,
},
);
my $Owner = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverview')->{Owner};
my $Queue = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverview')->{Queue};
if ($Owner) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageOwner',
Data => {
OrderBy => $OrderBy,
OwnerSort => $OwnerSort,
Filter => $FilterCurrent,
},
);
}
if ($Queue) {
$LayoutObject->Block(
Name => 'OverviewNavBarPageQueue',
Data => {
OrderBy => $OrderBy,
QueueSort => $QueueSort,
Filter => $FilterCurrent,
},
);
}
# show header filter
for my $Key ( sort keys %NavBarFilter ) {
$LayoutObject->Block(
Name => 'FilterHeader',
Data => {
%Param,
%{ $NavBarFilter{$Key} },
},
);
}
# show footer filter - show only if more the one page is available
if ( $AllTickets > $PageShown ) {
$LayoutObject->Block(
Name => 'FilterFooter',
Data => {
%Param,
%PageNav,
},
);
}
for my $Key ( sort keys %NavBarFilter ) {
if ( $AllTickets > $PageShown ) {
$LayoutObject->Block(
Name => 'FilterFooterItem',
Data => {
%{ $NavBarFilter{$Key} },
},
);
}
}
# get the dynamic fields for this screen
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get dynamic field config for frontend module
my $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::CustomerTicketOverview")->{DynamicField};
my $DynamicField = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $DynamicFieldFilter || {},
);
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @CustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @CustomerDynamicFields, $DynamicFieldConfig;
}
$DynamicField = \@CustomerDynamicFields;
# Dynamic fields
# cycle trough the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $Label = $DynamicFieldConfig->{Label};
# get field sortable condition
my $IsSortable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsSortable',
);
if ($IsSortable) {
my $CSS = '';
if (
$SortBy
&& ( $SortBy eq ( 'DynamicField_' . $DynamicFieldConfig->{Name} ) )
)
{
if ( $OrderByCurrent && ( $OrderByCurrent eq 'Up' ) ) {
$OrderBy = 'Down';
$CSS .= ' SortDescending';
}
else {
$OrderBy = 'Up';
$CSS .= ' SortAscending';
}
}
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField',
Data => {
%Param,
CSS => $CSS,
},
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldSortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
Filter => $FilterCurrent,
},
);
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name},
Data => {
%Param,
CSS => $CSS,
},
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_'
. $DynamicFieldConfig->{Name}
. '_Sortable',
Data => {
%Param,
OrderBy => $OrderBy,
Label => $Label,
DynamicFieldName => $DynamicFieldConfig->{Name},
Filter => $FilterCurrent,
},
);
}
else {
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField',
Data => {
%Param,
},
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicFieldNotSortable',
Data => {
%Param,
Label => $Label,
},
);
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_' . $DynamicFieldConfig->{Name},
Data => {
%Param,
},
);
$LayoutObject->Block(
Name => 'OverviewNavBarPageDynamicField_'
. $DynamicFieldConfig->{Name}
. '_NotSortable',
Data => {
%Param,
Label => $Label,
},
);
}
}
my @ViewableTickets = $TicketObject->TicketSearch(
%{ $Filters{ $Self->{Subaction} }->{$FilterCurrent}->{Search} },
%SearchInArchive,
Result => 'ARRAY',
);
# show tickets
$Counter = 0;
for my $TicketID (@ViewableTickets) {
$Counter++;
if (
$Counter >= $StartHit
&& $Counter < ( $PageShown + $StartHit )
)
{
$Self->ShowTicketStatus( TicketID => $TicketID );
}
}
}
# create & return output
my $Title = $Self->{Subaction};
if ( $Title eq 'MyTickets' ) {
$Title = Translatable('My Tickets');
}
elsif ( $Title eq 'CompanyTickets' ) {
$Title = Translatable('Company Tickets');
}
my $Refresh = '';
if ( $Self->{UserRefreshTime} ) {
$Refresh = 60 * $Self->{UserRefreshTime};
}
my $Output = $LayoutObject->CustomerHeader(
Title => $Title,
Refresh => $Refresh,
);
# build NavigationBar
$Output .= $LayoutObject->CustomerNavigationBar();
$Output .= $LayoutObject->Output(
TemplateFile => 'CustomerTicketOverview',
Data => \%Param,
);
# get page footer
$Output .= $LayoutObject->CustomerFooter();
# return page
return $Output;
}
# ShowTicket
sub ShowTicketStatus {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
my $ArticleObject = $Kernel::OM->Get('Kernel::System::Ticket::Article');
my $CommunicationChannelObject = $Kernel::OM->Get('Kernel::System::CommunicationChannel');
my $TicketID = $Param{TicketID} || return;
# Get last customer article.
my @ArticleList = $ArticleObject->ArticleList(
TicketID => $Param{TicketID},
IsVisibleForCustomer => 1,
OnlyLast => 1,
);
my %Article;
if ( $ArticleList[0] && IsHashRefWithData( $ArticleList[0] ) ) {
my $ArticleBackendObject = $ArticleObject->BackendForArticle( %{ $ArticleList[0] } );
%Article = $ArticleBackendObject->ArticleGet(
TicketID => $Param{TicketID},
ArticleID => $ArticleList[0]->{ArticleID},
);
}
# get ticket info
my %Ticket = $TicketObject->TicketGet(
TicketID => $TicketID,
DynamicFields => 0,
);
my $Subject;
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $SmallViewColumnHeader = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverview')->{ColumnHeader};
# Check if the last customer subject or ticket title should be shown.
# If ticket title should be shown, check if there are articles, because ticket title
# could be related with a subject of an article which does not visible for customer (see bug#13614).
# If there is no subject, set to 'Untitled'.
if ( $SmallViewColumnHeader eq 'LastCustomerSubject' ) {
$Subject = $Article{Subject} || '';
}
elsif ( $SmallViewColumnHeader eq 'TicketTitle' && $ArticleList[0] ) {
$Subject = $Ticket{Title};
}
else {
$Subject = Translatable('Untitled!');
}
# Condense down the subject.
$Subject = $TicketObject->TicketSubjectClean(
TicketNumber => $Ticket{TicketNumber},
Subject => $Subject,
);
# Age design.
$Ticket{CustomerAge} = $LayoutObject->CustomerAge(
Age => $Ticket{Age},
Space => ' '
) || 0;
# return ticket information if there is no article
if ( !IsHashRefWithData( \%Article ) ) {
$Article{State} = $Ticket{State};
$Article{TicketNumber} = $Ticket{TicketNumber};
$Article{Body} = $LayoutObject->{LanguageObject}->Translate('This item has no articles yet.');
}
# customer info (customer name)
if ( $Article{CustomerUserID} ) {
$Param{CustomerName} = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerName(
UserLogin => $Article{CustomerUserID},
);
$Param{CustomerName} = '(' . $Param{CustomerName} . ')' if ( $Param{CustomerName} );
}
# add block
$LayoutObject->Block(
Name => 'Record',
Data => {
%Article,
%Ticket,
Subject => $Subject,
%Param,
},
);
my $Owner = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverview')->{Owner};
my $Queue = $ConfigObject->Get('Ticket::Frontend::CustomerTicketOverview')->{Queue};
if ($Owner) {
my $OwnerName = $Kernel::OM->Get('Kernel::System::User')->UserName( UserID => $Ticket{OwnerID} );
$LayoutObject->Block(
Name => 'RecordOwner',
Data => {
OwnerName => $OwnerName,
},
);
}
if ($Queue) {
$LayoutObject->Block(
Name => 'RecordQueue',
Data => {
%Ticket,
},
);
}
# get the dynamic fields for this screen
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
# get dynamic field config for frontend module
my $DynamicFieldFilter = $ConfigObject->Get("Ticket::Frontend::CustomerTicketOverview")->{DynamicField};
my $DynamicField = $DynamicFieldObject->DynamicFieldListGet(
Valid => 1,
ObjectType => ['Ticket'],
FieldFilter => $DynamicFieldFilter || {},
);
# reduce the dynamic fields to only the ones that are desinged for customer interface
my @CustomerDynamicFields;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
my $IsCustomerInterfaceCapable = $BackendObject->HasBehavior(
DynamicFieldConfig => $DynamicFieldConfig,
Behavior => 'IsCustomerInterfaceCapable',
);
next DYNAMICFIELD if !$IsCustomerInterfaceCapable;
push @CustomerDynamicFields, $DynamicFieldConfig;
}
$DynamicField = \@CustomerDynamicFields;
# Dynamic fields
# cycle trough the activated Dynamic Fields for this screen
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# get field value
my $Value = $BackendObject->ValueGet(
DynamicFieldConfig => $DynamicFieldConfig,
ObjectID => $Ticket{TicketID},
);
my $ValueStrg = $BackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Value,
ValueMaxChars => 20,
LayoutObject => $LayoutObject,
);
$LayoutObject->Block(
Name => 'RecordDynamicField',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
if ( $ValueStrg->{Link} ) {
$LayoutObject->Block(
Name => 'RecordDynamicFieldLink',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
Link => $ValueStrg->{Link},
$DynamicFieldConfig->{Name} => $ValueStrg->{Title},
},
);
}
else {
$LayoutObject->Block(
Name => 'RecordDynamicFieldPlain',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
}
# example of dynamic fields order customization
$LayoutObject->Block(
Name => 'RecordDynamicField' . $DynamicFieldConfig->{Name},
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
if ( $ValueStrg->{Link} ) {
$LayoutObject->Block(
Name => 'RecordDynamicField' . $DynamicFieldConfig->{Name} . 'Link',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
Link => $ValueStrg->{Link},
$DynamicFieldConfig->{Name} => $ValueStrg->{Title},
},
);
}
else {
$LayoutObject->Block(
Name => 'RecordDynamicField' . $DynamicFieldConfig->{Name} . 'Plain',
Data => {
Value => $ValueStrg->{Value},
Title => $ValueStrg->{Title},
},
);
}
}
return;
}
1;