init III
This commit is contained in:
519
Perl OTRS/Kernel/Output/HTML/Dashboard/AppointmentCalendar.pm
Normal file
519
Perl OTRS/Kernel/Output/HTML/Dashboard/AppointmentCalendar.pm
Normal file
@@ -0,0 +1,519 @@
|
||||
# --
|
||||
# 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::AppointmentCalendar;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::Language qw(Translatable);
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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');
|
||||
|
||||
# get current filter
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'DashboardCalendarAppointmentFilter' . $Self->{Name};
|
||||
if ( $Self->{Name} eq $Name ) {
|
||||
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
|
||||
}
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# remember filter
|
||||
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') ) {
|
||||
$Kernel::OM->Get('Kernel::System::User')->SetPreferences(
|
||||
UserID => $Self->{UserID},
|
||||
Key => $PreferencesKey,
|
||||
Value => $Self->{Filter},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# set default filter if not set yet
|
||||
if ( !$Self->{Filter} ) {
|
||||
$Self->{Filter} = $Self->{$PreferencesKey} || $Self->{Config}->{Filter} || 'Today';
|
||||
}
|
||||
|
||||
# setup the prefrences keys
|
||||
$Self->{PrefKeyShown} = 'AppointmentDashboardPref' . $Self->{Name} . '-Shown';
|
||||
$Self->{PrefKeyRefresh} = 'AppointmentDashboardPref' . $Self->{Name} . '-Refresh';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyShown} }
|
||||
|| $Self->{Config}->{Limit} || 10;
|
||||
|
||||
$Self->{PageRefresh} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyRefresh} }
|
||||
|| 1;
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name} . '::';
|
||||
|
||||
# get configuration for the full name order for user names
|
||||
# and append it to the cache key to make sure, that the
|
||||
# correct data will be displayed every time
|
||||
my $FirstnameLastNameOrder = $ConfigObject->Get('FirstnameLastnameOrder') || 0;
|
||||
$Self->{CacheKey} .= '::' . $FirstnameLastNameOrder;
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown'),
|
||||
Name => $Self->{PrefKeyShown},
|
||||
Block => 'Option',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
{
|
||||
Desc => Translatable('Refresh (minutes)'),
|
||||
Name => $Self->{PrefKeyRefresh},
|
||||
Block => 'Option',
|
||||
Data => {
|
||||
0 => Translatable('off'),
|
||||
1 => '1',
|
||||
2 => '2',
|
||||
5 => '5',
|
||||
7 => '7',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
},
|
||||
SelectedID => $Self->{PageRefresh},
|
||||
Translation => 1,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
CanRefresh => 1,
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get config settings
|
||||
my $IdleMinutes = $Self->{Config}->{IdleMinutes} || 60;
|
||||
my $SortBy = $Self->{Config}->{SortBy} || 'UserFullname';
|
||||
|
||||
# get a list of at least readable calendars
|
||||
my @CalendarList = $Kernel::OM->Get('Kernel::System::Calendar')->CalendarList(
|
||||
UserID => $Self->{UserID},
|
||||
ValidID => 1,
|
||||
);
|
||||
|
||||
# collect calendar and appointment data
|
||||
# separate appointments to today, tomorrow
|
||||
# and the next five days (soon)
|
||||
my %Calendars;
|
||||
my %Appointments;
|
||||
my %AppointmentsCount;
|
||||
|
||||
# check cache
|
||||
my $CacheKeyCalendars = $Self->{CacheKey} . $Self->{UserID} . '::Calendars';
|
||||
my $CacheKeyAppointments = $Self->{CacheKey} . $Self->{UserID} . '::Appointments::' . $Self->{Filter};
|
||||
my $CacheKeyAppointmentsCount = $Self->{CacheKey} . $Self->{UserID} . '::AppointmentsCount';
|
||||
|
||||
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
||||
|
||||
# get cached data
|
||||
my $DataCalendars = $CacheObject->Get(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyCalendars,
|
||||
);
|
||||
my $DataAppointments = $CacheObject->Get(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyAppointments,
|
||||
);
|
||||
my $DataAppointmentsCount = $CacheObject->Get(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyAppointmentsCount,
|
||||
);
|
||||
|
||||
# if cache is up-to-date, use the given data
|
||||
if (
|
||||
ref $DataCalendars eq 'HASH'
|
||||
&& ref $DataAppointments eq 'HASH'
|
||||
&& ref $DataAppointmentsCount eq 'HASH'
|
||||
)
|
||||
{
|
||||
%Calendars = %{$DataCalendars};
|
||||
%Appointments = %{$DataAppointments};
|
||||
%AppointmentsCount = %{$DataAppointmentsCount};
|
||||
}
|
||||
|
||||
# collect the data again if necessary
|
||||
else {
|
||||
|
||||
CALENDAR:
|
||||
for my $Calendar (@CalendarList) {
|
||||
|
||||
next CALENDAR if !$Calendar;
|
||||
next CALENDAR if !IsHashRefWithData($Calendar);
|
||||
next CALENDAR if $Calendars{ $Calendar->{CalendarID} };
|
||||
|
||||
$Calendars{ $Calendar->{CalendarID} } = $Calendar;
|
||||
}
|
||||
|
||||
# set cache for calendars
|
||||
$CacheObject->Set(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyCalendars,
|
||||
Value => \%Calendars,
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
|
||||
# prepare calendar appointments
|
||||
my %AppointmentsUnsorted;
|
||||
|
||||
# get a local appointment object
|
||||
my $AppointmentObject = $Kernel::OM->Get('Kernel::System::Calendar::Appointment');
|
||||
|
||||
my $DateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
|
||||
CALENDARID:
|
||||
for my $CalendarID ( sort keys %Calendars ) {
|
||||
|
||||
next CALENDARID if !$CalendarID;
|
||||
|
||||
my @Appointments = $AppointmentObject->AppointmentList(
|
||||
CalendarID => $CalendarID,
|
||||
StartTime => $DateTimeObject->ToString(),
|
||||
Result => 'HASH',
|
||||
);
|
||||
|
||||
next CALENDARID if !IsArrayRefWithData( \@Appointments );
|
||||
|
||||
APPOINTMENT:
|
||||
for my $Appointment (@Appointments) {
|
||||
|
||||
next APPOINTMENT if !$Appointment;
|
||||
next APPOINTMENT if !IsHashRefWithData($Appointment);
|
||||
|
||||
# Save system time of StartTime.
|
||||
my $StartTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $Appointment->{StartTime},
|
||||
},
|
||||
);
|
||||
$Appointment->{SystemTimeStart} = $StartTimeObject->ToEpoch();
|
||||
|
||||
# save appointment in new hash for later sorting
|
||||
$AppointmentsUnsorted{ $Appointment->{AppointmentID} } = $Appointment;
|
||||
}
|
||||
}
|
||||
|
||||
# get datetime strings for the dates with related offsets
|
||||
# (today, tomorrow and soon - which means the next 5 days
|
||||
# except today and tomorrow counted from current timestamp)
|
||||
my %DateOffset = (
|
||||
Today => 0,
|
||||
Tomorrow => 86400,
|
||||
PlusTwo => 172800,
|
||||
PlusThree => 259200,
|
||||
PlusFour => 345600,
|
||||
);
|
||||
|
||||
my %Dates;
|
||||
|
||||
my $CurrentSystemTime = $DateTimeObject->ToEpoch();
|
||||
|
||||
for my $DateOffsetKey ( sort keys %DateOffset ) {
|
||||
|
||||
# Get date components with current offset.
|
||||
my $OffsetTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
Epoch => $CurrentSystemTime + $DateOffset{$DateOffsetKey},
|
||||
},
|
||||
);
|
||||
my $OffsetTimeSettings = $OffsetTimeObject->Get();
|
||||
|
||||
$Dates{$DateOffsetKey} = sprintf(
|
||||
"%02d-%02d-%02d",
|
||||
$OffsetTimeSettings->{Year},
|
||||
$OffsetTimeSettings->{Month},
|
||||
$OffsetTimeSettings->{Day}
|
||||
);
|
||||
}
|
||||
|
||||
$AppointmentsCount{Today} = 0;
|
||||
$AppointmentsCount{Tomorrow} = 0;
|
||||
$AppointmentsCount{Soon} = 0;
|
||||
|
||||
APPOINTMENTID:
|
||||
for my $AppointmentID ( sort keys %AppointmentsUnsorted ) {
|
||||
|
||||
next APPOINTMENTID if !$AppointmentID;
|
||||
next APPOINTMENTID if !IsHashRefWithData( $AppointmentsUnsorted{$AppointmentID} );
|
||||
next APPOINTMENTID if !$AppointmentsUnsorted{$AppointmentID}->{StartTime};
|
||||
|
||||
# Extract current date (without time).
|
||||
my $StartTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $AppointmentsUnsorted{$AppointmentID}->{StartTime},
|
||||
},
|
||||
);
|
||||
my $StartTimeSettings = $StartTimeObject->Get();
|
||||
|
||||
my $StartDate = sprintf(
|
||||
"%02d-%02d-%02d",
|
||||
$StartTimeSettings->{Year},
|
||||
$StartTimeSettings->{Month},
|
||||
$StartTimeSettings->{Day}
|
||||
);
|
||||
|
||||
# today
|
||||
if ( $StartDate eq $Dates{Today} ) {
|
||||
|
||||
$AppointmentsCount{Today}++;
|
||||
|
||||
if ( $Self->{Filter} eq 'Today' ) {
|
||||
$Appointments{$AppointmentID} = $AppointmentsUnsorted{$AppointmentID};
|
||||
}
|
||||
}
|
||||
|
||||
# tomorrow
|
||||
elsif ( $StartDate eq $Dates{Tomorrow} ) {
|
||||
|
||||
$AppointmentsCount{Tomorrow}++;
|
||||
|
||||
if ( $Self->{Filter} eq 'Tomorrow' ) {
|
||||
$Appointments{$AppointmentID} = $AppointmentsUnsorted{$AppointmentID};
|
||||
}
|
||||
}
|
||||
|
||||
# soon
|
||||
elsif (
|
||||
$StartDate eq $Dates{PlusTwo}
|
||||
|| $StartDate eq $Dates{PlusThree}
|
||||
|| $StartDate eq $Dates{PlusFour}
|
||||
)
|
||||
{
|
||||
$AppointmentsCount{Soon}++;
|
||||
|
||||
if ( $Self->{Filter} eq 'Soon' ) {
|
||||
$Appointments{$AppointmentID} = $AppointmentsUnsorted{$AppointmentID};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# set cache for appointments
|
||||
$CacheObject->Set(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyAppointments,
|
||||
Value => \%Appointments,
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
|
||||
# set cache for appointments count
|
||||
$CacheObject->Set(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKeyAppointmentsCount,
|
||||
Value => \%AppointmentsCount,
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
}
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $AppointmentTableBlock = 'ContentSmallTable';
|
||||
|
||||
# prepare appointments table
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallTable',
|
||||
);
|
||||
|
||||
my $Count = 0;
|
||||
my $Shown = 0;
|
||||
|
||||
APPOINTMENTID:
|
||||
for my $AppointmentID (
|
||||
sort {
|
||||
$Appointments{$a}->{SystemTimeStart} <=> $Appointments{$b}->{SystemTimeStart}
|
||||
|| $Appointments{$a}->{AppointmentID} <=> $Appointments{$b}->{AppointmentID}
|
||||
} keys %Appointments
|
||||
)
|
||||
{
|
||||
# pagination handling
|
||||
last APPOINTMENTID if $Shown >= $Self->{PageShown};
|
||||
|
||||
if ( ( $Self->{StartHit} - 1 ) > $Count ) {
|
||||
$Count++;
|
||||
next APPOINTMENTID;
|
||||
}
|
||||
|
||||
my $StartTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $Appointments{$AppointmentID}->{StartTime},
|
||||
},
|
||||
);
|
||||
|
||||
# Convert time to user time zone.
|
||||
if ( $Self->{UserTimeZone} ) {
|
||||
$StartTimeObject->ToTimeZone(
|
||||
TimeZone => $Self->{UserTimeZone},
|
||||
);
|
||||
}
|
||||
|
||||
my $StartTimeSettings = $StartTimeObject->Get();
|
||||
|
||||
# prepare dates and times
|
||||
my $StartTime = sprintf( "%02d:%02d", $StartTimeSettings->{Hour}, $StartTimeSettings->{Minute} );
|
||||
my $StartTimeLong = $LayoutObject->{LanguageObject}
|
||||
->FormatTimeString( $Appointments{$AppointmentID}->{StartTime}, 'DateFormatLong' );
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallAppointmentRow',
|
||||
Data => {
|
||||
AppointmentID => $Appointments{$AppointmentID}->{AppointmentID},
|
||||
Title => $Appointments{$AppointmentID}->{Title},
|
||||
StartTime => $StartTime,
|
||||
StartTimeLong => $StartTimeLong,
|
||||
Color => $Calendars{ $Appointments{$AppointmentID}->{CalendarID} }->{Color},
|
||||
CalendarName => $Calendars{ $Appointments{$AppointmentID}->{CalendarID} }->{CalendarName},
|
||||
},
|
||||
);
|
||||
|
||||
# increase shown item count
|
||||
$Shown++;
|
||||
}
|
||||
|
||||
if ( !IsHashRefWithData( \%Appointments ) ) {
|
||||
|
||||
# show up message for no appointments
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallAppointmentNone',
|
||||
);
|
||||
}
|
||||
|
||||
# set css class
|
||||
my %Summary;
|
||||
$Summary{ $Self->{Filter} . '::Selected' } = 'Selected';
|
||||
|
||||
# filter bar
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallAppointmentFilter',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
%Summary,
|
||||
Name => $Self->{Name},
|
||||
TodayCount => $AppointmentsCount{Today},
|
||||
TomorrowCount => $AppointmentsCount{Tomorrow},
|
||||
SoonCount => $AppointmentsCount{Soon},
|
||||
},
|
||||
);
|
||||
|
||||
# add page nav bar
|
||||
my $Total = $AppointmentsCount{ $Self->{Filter} } || 0;
|
||||
my $LinkPage = 'Subaction=Element;Name=' . $Self->{Name} . ';Filter=' . $Self->{Filter} . ';';
|
||||
my %PageNav = $LayoutObject->PageNavBar(
|
||||
StartHit => $Self->{StartHit},
|
||||
PageShown => $Self->{PageShown},
|
||||
AllHits => $Total || 1,
|
||||
Action => 'Action=' . $LayoutObject->{Action},
|
||||
Link => $LinkPage,
|
||||
WindowSize => 5,
|
||||
AJAXReplace => 'Dashboard' . $Self->{Name},
|
||||
IDPrefix => 'Dashboard' . $Self->{Name},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallAppointmentFilterNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
|
||||
# check for refresh time
|
||||
my $Refresh = $LayoutObject->{ $Self->{PrefKeyRefresh} } // 1;
|
||||
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardAppointmentCalendar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh * 60,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
# Send data to JS.
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'AppointmentCalendar',
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh * 60,
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
214
Perl OTRS/Kernel/Output/HTML/Dashboard/Calendar.pm
Normal file
214
Perl OTRS/Kernel/Output/HTML/Dashboard/Calendar.pm
Normal file
@@ -0,0 +1,214 @@
|
||||
# --
|
||||
# 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::Calendar;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $ObjectManagerDisabled = 1;
|
||||
|
||||
sub new {
|
||||
my ( $Type, %Param ) = @_;
|
||||
|
||||
# allocate new hash for object
|
||||
my $Self = {%Param};
|
||||
bless( $Self, $Type );
|
||||
|
||||
# get needed objects
|
||||
for my $Needed (qw(Config Name UserID)) {
|
||||
die "Got no $Needed!" if ( !$Self->{$Needed} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
CacheKey => 'Calendar'
|
||||
. $Self->{UserID} . '-'
|
||||
. $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage},
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# find tickets with reached times in near future
|
||||
my $PendingReminderStateTypes = $Kernel::OM->Get('Kernel::Config')->Get('Ticket::PendingReminderStateType');
|
||||
|
||||
my %Map = (
|
||||
Escalation => [
|
||||
|
||||
# text for content table
|
||||
'Escalation',
|
||||
|
||||
# search attributes
|
||||
{
|
||||
|
||||
# where escalation time reached
|
||||
TicketEscalationTimeNewerMinutes => 15,
|
||||
|
||||
# sort
|
||||
SortBy => 'EscalationTime',
|
||||
OrderBy => 'Up',
|
||||
},
|
||||
],
|
||||
Pending => [
|
||||
|
||||
# text for content table
|
||||
'Reminder Reached',
|
||||
|
||||
# search attributes
|
||||
{
|
||||
|
||||
# only pending reminder tickets
|
||||
StateType => $PendingReminderStateTypes,
|
||||
|
||||
# where pending time reached in
|
||||
TicketPendingTimeNewerMinutes => 15,
|
||||
|
||||
# sort
|
||||
SortBy => 'PendingTime',
|
||||
OrderBy => 'Up',
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
if ( $Self->{Config}->{OwnerOnly} ) {
|
||||
$Map{Escalation}->[1]->{OwnerIDs} = [ $Self->{UserID} ];
|
||||
$Map{Pending}->[1]->{OwnerIDs} = [ $Self->{UserID} ];
|
||||
}
|
||||
|
||||
# get needed objects
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my %Date;
|
||||
for my $Type ( sort keys %Map ) {
|
||||
|
||||
# search tickets
|
||||
my @TicketIDs = $TicketObject->TicketSearch(
|
||||
|
||||
# add search attributes
|
||||
%{ $Map{$Type}->[1] },
|
||||
|
||||
Result => 'ARRAY',
|
||||
Permission => $Self->{Config}->{Permission} || 'ro',
|
||||
UserID => $Self->{UserID},
|
||||
Limit => 25,
|
||||
);
|
||||
|
||||
# get ticket attributes
|
||||
TICKETID:
|
||||
for my $TicketID (@TicketIDs) {
|
||||
|
||||
my %Ticket = $TicketObject->TicketGet(
|
||||
TicketID => $TicketID,
|
||||
UserID => $Self->{UserID},
|
||||
DynamicFields => 0,
|
||||
);
|
||||
my $TimeStamp;
|
||||
my $TimeTill;
|
||||
if ( $Type eq 'Escalation' ) {
|
||||
|
||||
next TICKETID if !$Ticket{EscalationTime};
|
||||
next TICKETID if !$Ticket{EscalationDestinationDate};
|
||||
|
||||
$TimeTill = $Ticket{EscalationTime};
|
||||
$TimeStamp = $Ticket{EscalationDestinationDate};
|
||||
}
|
||||
elsif ( $Type eq 'Pending' ) {
|
||||
|
||||
# only show own pending tickets
|
||||
if (
|
||||
$Ticket{OwnerID} ne $Self->{UserID}
|
||||
&& $Ticket{ResponsibleID} ne $Self->{UserID}
|
||||
)
|
||||
{
|
||||
next TICKETID;
|
||||
}
|
||||
|
||||
# get current system datetime object
|
||||
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
|
||||
my $DestDate = $CurSystemDateTimeObject->ToEpoch() + $Ticket{UntilTime};
|
||||
my $TimeStampObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
Epoch => $DestDate,
|
||||
},
|
||||
);
|
||||
if ($TimeStampObject) {
|
||||
$TimeStamp = $TimeStampObject->ToString();
|
||||
}
|
||||
|
||||
$TimeTill = $Ticket{UntilTime};
|
||||
}
|
||||
|
||||
# remember attributes for content table
|
||||
$Date{ $TimeStamp . '::' . $TicketID } = {
|
||||
Type => $Type,
|
||||
Text => $Map{$Type}->[0],
|
||||
Object => 'Ticket',
|
||||
ObjectID => $Ticket{TicketID},
|
||||
ObjectNumber => $Ticket{TicketNumber},
|
||||
Title => $Ticket{Title},
|
||||
Link => "Action=AgentTicketZoom;TicketID=$Ticket{TicketID}",
|
||||
TimeStamp => $TimeStamp,
|
||||
TimeTill => $TimeTill,
|
||||
In => $LayoutObject->CustomerAge(
|
||||
Age => $TimeTill,
|
||||
Space => ' ',
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
# show content rows
|
||||
my $Count = 0;
|
||||
DATE:
|
||||
for my $Data ( sort keys %Date ) {
|
||||
$Count++;
|
||||
last DATE if $Count > $Self->{Config}->{Limit};
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCalendarOverviewRow',
|
||||
Data => $Date{$Data},
|
||||
);
|
||||
}
|
||||
|
||||
# fill-up if no content exists
|
||||
if ( !$Count ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCalendarOverviewNone',
|
||||
Data => {},
|
||||
);
|
||||
}
|
||||
|
||||
# render content
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCalendarOverview',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
# return content
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
66
Perl OTRS/Kernel/Output/HTML/Dashboard/CmdOutput.pm
Normal file
66
Perl OTRS/Kernel/Output/HTML/Dashboard/CmdOutput.pm
Normal file
@@ -0,0 +1,66 @@
|
||||
# --
|
||||
# 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::CmdOutput;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# command to run
|
||||
my $Cmd = $Self->{Config}->{Cmd};
|
||||
|
||||
my $CmdOutput = qx{$Cmd 2>&1};
|
||||
|
||||
$Kernel::OM->Get('Kernel::System::Encode')->EncodeInput( \$CmdOutput );
|
||||
|
||||
my $Content = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Output(
|
||||
TemplateFile => 'AgentDashboardCmdOutput',
|
||||
Data => {
|
||||
CmdOutput => $CmdOutput,
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,253 @@
|
||||
# --
|
||||
# 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::CustomerCompanyInformation;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# caching not needed
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get customer id from customer user data if neccessary
|
||||
if ( !$Param{CustomerID} && $Param{CustomerUserID} ) {
|
||||
my %CustomerUserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
|
||||
User => $Self->{CustomerUserID},
|
||||
);
|
||||
|
||||
$Param{CustomerID} = $CustomerUserData{UserCustomerID} || '';
|
||||
}
|
||||
|
||||
return if !$Param{CustomerID};
|
||||
|
||||
my %CustomerCompany = $Kernel::OM->Get('Kernel::System::CustomerCompany')->CustomerCompanyGet(
|
||||
CustomerID => $Param{CustomerID},
|
||||
);
|
||||
|
||||
my $CustomerCompanyConfig = $Kernel::OM->Get('Kernel::Config')->Get( $CustomerCompany{Source} || '' );
|
||||
return if ref $CustomerCompanyConfig ne 'HASH';
|
||||
return if ref $CustomerCompanyConfig->{Map} ne 'ARRAY';
|
||||
|
||||
return if !%CustomerCompany;
|
||||
|
||||
# Get needed objects
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
my $ValidObject = $Kernel::OM->Get('Kernel::System::Valid');
|
||||
my $CompanyIsValid;
|
||||
|
||||
# make ValidID readable
|
||||
if ( $CustomerCompany{ValidID} ) {
|
||||
my @ValidIDs = $ValidObject->ValidIDsGet();
|
||||
$CompanyIsValid = grep { $CustomerCompany{ValidID} == $_ } @ValidIDs;
|
||||
|
||||
$CustomerCompany{ValidID} = $ValidObject->ValidLookup(
|
||||
ValidID => $CustomerCompany{ValidID},
|
||||
);
|
||||
|
||||
$CustomerCompany{ValidID} = $LayoutObject->{LanguageObject}->Translate( $CustomerCompany{ValidID} );
|
||||
}
|
||||
|
||||
my $DynamicFieldConfigs = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
||||
ObjectType => 'CustomerCompany',
|
||||
);
|
||||
|
||||
my %DynamicFieldLookup = map { $_->{Name} => $_ } @{$DynamicFieldConfigs};
|
||||
|
||||
# Get dynamic field backend object.
|
||||
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
||||
|
||||
# get attributes
|
||||
my $Attributes = $Self->{Config}->{Attributes};
|
||||
|
||||
# get customer company config mapping
|
||||
my @CustomerCompanyConfigMap = @{ $CustomerCompanyConfig->{Map} };
|
||||
|
||||
if ($Attributes) {
|
||||
|
||||
my @NewMap = $Self->_AttributesGet(
|
||||
Attributes => $Attributes,
|
||||
CustomerCompany => \%CustomerCompany,
|
||||
);
|
||||
|
||||
@CustomerCompanyConfigMap = @NewMap;
|
||||
}
|
||||
|
||||
ENTRY:
|
||||
for my $Entry (@CustomerCompanyConfigMap) {
|
||||
my $Key = $Entry->[0];
|
||||
|
||||
# do not show items if they're not marked as visible
|
||||
next ENTRY if !$Entry->[3];
|
||||
|
||||
my $Label = $Entry->[1];
|
||||
my $Value = $CustomerCompany{$Key};
|
||||
|
||||
# render dynamic field values
|
||||
if ( $Entry->[5] eq 'dynamic_field' ) {
|
||||
if ( !IsArrayRefWithData($Value) ) {
|
||||
$Value = [$Value];
|
||||
}
|
||||
|
||||
my $DynamicFieldConfig = $DynamicFieldLookup{ $Entry->[2] };
|
||||
|
||||
next ENTRY if !$DynamicFieldConfig;
|
||||
|
||||
my @RenderedValues;
|
||||
VALUE:
|
||||
for my $Value ( @{$Value} ) {
|
||||
my $RenderedValue = $DynamicFieldBackendObject->DisplayValueRender(
|
||||
DynamicFieldConfig => $DynamicFieldConfig,
|
||||
Value => $Value,
|
||||
HTMLOutput => 0,
|
||||
LayoutObject => $LayoutObject,
|
||||
);
|
||||
|
||||
next VALUE if !IsHashRefWithData($RenderedValue) || !defined $RenderedValue->{Value};
|
||||
|
||||
# If there is configured show link in DF, save as map value.
|
||||
$Entry->[6] = $RenderedValue->{Link} ? $RenderedValue->{Link} : $Entry->[6];
|
||||
|
||||
push @RenderedValues, $RenderedValue->{Value};
|
||||
}
|
||||
|
||||
$Value = join ', ', @RenderedValues;
|
||||
$Label = $DynamicFieldConfig->{Label};
|
||||
}
|
||||
|
||||
# do not show empty entries
|
||||
next ENTRY if !length($Value);
|
||||
|
||||
$LayoutObject->Block( Name => "ContentSmallCustomerCompanyInformationRow" );
|
||||
|
||||
if ( $Key eq 'CustomerID' ) {
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerCompanyInformationRowLink",
|
||||
Data => {
|
||||
%CustomerCompany,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
URL =>
|
||||
'[% Env("Baselink") %]Action=AdminCustomerCompany;Subaction=Change;CustomerID=[% Data.CustomerID | uri %];Nav=Agent',
|
||||
Target => '',
|
||||
},
|
||||
);
|
||||
|
||||
next ENTRY;
|
||||
}
|
||||
|
||||
# check if a link must be placed
|
||||
if ( $Entry->[6] ) {
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerCompanyInformationRowLink",
|
||||
Data => {
|
||||
%CustomerCompany,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
URL => $Entry->[6],
|
||||
Target => '_blank',
|
||||
},
|
||||
);
|
||||
|
||||
next ENTRY;
|
||||
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerCompanyInformationRowText",
|
||||
Data => {
|
||||
%CustomerCompany,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
},
|
||||
);
|
||||
|
||||
if ( $Key eq 'CustomerCompanyName' && defined $CompanyIsValid && !$CompanyIsValid ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerCompanyInvalid',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCustomerCompanyInformation',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%CustomerCompany,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
sub _AttributesGet {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @AttributeArray = split ';', $Param{Attributes};
|
||||
my $CustomerCompany = $Param{CustomerCompany};
|
||||
my @Map;
|
||||
|
||||
my $CustomerCompanyConfig = $Kernel::OM->Get('Kernel::Config')->Get( $CustomerCompany->{Source} || '' );
|
||||
return if ref $CustomerCompanyConfig ne 'HASH';
|
||||
return if ref $CustomerCompanyConfig->{Map} ne 'ARRAY';
|
||||
|
||||
# define filtered map
|
||||
ENTRY:
|
||||
for my $Entry ( @{ $CustomerCompanyConfig->{Map} } ) {
|
||||
if ( grep { $_ eq $Entry->[0] } @AttributeArray ) {
|
||||
push @Map, $Entry;
|
||||
}
|
||||
}
|
||||
|
||||
return @Map;
|
||||
|
||||
}
|
||||
|
||||
1;
|
||||
258
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerIDList.pm
Normal file
258
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerIDList.pm
Normal file
@@ -0,0 +1,258 @@
|
||||
# --
|
||||
# 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::CustomerIDList;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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');
|
||||
|
||||
# get current filter
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'UserDashboardCustomerIDListFilter' . $Self->{Name};
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKey} }
|
||||
|| $Self->{Config}->{Limit};
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown customer ids'),
|
||||
Name => $Self->{PrefKey},
|
||||
Block => 'Option',
|
||||
|
||||
# Block => 'Input',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheTTL => undef,
|
||||
CacheKey => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return if !$Param{CustomerUserID};
|
||||
|
||||
# get needed objects
|
||||
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
||||
my $CustomerCompanyObject = $Kernel::OM->Get('Kernel::System::CustomerCompany');
|
||||
|
||||
# get all customer ids of this customer user
|
||||
my @CustomerIDs = $CustomerUserObject->CustomerIDs(
|
||||
User => $Param{CustomerUserID},
|
||||
);
|
||||
|
||||
# add page nav bar
|
||||
my $Total = scalar @CustomerIDs;
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $LinkPage = 'Subaction=Element;Name='
|
||||
. $Self->{Name} . ';'
|
||||
. 'CustomerUserID='
|
||||
. $LayoutObject->LinkEncode( $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 => 'ContentLargeCustomerIDNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# show change customer relations button if the agent has permission
|
||||
my $ChangeCustomerReleationsAccess = $LayoutObject->Permission(
|
||||
Action => 'AdminCustomerUserCustomer',
|
||||
Type => 'rw', # ro|rw possible
|
||||
);
|
||||
|
||||
if ($ChangeCustomerReleationsAccess) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerIDAdd',
|
||||
Data => {
|
||||
CustomerUserID => $Param{CustomerUserID},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# show links to edit customer id if the agent has permission
|
||||
my $EditCustomerIDPermission = $LayoutObject->Permission(
|
||||
Action => 'AdminCustomerCompany',
|
||||
Type => 'rw', # ro|rw possible
|
||||
);
|
||||
|
||||
@CustomerIDs = splice @CustomerIDs, $Self->{StartHit} - 1, $Self->{PageShown};
|
||||
|
||||
for my $CustomerID (@CustomerIDs) {
|
||||
|
||||
# get customer company data
|
||||
my %CustomerCompany = $CustomerCompanyObject->CustomerCompanyGet(
|
||||
CustomerID => $CustomerID,
|
||||
);
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerIDListRow',
|
||||
Data => {
|
||||
%Param,
|
||||
%CustomerCompany,
|
||||
CustomerID => $CustomerID,
|
||||
EditCustomerIDPermission => %CustomerCompany ? $EditCustomerIDPermission : 0,
|
||||
},
|
||||
);
|
||||
|
||||
# get ticket object
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
|
||||
my $TicketCountOpen = $TicketObject->TicketSearch(
|
||||
StateType => 'Open',
|
||||
CustomerID => $CustomerID,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
my $CustomerIDSQL = $Kernel::OM->Get('Kernel::System::DB')->QueryStringEscape( QueryString => $CustomerID );
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerIDListRowCustomerIDTicketsOpen',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $TicketCountOpen,
|
||||
CustomerID => $CustomerID,
|
||||
CustomerIDSQL => $CustomerIDSQL,
|
||||
},
|
||||
);
|
||||
|
||||
my $TicketCountClosed = $TicketObject->TicketSearch(
|
||||
StateType => 'Closed',
|
||||
CustomerID => $CustomerID,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerIDListRowCustomerIDTicketsClosed',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $TicketCountClosed,
|
||||
CustomerID => $CustomerID,
|
||||
CustomerIDSQL => $CustomerIDSQL,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# show "none" if there are no customers
|
||||
if ( !@CustomerIDs ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerIDListNone',
|
||||
Data => {},
|
||||
);
|
||||
}
|
||||
|
||||
# check for refresh time
|
||||
my $Refresh = '';
|
||||
if ( $Self->{UserRefreshTime} ) {
|
||||
$Refresh = 60 * $Self->{UserRefreshTime};
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'CustomerIDRefresh',
|
||||
Value => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh,
|
||||
CustomerID => $Param{CustomerID},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCustomerIDList',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
EditCustomerIDPermission => $EditCustomerIDPermission,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
169
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerIDStatus.pm
Normal file
169
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerIDStatus.pm
Normal file
@@ -0,0 +1,169 @@
|
||||
# --
|
||||
# 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::CustomerIDStatus;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# caching not needed
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return if !$Param{CustomerID};
|
||||
|
||||
my $CustomerIDRaw = $Param{CustomerID};
|
||||
|
||||
# get ticket object
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
|
||||
# escalated tickets
|
||||
my $Count = $TicketObject->TicketSearch(
|
||||
TicketEscalationTimeOlderMinutes => 1,
|
||||
CustomerIDRaw => $CustomerIDRaw,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerIDStatusEscalatedTickets',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $Count
|
||||
},
|
||||
);
|
||||
|
||||
# open tickets
|
||||
$Count = $TicketObject->TicketSearch(
|
||||
StateType => 'Open',
|
||||
CustomerIDRaw => $CustomerIDRaw,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerIDStatusOpenTickets',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $Count
|
||||
},
|
||||
);
|
||||
|
||||
# closed tickets
|
||||
$Count = $TicketObject->TicketSearch(
|
||||
StateType => 'Closed',
|
||||
CustomerIDRaw => $CustomerIDRaw,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerIDStatusClosedTickets',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $Count
|
||||
},
|
||||
);
|
||||
|
||||
# all tickets
|
||||
$Count = $TicketObject->TicketSearch(
|
||||
CustomerIDRaw => $CustomerIDRaw,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerIDStatusAllTickets',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $Count
|
||||
},
|
||||
);
|
||||
|
||||
# archived tickets
|
||||
if ( $Kernel::OM->Get('Kernel::Config')->Get('Ticket::ArchiveSystem') ) {
|
||||
$Count = $TicketObject->TicketSearch(
|
||||
CustomerIDRaw => $CustomerIDRaw,
|
||||
ArchiveFlags => ['y'],
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerIDStatusArchivedTickets',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $Count
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCustomerIDStatus',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -0,0 +1,222 @@
|
||||
# --
|
||||
# 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::CustomerUserInformation;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# caching not needed
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return if !$Param{CustomerUserID};
|
||||
|
||||
my %CustomerUser = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
|
||||
User => $Param{CustomerUserID},
|
||||
);
|
||||
|
||||
my $CustomerUserConfig = $Kernel::OM->Get('Kernel::Config')->Get( $CustomerUser{Source} || '' );
|
||||
return if ref $CustomerUserConfig ne 'HASH';
|
||||
return if ref $CustomerUserConfig->{Map} ne 'ARRAY';
|
||||
|
||||
return if !%CustomerUser;
|
||||
|
||||
# Get needed objects
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
my $ValidObject = $Kernel::OM->Get('Kernel::System::Valid');
|
||||
my $CustomerUserIsValid;
|
||||
|
||||
# make ValidID readable
|
||||
if ( $CustomerUser{ValidID} ) {
|
||||
my @ValidIDs = $ValidObject->ValidIDsGet();
|
||||
$CustomerUserIsValid = grep { $CustomerUser{ValidID} == $_ } @ValidIDs;
|
||||
|
||||
$CustomerUser{ValidID} = $ValidObject->ValidLookup(
|
||||
ValidID => $CustomerUser{ValidID},
|
||||
);
|
||||
|
||||
$CustomerUser{ValidID} = $LayoutObject->{LanguageObject}->Translate( $CustomerUser{ValidID} );
|
||||
}
|
||||
|
||||
if ( $CustomerUser{UserTitle} ) {
|
||||
$CustomerUser{UserTitle} = $LayoutObject->{LanguageObject}->Translate( $CustomerUser{UserTitle} );
|
||||
}
|
||||
|
||||
my $DynamicFieldConfigs = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
||||
ObjectType => 'CustomerUser',
|
||||
);
|
||||
|
||||
my %DynamicFieldLookup = map { $_->{Name} => $_ } @{$DynamicFieldConfigs};
|
||||
|
||||
# Get dynamic field backend object.
|
||||
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
|
||||
|
||||
ENTRY:
|
||||
for my $Entry ( @{ $CustomerUserConfig->{Map} } ) {
|
||||
my $Key = $Entry->[0];
|
||||
|
||||
# do not show items if they're not marked as visible
|
||||
next ENTRY if !$Entry->[3];
|
||||
|
||||
my $Label = $Entry->[1];
|
||||
my $Value = $CustomerUser{$Key};
|
||||
|
||||
# render dynamic field values
|
||||
if ( $Entry->[5] eq 'dynamic_field' ) {
|
||||
if ( !IsArrayRefWithData($Value) ) {
|
||||
$Value = [$Value];
|
||||
}
|
||||
|
||||
my $DynamicFieldConfig = $DynamicFieldLookup{ $Entry->[2] };
|
||||
|
||||
next ENTRY if !$DynamicFieldConfig;
|
||||
|
||||
my @RenderedValues;
|
||||
VALUE:
|
||||
for my $Value ( @{$Value} ) {
|
||||
my $RenderedValue = $DynamicFieldBackendObject->DisplayValueRender(
|
||||
DynamicFieldConfig => $DynamicFieldConfig,
|
||||
Value => $Value,
|
||||
HTMLOutput => 0,
|
||||
LayoutObject => $LayoutObject,
|
||||
);
|
||||
|
||||
next VALUE if !IsHashRefWithData($RenderedValue) || !defined $RenderedValue->{Value};
|
||||
|
||||
push @RenderedValues, $RenderedValue->{Value};
|
||||
}
|
||||
|
||||
$Value = join ', ', @RenderedValues;
|
||||
$Label = $DynamicFieldConfig->{Label};
|
||||
}
|
||||
|
||||
# do not show empty entries
|
||||
next ENTRY if !length($Value);
|
||||
|
||||
$LayoutObject->Block( Name => "ContentSmallCustomerUserInformationRow" );
|
||||
|
||||
if ( $Key eq 'UserCustomerID' ) {
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerUserInformationRowLink",
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
URL =>
|
||||
'[% Env("Baselink") %]Action=AdminCustomerCompany;Subaction=Change;CustomerID=[% Data.UserCustomerID | uri %];Nav=Agent',
|
||||
Target => '',
|
||||
},
|
||||
);
|
||||
|
||||
next ENTRY;
|
||||
}
|
||||
|
||||
if ( $Key eq 'UserLogin' ) {
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerUserInformationRowLink",
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
URL =>
|
||||
'[% Env("Baselink") %]Action=AdminCustomerUser;Subaction=Change;ID=[% Data.UserLogin | uri %];Nav=Agent',
|
||||
Target => '',
|
||||
},
|
||||
);
|
||||
|
||||
next ENTRY;
|
||||
}
|
||||
|
||||
# check if a link must be placed
|
||||
if ( $Entry->[6] ) {
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerUserInformationRowLink",
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
URL => $Entry->[6],
|
||||
Target => '_blank',
|
||||
},
|
||||
);
|
||||
|
||||
next ENTRY;
|
||||
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => "ContentSmallCustomerUserInformationRowText",
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
Label => $Label,
|
||||
Value => $Value,
|
||||
},
|
||||
);
|
||||
|
||||
if ( $Key eq 'UserLogin' && defined $CustomerUserIsValid && !$CustomerUserIsValid ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallCustomerUserInvalid',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCustomerUserInformation',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%CustomerUser,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
452
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerUserList.pm
Normal file
452
Perl OTRS/Kernel/Output/HTML/Dashboard/CustomerUserList.pm
Normal file
@@ -0,0 +1,452 @@
|
||||
# --
|
||||
# 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::CustomerUserList;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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');
|
||||
|
||||
# get current filter
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'UserDashboardCustomerUserListFilter' . $Self->{Name};
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKey} }
|
||||
|| $Self->{Config}->{Limit};
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown customer users'),
|
||||
Name => $Self->{PrefKey},
|
||||
Block => 'Option',
|
||||
|
||||
# Block => 'Input',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheTTL => undef,
|
||||
CacheKey => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return if !$Param{CustomerID};
|
||||
|
||||
# get customer user object
|
||||
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
||||
|
||||
my $CustomerIDs = { $CustomerUserObject->CustomerSearch( CustomerIDRaw => $Param{CustomerID} ) };
|
||||
|
||||
# add page nav bar
|
||||
my $Total = scalar keys %{$CustomerIDs};
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $LinkPage = 'Subaction=Element;Name='
|
||||
. $Self->{Name} . ';'
|
||||
. 'CustomerID='
|
||||
. $LayoutObject->LinkEncode( $Param{CustomerID} ) . ';';
|
||||
|
||||
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 => 'ContentLargeCustomerUserListNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# check the permission for the SwitchToCustomer feature
|
||||
if ( $ConfigObject->Get('SwitchToCustomer') ) {
|
||||
|
||||
# get group object
|
||||
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
|
||||
|
||||
# get the group id which is allowed to use the switch to customer feature
|
||||
my $SwitchToCustomerGroupID = $GroupObject->GroupLookup(
|
||||
Group => $ConfigObject->Get('SwitchToCustomer::PermissionGroup'),
|
||||
);
|
||||
|
||||
# get user groups, where the user has the rw privilege
|
||||
my %Groups = $GroupObject->PermissionUserGet(
|
||||
UserID => $Self->{UserID},
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
# if the user is a member in this group he can access the feature
|
||||
if ( $Groups{$SwitchToCustomerGroupID} ) {
|
||||
|
||||
$Self->{SwitchToCustomerPermission} = 1;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewResultSwitchToCustomer',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Show add new customer button if:
|
||||
# - The agent has permission to use the module
|
||||
# - There are writable customer backends
|
||||
my $AddAccess;
|
||||
|
||||
TYPE:
|
||||
for my $Permission (qw(ro rw)) {
|
||||
$AddAccess = $LayoutObject->Permission(
|
||||
Action => 'AdminCustomerUser',
|
||||
Type => $Permission,
|
||||
);
|
||||
last TYPE if $AddAccess;
|
||||
}
|
||||
|
||||
# Get writable data sources.
|
||||
my %CustomerSource = $CustomerUserObject->CustomerSourceList(
|
||||
ReadOnly => 0,
|
||||
);
|
||||
|
||||
if ( $AddAccess && scalar keys %CustomerSource ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserAdd',
|
||||
Data => {
|
||||
CustomerID => $Self->{CustomerID},
|
||||
},
|
||||
);
|
||||
|
||||
$Self->{EditCustomerPermission} = 1;
|
||||
}
|
||||
|
||||
# get the permission for the phone ticket creation
|
||||
my $NewAgentTicketPhonePermission = $LayoutObject->Permission(
|
||||
Action => 'AgentTicketPhone',
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
# check the permission for the phone ticket creation
|
||||
if ($NewAgentTicketPhonePermission) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewResultNewAgentTicketPhone',
|
||||
);
|
||||
}
|
||||
|
||||
# get the permission for the email ticket creation
|
||||
my $NewAgentTicketEmailPermission = $LayoutObject->Permission(
|
||||
Action => 'AgentTicketEmail',
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
# check the permission for the email ticket creation
|
||||
if ($NewAgentTicketEmailPermission) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewResultNewAgentTicketEmail',
|
||||
);
|
||||
}
|
||||
|
||||
my @CustomerKeys = sort { lc( $CustomerIDs->{$a} ) cmp lc( $CustomerIDs->{$b} ) } keys %{$CustomerIDs};
|
||||
@CustomerKeys = splice @CustomerKeys, $Self->{StartHit} - 1, $Self->{PageShown};
|
||||
|
||||
for my $CustomerKey (@CustomerKeys) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListRow',
|
||||
Data => {
|
||||
%Param,
|
||||
EditCustomerPermission => $Self->{EditCustomerPermission},
|
||||
CustomerKey => $CustomerKey,
|
||||
CustomerListEntry => $CustomerIDs->{$CustomerKey},
|
||||
},
|
||||
);
|
||||
|
||||
if ( $ConfigObject->Get('ChatEngine::Active') ) {
|
||||
|
||||
# Check if agent has permission to start chats with the customer users.
|
||||
my $EnableChat = 1;
|
||||
my $ChatStartingAgentsGroup
|
||||
= $ConfigObject->Get('ChatEngine::PermissionGroup::ChatStartingAgents') || 'users';
|
||||
my $ChatStartingAgentsGroupPermission = $Kernel::OM->Get('Kernel::System::Group')->PermissionCheck(
|
||||
UserID => $Self->{UserID},
|
||||
GroupName => $ChatStartingAgentsGroup,
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
if ( !$ChatStartingAgentsGroupPermission ) {
|
||||
$EnableChat = 0;
|
||||
}
|
||||
if (
|
||||
$EnableChat
|
||||
&& !$ConfigObject->Get('ChatEngine::ChatDirection::AgentToCustomer')
|
||||
)
|
||||
{
|
||||
$EnableChat = 0;
|
||||
}
|
||||
|
||||
if ($EnableChat) {
|
||||
my $VideoChatEnabled = 0;
|
||||
my $VideoChatAgentsGroup
|
||||
= $ConfigObject->Get('ChatEngine::PermissionGroup::VideoChatAgents') || 'users';
|
||||
my $VideoChatAgentsGroupPermission = $Kernel::OM->Get('Kernel::System::Group')->PermissionCheck(
|
||||
UserID => $Self->{UserID},
|
||||
GroupName => $VideoChatAgentsGroup,
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
# Enable the video chat feature if system is entitled and agent is a member of configured group.
|
||||
if ($VideoChatAgentsGroupPermission) {
|
||||
if ( $Kernel::OM->Get('Kernel::System::Main')->Require( 'Kernel::System::VideoChat', Silent => 1 ) )
|
||||
{
|
||||
$VideoChatEnabled = $Kernel::OM->Get('Kernel::System::VideoChat')->IsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
my $CustomerEnableChat = 0;
|
||||
my $ChatAccess = 0;
|
||||
my $VideoChatAvailable = 0;
|
||||
my $VideoChatSupport = 0;
|
||||
|
||||
# Default status is offline.
|
||||
my $UserState = Translatable('Offline');
|
||||
my $UserStateDescription = $LayoutObject->{LanguageObject}->Translate('User is currently offline.');
|
||||
|
||||
my $CustomerChatAvailability = $Kernel::OM->Get('Kernel::System::Chat')->CustomerAvailabilityGet(
|
||||
UserID => $CustomerKey,
|
||||
);
|
||||
|
||||
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
||||
|
||||
my %CustomerUser = $CustomerUserObject->CustomerUserDataGet(
|
||||
User => $CustomerKey,
|
||||
);
|
||||
$CustomerUser{UserFullname} = $CustomerUserObject->CustomerName(
|
||||
UserLogin => $CustomerKey,
|
||||
);
|
||||
$VideoChatSupport = 1 if $CustomerUser{VideoChatHasWebRTC};
|
||||
|
||||
if ( $CustomerChatAvailability == 3 ) {
|
||||
$UserState = Translatable('Active');
|
||||
$CustomerEnableChat = 1;
|
||||
$UserStateDescription = $LayoutObject->{LanguageObject}->Translate('User is currently active.');
|
||||
$VideoChatAvailable = 1;
|
||||
}
|
||||
elsif ( $CustomerChatAvailability == 2 ) {
|
||||
$UserState = Translatable('Away');
|
||||
$CustomerEnableChat = 1;
|
||||
$UserStateDescription
|
||||
= $LayoutObject->{LanguageObject}->Translate('User was inactive for a while.');
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListRowUserStatus',
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
UserState => $UserState,
|
||||
UserStateDescription => $UserStateDescription,
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
$CustomerEnableChat
|
||||
&& $ConfigObject->Get('Ticket::Agent::StartChatWOTicket')
|
||||
)
|
||||
{
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListRowChatIcons',
|
||||
Data => {
|
||||
%CustomerUser,
|
||||
VideoChatEnabled => $VideoChatEnabled,
|
||||
VideoChatAvailable => $VideoChatAvailable,
|
||||
VideoChatSupport => $VideoChatSupport,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# get ticket object
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
|
||||
my $TicketCountOpen = $TicketObject->TicketSearch(
|
||||
StateType => 'Open',
|
||||
CustomerUserLoginRaw => $CustomerKey,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
my $CustomerKeySQL = $Kernel::OM->Get('Kernel::System::DB')->QueryStringEscape( QueryString => $CustomerKey );
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListRowCustomerUserTicketsOpen',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $TicketCountOpen,
|
||||
CustomerKey => $CustomerKey,
|
||||
CustomerKeySQL => $CustomerKeySQL,
|
||||
},
|
||||
);
|
||||
|
||||
my $TicketCountClosed = $TicketObject->TicketSearch(
|
||||
StateType => 'Closed',
|
||||
CustomerUserLoginRaw => $CustomerKey,
|
||||
Result => 'COUNT',
|
||||
Permission => $Self->{Config}->{Permission},
|
||||
UserID => $Self->{UserID},
|
||||
CacheTTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
) || 0;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListRowCustomerUserTicketsClosed',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $TicketCountClosed,
|
||||
CustomerKey => $CustomerKey,
|
||||
CustomerKeySQL => $CustomerKeySQL,
|
||||
},
|
||||
);
|
||||
|
||||
# check the permission for the phone ticket creation
|
||||
if ($NewAgentTicketPhonePermission) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListNewAgentTicketPhone',
|
||||
Data => {
|
||||
%Param,
|
||||
CustomerKey => $CustomerKey,
|
||||
CustomerListEntry => $CustomerIDs->{$CustomerKey},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# check the permission for the email ticket creation
|
||||
if ($NewAgentTicketEmailPermission) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListNewAgentTicketEmail',
|
||||
Data => {
|
||||
%Param,
|
||||
CustomerKey => $CustomerKey,
|
||||
CustomerListEntry => $CustomerIDs->{$CustomerKey},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if ( $ConfigObject->Get('SwitchToCustomer') && $Self->{SwitchToCustomerPermission} )
|
||||
{
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewResultRowSwitchToCustomer',
|
||||
Data => {
|
||||
%Param,
|
||||
Count => $TicketCountClosed,
|
||||
CustomerKey => $CustomerKey,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# show "none" if there are no customers
|
||||
if ( !%{$CustomerIDs} ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeCustomerUserListNone',
|
||||
Data => {},
|
||||
);
|
||||
}
|
||||
|
||||
# check for refresh time
|
||||
my $Refresh = '';
|
||||
if ( $Self->{UserRefreshTime} ) {
|
||||
$Refresh = 60 * $Self->{UserRefreshTime};
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'CustomerUserListRefresh',
|
||||
Value => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh,
|
||||
CustomerID => $Param{CustomerID},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardCustomerUserList',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
EditCustomerPermission => $Self->{EditCustomerPermission},
|
||||
Name => $Self->{Name},
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
375
Perl OTRS/Kernel/Output/HTML/Dashboard/EventsTicketCalendar.pm
Normal file
375
Perl OTRS/Kernel/Output/HTML/Dashboard/EventsTicketCalendar.pm
Normal file
@@ -0,0 +1,375 @@
|
||||
# --
|
||||
# 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::EventsTicketCalendar;
|
||||
|
||||
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 dynamic fields list
|
||||
my $DynamicFieldsList = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
|
||||
Valid => 0,
|
||||
ObjectType => ['Ticket'],
|
||||
);
|
||||
|
||||
if ( !IsArrayRefWithData($DynamicFieldsList) ) {
|
||||
$DynamicFieldsList = [];
|
||||
}
|
||||
|
||||
# create a dynamic field lookup table (by name)
|
||||
DYNAMICFIELD:
|
||||
for my $DynamicField ( @{$DynamicFieldsList} ) {
|
||||
next DYNAMICFIELD if !$DynamicField;
|
||||
next DYNAMICFIELD if !IsHashRefWithData($DynamicField);
|
||||
next DYNAMICFIELD if !$DynamicField->{Name};
|
||||
$Self->{DynamicFieldLookup}->{ $DynamicField->{Name} } = $DynamicField;
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
my $Queues = $ConfigObject->{DashboardEventsTicketCalendar}->{Queues};
|
||||
|
||||
# get start and end time from config
|
||||
my $StartTimeDynamicField = $ConfigObject->Get('DashboardEventsTicketCalendar::DynamicFieldStartTime')
|
||||
|| 'TicketCalendarStartTime';
|
||||
my $EndTimeDynamicField =
|
||||
$ConfigObject->Get('DashboardEventsTicketCalendar::DynamicFieldEndTime')
|
||||
|| 'TicketCalendarEndTime';
|
||||
|
||||
$Param{CalendarWidth} = $ConfigObject->{DashboardEventsTicketCalendar}->{CalendarWidth};
|
||||
|
||||
my %QueuesAll = $Kernel::OM->Get('Kernel::System::Queue')->GetAllQueues(
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
|
||||
my $EventTicketFields = $ConfigObject->Get('DashboardEventsTicketCalendar::TicketFieldsForEvents');
|
||||
my $EventDynamicFields = $ConfigObject->Get('DashboardEventsTicketCalendar::DynamicFieldsForEvents');
|
||||
|
||||
my %DynamicFieldTimeSearch = (
|
||||
'DynamicField_' . $StartTimeDynamicField => {
|
||||
GreaterThanEquals => '1970-01-01 00:00:00',
|
||||
},
|
||||
'DynamicField_' . $EndTimeDynamicField => {
|
||||
GreaterThanEquals => '1970-01-01 00:00:00',
|
||||
},
|
||||
);
|
||||
|
||||
my @ViewableStateIDs = $Kernel::OM->Get('Kernel::System::State')->StateGetStatesByType(
|
||||
Type => 'Viewable',
|
||||
Result => 'ID',
|
||||
);
|
||||
|
||||
my %QueuesConfigured;
|
||||
for my $Queue ( @{$Queues} ) {
|
||||
for my $QueueID ( sort keys %QueuesAll ) {
|
||||
if ( $QueuesAll{$QueueID} eq $Queue ) {
|
||||
$QueuesConfigured{$QueueID} = $QueuesAll{$QueueID};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# get ticket object
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
|
||||
my %Tickets;
|
||||
if (%QueuesConfigured) {
|
||||
%Tickets = $TicketObject->TicketSearch(
|
||||
SortBy => $ConfigObject->{'SortBy::Default'} || 'Age',
|
||||
QueueIDs => [ sort keys %QueuesConfigured ],
|
||||
UserID => $Self->{UserID},
|
||||
StateIDs => \@ViewableStateIDs,
|
||||
Result => 'HASH',
|
||||
%DynamicFieldTimeSearch,
|
||||
);
|
||||
}
|
||||
|
||||
my @EventsDisplayed;
|
||||
|
||||
my $Counter = 1;
|
||||
my $Limit = scalar keys %Tickets;
|
||||
|
||||
# get needed objects
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
||||
my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
|
||||
my $CustomerCompanyObject = $Kernel::OM->Get('Kernel::System::CustomerCompany');
|
||||
|
||||
my $Content;
|
||||
|
||||
if (%Tickets) {
|
||||
TICKET:
|
||||
for my $TicketID ( sort keys %Tickets ) {
|
||||
|
||||
my %TicketDetail = $TicketObject->TicketGet(
|
||||
TicketID => $TicketID,
|
||||
DynamicFields => 1,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
|
||||
if (
|
||||
%TicketDetail &&
|
||||
$TicketDetail{ 'DynamicField_' . $StartTimeDynamicField } &&
|
||||
$TicketDetail{ 'DynamicField_' . $EndTimeDynamicField }
|
||||
)
|
||||
{
|
||||
|
||||
# end time should be greater than start time
|
||||
my $StartTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $TicketDetail{ 'DynamicField_' . $StartTimeDynamicField }
|
||||
},
|
||||
);
|
||||
|
||||
my $EndTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $TicketDetail{ 'DynamicField_' . $EndTimeDynamicField }
|
||||
},
|
||||
);
|
||||
|
||||
# check if start time is after end time
|
||||
if ( $StartTimeObject > $EndTimeObject ) {
|
||||
|
||||
# turn start and end time around for the calendar view
|
||||
my $NewStartTimeObject = $EndTimeObject;
|
||||
my $NewEndTimeObject = $StartTimeObject;
|
||||
$StartTimeObject = $NewStartTimeObject;
|
||||
$EndTimeObject = $NewEndTimeObject;
|
||||
|
||||
# we also need to turn the time in the tooltip around, otherwiese the time bar display would be wrong
|
||||
$TicketDetail{ 'DynamicField_' . $StartTimeDynamicField } = $StartTimeObject->ToString();
|
||||
$TicketDetail{ 'DynamicField_' . $EndTimeDynamicField } = $EndTimeObject->ToString();
|
||||
|
||||
# show a notification bar to indicate that the start and end time are set in a wrong way
|
||||
$Content .= $LayoutObject->Notify(
|
||||
Priority => 'Warning',
|
||||
Info => Translatable('The start time of a ticket has been set after the end time!'),
|
||||
Link => "index.pl?Action=AgentTicketZoom;TicketID=$TicketID",
|
||||
);
|
||||
}
|
||||
|
||||
my %Data;
|
||||
|
||||
# set start/end time individual units
|
||||
# ex: SYear; EMinute
|
||||
my $SetDataDateTimeUnits = sub {
|
||||
my ( $Type, $Details ) = @_;
|
||||
|
||||
my $Prefix = substr( $Type, 0, 1 );
|
||||
for my $Unit (qw(Year Month Day Hour Minute Second)) {
|
||||
my $Value = $Details->{$Unit};
|
||||
if ( $Unit eq 'Month' ) {
|
||||
$Value -= 1;
|
||||
}
|
||||
|
||||
$Data{"${Prefix}${Unit}"} = $Value;
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
$SetDataDateTimeUnits->( Start => $StartTimeObject->Get() );
|
||||
$SetDataDateTimeUnits->( End => $EndTimeObject->Get() );
|
||||
#
|
||||
|
||||
$Data{ID} = $TicketID;
|
||||
$Data{Title} = $TicketDetail{Title};
|
||||
$Data{Color} = $Self->{TicketColors}->{$TicketID};
|
||||
$Data{AllDay} = 'false';
|
||||
$Data{Url} = "index.pl?Action=AgentTicketZoom;TicketID=$TicketID";
|
||||
$Data{QueueName} = $QueuesAll{ $TicketDetail{QueueID} };
|
||||
$Data{QueueName} =~ s/.*[\:]([^\:]+)$/$1/;
|
||||
$Data{Description} = "";
|
||||
|
||||
# add 1 second to end date as workaround
|
||||
# for a bug on full calendar when start and end
|
||||
# dates are exactly the same (ESecond is 00 normally)
|
||||
$Data{ESecond}++;
|
||||
|
||||
push @{ $Self->{EventsTicketCalendar} }, \%Data;
|
||||
|
||||
# add ticket info container
|
||||
$LayoutObject->Block(
|
||||
Name => 'EventInfo',
|
||||
Data => \%Data,
|
||||
);
|
||||
|
||||
# define container for dynamic fields
|
||||
my @EventTicketDynamicFields;
|
||||
|
||||
# add ticket field for the event
|
||||
if ( IsHashRefWithData($EventTicketFields) ) {
|
||||
|
||||
# include dynamic fields container
|
||||
$LayoutObject->Block(
|
||||
Name => 'EventTicketFieldContainer',
|
||||
Data => \%Data,
|
||||
);
|
||||
|
||||
# include dynamic fields
|
||||
TICKETFIELD:
|
||||
for my $Key ( sort keys %{$EventTicketFields} ) {
|
||||
|
||||
next TICKETFIELD if !$Key;
|
||||
next TICKETFIELD if !$EventTicketFields->{$Key};
|
||||
|
||||
# skip dynamic fields, will them added later
|
||||
if ( $Key =~ m{\A DynamicField_(.*) \z}msx ) {
|
||||
my $DynamicFieldName = $Key;
|
||||
$DynamicFieldName =~ s{\A DynamicField_ }{}msxg;
|
||||
push @EventTicketDynamicFields, $DynamicFieldName;
|
||||
next TICKETFIELD;
|
||||
}
|
||||
|
||||
if ( $Key eq 'CustomerName' && $TicketDetail{CustomerUserID} ) {
|
||||
$TicketDetail{$Key} = $CustomerUserObject->CustomerName(
|
||||
UserLogin => $TicketDetail{CustomerUserID},
|
||||
);
|
||||
}
|
||||
|
||||
if ( $Key eq 'CustomerCompanyName' && $TicketDetail{CustomerID} ) {
|
||||
my %CustomerCompany = $CustomerCompanyObject->CustomerCompanyGet(
|
||||
CustomerID => $TicketDetail{CustomerID},
|
||||
);
|
||||
$TicketDetail{$Key} = $CustomerCompany{$Key};
|
||||
}
|
||||
|
||||
if ( ( $Key eq 'Owner' || $Key eq 'Responsible' ) && $TicketDetail{$Key} ) {
|
||||
my %UserData = $UserObject->GetUserData(
|
||||
User => $TicketDetail{$Key},
|
||||
);
|
||||
$TicketDetail{$Key} = $UserData{UserFullname};
|
||||
}
|
||||
|
||||
# translate state and priority name
|
||||
if ( ( $Key eq 'State' || $Key eq 'Priority' ) && $TicketDetail{$Key} ) {
|
||||
$TicketDetail{$Key} = $LayoutObject->{LanguageObject}->Translate( $TicketDetail{$Key} );
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'CalendarEventInfoTicketFieldElement',
|
||||
Data => {
|
||||
InfoLabel => $EventTicketFields->{$Key},
|
||||
InfoValue => $TicketDetail{$Key},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# merge event ticket dynamic fields
|
||||
my $DynamicFieldsForEvent = [ @{$EventDynamicFields}, @EventTicketDynamicFields ];
|
||||
|
||||
# add dynamic field for the event
|
||||
if ( IsArrayRefWithData($DynamicFieldsForEvent) ) {
|
||||
|
||||
# include dynamic fields container
|
||||
$LayoutObject->Block(
|
||||
Name => 'EventDynamicFieldContainer',
|
||||
Data => \%Data,
|
||||
);
|
||||
|
||||
# include dynamic fields
|
||||
DYNAMICFIELD:
|
||||
for my $Item ( @{$DynamicFieldsForEvent} ) {
|
||||
|
||||
next DYNAMICFIELD if !$Item;
|
||||
next DYNAMICFIELD if !$Self->{DynamicFieldLookup}->{$Item}->{Label};
|
||||
|
||||
# check if we need to format the date
|
||||
my $DisplayValue
|
||||
= $Kernel::OM->Get('Kernel::System::DynamicField::Backend')->DisplayValueRender(
|
||||
DynamicFieldConfig => $Self->{DynamicFieldLookup}->{$Item},
|
||||
Value => $TicketDetail{ 'DynamicField_' . $Item },
|
||||
LayoutObject => $LayoutObject,
|
||||
);
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'CalendarEventInfoDynamicFieldElement',
|
||||
Data => {
|
||||
InfoLabel => $Self->{DynamicFieldLookup}->{$Item}->{Label},
|
||||
InfoValue => $DisplayValue->{Value},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$Counter++;
|
||||
}
|
||||
else {
|
||||
$Limit-- if ( $Limit > 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'CalendarDiv',
|
||||
Data => {
|
||||
CalendarWidth => $ConfigObject->{DashboardEventsTicketCalendar}->{CalendarWidth}
|
||||
|| 95,
|
||||
}
|
||||
);
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'EventsTicketCalendar',
|
||||
Value => $Self->{EventsTicketCalendar},
|
||||
);
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'FirstDay',
|
||||
Value => $ConfigObject->Get('CalendarWeekDayStart') || 0,
|
||||
);
|
||||
|
||||
$Content .= $LayoutObject->Output(
|
||||
TemplateFile => 'DashboardEventsTicketCalendar',
|
||||
Data => {},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
85
Perl OTRS/Kernel/Output/HTML/Dashboard/FAQ.pm
Normal file
85
Perl OTRS/Kernel/Output/HTML/Dashboard/FAQ.pm
Normal file
@@ -0,0 +1,85 @@
|
||||
# --
|
||||
# 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::FAQ;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our @ObjectDependencies = (
|
||||
'Kernel::Config',
|
||||
'Kernel::Output::HTML::Layout',
|
||||
'Kernel::System::FAQ',
|
||||
);
|
||||
|
||||
sub new {
|
||||
my ( $Type, %Param ) = @_;
|
||||
|
||||
# allocate new hash for object
|
||||
my $Self = {%Param};
|
||||
bless( $Self, $Type );
|
||||
|
||||
for my $Needed (qw(Config Name UserID))
|
||||
{
|
||||
die "Got no $Needed!" if ( !$Self->{$Needed} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $FAQObject = $Kernel::OM->Get('Kernel::System::FAQ');
|
||||
|
||||
# set default interface settings
|
||||
my $Interface = $FAQObject->StateTypeGet(
|
||||
Name => 'internal',
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
my $InterfaceStates = $FAQObject->StateTypeList(
|
||||
Types => $Kernel::OM->Get('Kernel::Config')->Get('FAQ::Agent::StateTypes'),
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
$LayoutObject->FAQShowLatestNewsBox(
|
||||
FAQObject => $FAQObject,
|
||||
Type => $Self->{Config}->{Type},
|
||||
Mode => 'Agent',
|
||||
CategoryID => 0,
|
||||
Interface => $Interface,
|
||||
InterfaceStates => $InterfaceStates,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardFAQOverview',
|
||||
Data => {
|
||||
CategoryID => 0,
|
||||
SidebarClass => 'Medium',
|
||||
},
|
||||
);
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
66
Perl OTRS/Kernel/Output/HTML/Dashboard/IFrame.pm
Normal file
66
Perl OTRS/Kernel/Output/HTML/Dashboard/IFrame.pm
Normal file
@@ -0,0 +1,66 @@
|
||||
# --
|
||||
# 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::IFrame;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# prevent 'Used once' warning
|
||||
use Kernel::System::ObjectManager;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# quote Title attribute, it will be used as name="" parameter of the iframe
|
||||
my $Title = $Self->{Config}->{Title} || '';
|
||||
$Title =~ s/\s/_/smx;
|
||||
|
||||
my $Content = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Output(
|
||||
TemplateFile => 'AgentDashboardIFrame',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Title => $Title,
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
894
Perl OTRS/Kernel/Output/HTML/Dashboard/ITSMConfigItemGeneric.pm
Normal file
894
Perl OTRS/Kernel/Output/HTML/Dashboard/ITSMConfigItemGeneric.pm
Normal file
@@ -0,0 +1,894 @@
|
||||
# --
|
||||
# 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::ITSMConfigItemGeneric;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::Language qw(Translatable);
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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');
|
||||
|
||||
# Get current filter.
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'DashboardITSMConfigItemGeneric' . $Self->{Name};
|
||||
if ( $Self->{Name} eq $Name ) {
|
||||
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
|
||||
}
|
||||
|
||||
# Get config object.
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# Remember filter.
|
||||
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') ) {
|
||||
$Kernel::OM->Get('Kernel::System::User')->SetPreferences(
|
||||
UserID => $Self->{UserID},
|
||||
Key => $PreferencesKey,
|
||||
Value => $Self->{Filter},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Set default filter if not set yet.
|
||||
if ( !$Self->{Filter} ) {
|
||||
$Self->{Filter} = $Self->{$PreferencesKey} || $Self->{Config}->{Filter} || 'All';
|
||||
}
|
||||
|
||||
# Setup the prefrences keys.
|
||||
$Self->{PrefKeyShown} = 'DashboardITSMConfigItemGeneric' . $Self->{Name} . '-Shown';
|
||||
$Self->{PrefKeyRefresh} = 'DashboardITSMConfigItemGeneric' . $Self->{Name} . '-Refresh';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyShown} }
|
||||
|| $Self->{Config}->{Limit} || 10;
|
||||
|
||||
$Self->{PageRefresh} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKeyRefresh} }
|
||||
|| 1;
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown config items'),
|
||||
Name => $Self->{PrefKeyShown},
|
||||
Block => 'Option',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
30 => '30',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# Remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# Get param object.
|
||||
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
|
||||
|
||||
# Get layout object.
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
# Get sorting parameters.
|
||||
$Param{SortBy} = $ParamObject->GetParam( Param => 'SortBy' ) || 'Number';
|
||||
|
||||
# Get ordering parameters.
|
||||
$Param{OrderBy} = $ParamObject->GetParam( Param => 'OrderBy' ) || 'Up';
|
||||
|
||||
# Set Sort and Order by as Arrays.
|
||||
my @SortByArray = ( $Param{SortBy} );
|
||||
my @OrderByArray = ( $Param{OrderBy} );
|
||||
|
||||
# Get general catalog object.
|
||||
my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog');
|
||||
|
||||
# Get class list.
|
||||
my $ClassList = $GeneralCatalogObject->ItemList(
|
||||
Class => 'ITSM::ConfigItem::Class',
|
||||
);
|
||||
|
||||
# To store the color for the deployment states.
|
||||
my %DeplSignals;
|
||||
|
||||
# Get list of deployment states.
|
||||
my $DeploymentStatesList = $GeneralCatalogObject->ItemList(
|
||||
Class => 'ITSM::ConfigItem::DeploymentState',
|
||||
);
|
||||
|
||||
# Set deployment style colors.
|
||||
my $StyleClasses = '';
|
||||
|
||||
# To store depl state ids.
|
||||
my $DeplStateIDs;
|
||||
|
||||
ITEMID:
|
||||
for my $ItemID ( sort keys %{$DeploymentStatesList} ) {
|
||||
|
||||
# Get deployment state preferences.
|
||||
my %Preferences = $GeneralCatalogObject->GeneralCatalogPreferencesGet(
|
||||
ItemID => $ItemID,
|
||||
);
|
||||
|
||||
# Check if a color is defined in preferences.
|
||||
next ITEMID if !$Preferences{Color};
|
||||
|
||||
# Get deployment state.
|
||||
my $DeplState = $DeploymentStatesList->{$ItemID};
|
||||
|
||||
# Remove any non ascii word characters.
|
||||
$DeplState =~ s{ [^a-zA-Z0-9] }{_}msxg;
|
||||
|
||||
# Store the original deployment state as key
|
||||
# and the ss safe coverted deployment state as value.
|
||||
$DeplSignals{ $DeploymentStatesList->{$ItemID} } = $DeplState;
|
||||
|
||||
# Convert to lower case.
|
||||
my $DeplStateColor = lc $Preferences{Color};
|
||||
|
||||
# Add to style classes string.
|
||||
$StyleClasses .= "
|
||||
.Flag span.$DeplState {
|
||||
background-color: #$DeplStateColor;
|
||||
}
|
||||
";
|
||||
|
||||
# Set depl state ids.
|
||||
push @{$DeplStateIDs}, $ItemID;
|
||||
}
|
||||
|
||||
# Wrap into style tags.
|
||||
if ($StyleClasses) {
|
||||
$StyleClasses = "<style>$StyleClasses</style>";
|
||||
}
|
||||
|
||||
# To store the default class.
|
||||
my $ClassIDAuto = '';
|
||||
|
||||
# To store the NavBar filters.
|
||||
my %Filters;
|
||||
|
||||
# Define position of the filter in the frontend.
|
||||
my $PrioCounter = 1000;
|
||||
|
||||
# To store the total number of config items in all classes that the user has access.
|
||||
my $TotalCount;
|
||||
|
||||
# To store all the clases that the user has access, used in search for filter 'All'.
|
||||
my $AccessClassList;
|
||||
|
||||
# Get config item object.
|
||||
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
|
||||
|
||||
# Define incident signals, needed for services.
|
||||
my %InciSignals = (
|
||||
Translatable('operational') => 'greenled',
|
||||
Translatable('warning') => 'yellowled',
|
||||
Translatable('incident') => 'redled',
|
||||
);
|
||||
|
||||
CLASSID:
|
||||
for my $ClassID ( sort { ${$ClassList}{$a} cmp ${$ClassList}{$b} } keys %{$ClassList} ) {
|
||||
|
||||
# Check permission.
|
||||
my $HasAccess = $ConfigItemObject->Permission(
|
||||
Scope => 'Class',
|
||||
ClassID => $ClassID,
|
||||
UserID => $Self->{UserID},
|
||||
Type => $Self->{Config}->{Permission},
|
||||
);
|
||||
|
||||
next CLASSID if !$HasAccess;
|
||||
|
||||
# Insert this class to be passed as search parameter for filter 'All'.
|
||||
push @{$AccessClassList}, $ClassID;
|
||||
|
||||
# Increase the PrioCounter.
|
||||
$PrioCounter++;
|
||||
|
||||
# Add filter with params for the search method.
|
||||
$Filters{$ClassID} = {
|
||||
Name => $ClassList->{$ClassID},
|
||||
Prio => $PrioCounter,
|
||||
Search => {
|
||||
ClassIDs => [$ClassID],
|
||||
DeplStateIDs => $DeplStateIDs,
|
||||
OrderBy => \@SortByArray,
|
||||
OrderByDirection => \@OrderByArray,
|
||||
Limit => $Self->{SearchLimit},
|
||||
},
|
||||
};
|
||||
|
||||
# Remember the first class id to show this in the overview
|
||||
# if no class id was given.
|
||||
if ( !$ClassIDAuto ) {
|
||||
$ClassIDAuto = $ClassID;
|
||||
}
|
||||
}
|
||||
|
||||
# If only one filter exists.
|
||||
if ( scalar keys %Filters == 1 ) {
|
||||
|
||||
# Get the name of the only filter.
|
||||
my ($FilterKey) = keys %Filters;
|
||||
|
||||
# Activate this filter.
|
||||
$Self->{Filter} = $Filters{$FilterKey}->{Name};
|
||||
}
|
||||
else {
|
||||
|
||||
# Add default filter, which shows all items.
|
||||
$Filters{All} = {
|
||||
Name => 'All',
|
||||
Prio => 1000,
|
||||
Search => {
|
||||
ClassIDs => $AccessClassList,
|
||||
DeplStateIDs => $DeplStateIDs,
|
||||
OrderBy => \@SortByArray,
|
||||
OrderByDirection => \@OrderByArray,
|
||||
Limit => $Self->{SearchLimit},
|
||||
},
|
||||
};
|
||||
|
||||
# If no filter was selected activate the filter for the default class.
|
||||
if ( !$Self->{Filter} ) {
|
||||
$Self->{Filter} = $ClassIDAuto;
|
||||
}
|
||||
}
|
||||
|
||||
# Get given filters.
|
||||
my @NavBarFilters;
|
||||
for my $Prio ( sort keys %Filters ) {
|
||||
push @NavBarFilters, $Filters{$Prio};
|
||||
}
|
||||
|
||||
# Sort according to prio.
|
||||
@NavBarFilters = sort { $a->{Prio} <=> $b->{Prio} } @NavBarFilters;
|
||||
|
||||
# Find out which columns should be shown.
|
||||
my @ShowColumns;
|
||||
my @XMLShowColumns;
|
||||
if ( $Self->{Config}->{DefaultColumns} ) {
|
||||
|
||||
# Get all possible columns from config.
|
||||
my %PossibleColumn = %{ $Self->{Config}->{DefaultColumns} };
|
||||
|
||||
# Show column "Class" if filter 'All' is selected.
|
||||
if ( $Self->{Filter} eq 'All' ) {
|
||||
$PossibleColumn{Class} = '1';
|
||||
}
|
||||
|
||||
# Get the column names that should be shown.
|
||||
COLUMNNAME:
|
||||
for my $Name ( sort keys %PossibleColumn ) {
|
||||
next COLUMNNAME if !$PossibleColumn{$Name};
|
||||
push @ShowColumns, $Name;
|
||||
}
|
||||
}
|
||||
|
||||
# Get frontend config.
|
||||
my ($Module) = $Self->{Action} =~ m{^Agent(.*)}xms;
|
||||
my $ShowColumnsByClassConfig
|
||||
= $Kernel::OM->Get('Kernel::Config')->Get( 'Frontend::Agent::' . $Module . '::ITSMConfigItem' );
|
||||
|
||||
my $ShowColumnsByClass = $ShowColumnsByClassConfig->{ShowColumnsByClass} // '';
|
||||
|
||||
# Get the configured columns and reorganize them by class name.
|
||||
if (
|
||||
IsArrayRefWithData($ShowColumnsByClass)
|
||||
&& $Self->{Filter}
|
||||
&& $Self->{Filter} ne 'All'
|
||||
)
|
||||
{
|
||||
|
||||
my %ColumnByClass;
|
||||
NAME:
|
||||
for my $Name ( @{$ShowColumnsByClass} ) {
|
||||
my ( $Class, $Column ) = split /::/, $Name, 2;
|
||||
|
||||
next NAME if !$Column;
|
||||
|
||||
push @{ $ColumnByClass{$Class} }, $Column;
|
||||
}
|
||||
|
||||
# Check if there is a specific column config for the selected class.
|
||||
if ( $ColumnByClass{ $Self->{Filter} } ) {
|
||||
@ShowColumns = @{ $ColumnByClass{ $Self->{Filter} } };
|
||||
}
|
||||
}
|
||||
|
||||
if (@ShowColumns) {
|
||||
|
||||
for my $Column (@ShowColumns) {
|
||||
|
||||
# Create needed veriables.
|
||||
my $CSS = 'OverviewHeader';
|
||||
my $OrderBy;
|
||||
|
||||
# Remove ID if necesary.
|
||||
if ( $Param{SortBy} ) {
|
||||
$Param{SortBy} = ( $Param{SortBy} eq 'InciStateID' )
|
||||
? 'CurInciState'
|
||||
: ( $Param{SortBy} eq 'DeplStateID' ) ? 'CurDeplState'
|
||||
: ( $Param{SortBy} eq 'ClassID' ) ? 'Class'
|
||||
: ( $Param{SortBy} eq 'ChangeTime' ) ? 'LastChanged'
|
||||
: $Param{SortBy};
|
||||
}
|
||||
|
||||
# Set the correct Set CSS class and order by link.
|
||||
if ( $Param{SortBy} && ( $Param{SortBy} eq $Column ) ) {
|
||||
if ( $Param{OrderBy} && ( $Param{OrderBy} eq 'Up' ) ) {
|
||||
$OrderBy = 'Down';
|
||||
$CSS .= ' SortDescendingLarge';
|
||||
}
|
||||
else {
|
||||
$OrderBy = 'Up';
|
||||
$CSS .= ' SortAscendingLarge';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$OrderBy = 'Up';
|
||||
}
|
||||
|
||||
# Set column.
|
||||
$Param{Column} = $Column;
|
||||
|
||||
# Create header block.
|
||||
$LayoutObject->Block(
|
||||
Name => 'Record' . $Column . 'Header',
|
||||
Data => {
|
||||
%Param,
|
||||
CSS => $CSS,
|
||||
OrderBy => $OrderBy,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Get assigned config items (to customer or customer user).
|
||||
my $AssignedCIs = $Self->_AssignedCIsGet(
|
||||
Filter => $Self->{Filter},
|
||||
);
|
||||
my @AssignedConfigItemIDs = @{ $AssignedCIs->{ConfigItemIDs} };
|
||||
|
||||
# Get all assigned config items for count.
|
||||
my $CountCIs = $Self->_AssignedCIsGet(
|
||||
Filter => 'All',
|
||||
Count => 1,
|
||||
);
|
||||
|
||||
# Create search with sorting parameters.
|
||||
my $AssignedConfigItemIDs;
|
||||
if ( IsArrayRefWithData( \@AssignedConfigItemIDs ) ) {
|
||||
$AssignedConfigItemIDs = $ConfigItemObject->ConfigItemSearch(
|
||||
ConfigItemIDs => \@AssignedConfigItemIDs,
|
||||
OrderBy => \@SortByArray,
|
||||
OrderByDirection => \@OrderByArray,
|
||||
);
|
||||
}
|
||||
|
||||
# Clear old array and push new data.
|
||||
if ( IsArrayRefWithData($AssignedConfigItemIDs) ) {
|
||||
|
||||
@AssignedConfigItemIDs = ();
|
||||
push @AssignedConfigItemIDs, @{$AssignedConfigItemIDs};
|
||||
}
|
||||
|
||||
if (@ShowColumns) {
|
||||
|
||||
# Get the XML column headers only if the filter is not set to 'all'
|
||||
# and if there are CIs to show.
|
||||
if ( $Self->{Filter} && $Self->{Filter} ne 'All' && @AssignedConfigItemIDs ) {
|
||||
|
||||
# Get the version data of the first config item, including all the XML data
|
||||
# to get the column header names.
|
||||
my $ConfigItem = $ConfigItemObject->VersionGet(
|
||||
ConfigItemID => $AssignedConfigItemIDs[0],
|
||||
XMLDataGet => 1,
|
||||
);
|
||||
|
||||
# Convert the XML data into a hash.
|
||||
my $ExtendedVersionData = $LayoutObject->XMLData2Hash(
|
||||
XMLDefinition => $ConfigItem->{XMLDefinition},
|
||||
XMLData => $ConfigItem->{XMLData}->[1]->{Version}->[1],
|
||||
Attributes => \@ShowColumns,
|
||||
);
|
||||
|
||||
# Get the xml columns (they contain ::).
|
||||
@XMLShowColumns = grep {/::/} @ShowColumns;
|
||||
|
||||
COLUMN:
|
||||
for my $Column (@XMLShowColumns) {
|
||||
|
||||
# Check if column exists in CI-Data.
|
||||
next COLUMN if !$ExtendedVersionData->{$Column}->{Name};
|
||||
|
||||
# Show the xml attribute header.
|
||||
$LayoutObject->Block(
|
||||
Name => 'RecordXMLAttributeHeader',
|
||||
Data => {
|
||||
%Param,
|
||||
XMLAttributeHeader => $ExtendedVersionData->{$Column}->{Name},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Build filter content.
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewNavBarFilter',
|
||||
Data => {
|
||||
%Filters,
|
||||
},
|
||||
);
|
||||
|
||||
# Set summary for page nav.
|
||||
my %Summary;
|
||||
|
||||
# Loop over filters.
|
||||
my $Count = 0;
|
||||
FILTER:
|
||||
for my $Filter (@NavBarFilters) {
|
||||
|
||||
# Get count by assigned config item ids.
|
||||
$Filter->{Count} = $CountCIs->{ $Filter->{Name} } // '0';
|
||||
|
||||
$Count++;
|
||||
if ( $Count == scalar @NavBarFilters ) {
|
||||
$Filter->{CSS} = 'Last';
|
||||
}
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewNavBarFilterItem',
|
||||
Data => {
|
||||
%Param,
|
||||
%{$Filter},
|
||||
},
|
||||
);
|
||||
if ( $Filter->{Name} eq $Self->{Filter} ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewNavBarFilterItemSelected',
|
||||
Data => {
|
||||
%Param,
|
||||
%{$Filter},
|
||||
},
|
||||
);
|
||||
}
|
||||
else {
|
||||
$LayoutObject->Block(
|
||||
Name => 'OverviewNavBarFilterItemSelectedNot',
|
||||
Data => {
|
||||
%Param,
|
||||
%{$Filter},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# Add filter to new hash.
|
||||
$Summary{ $Filter->{Name} } = \%{$Filter};
|
||||
}
|
||||
|
||||
# Add page nav bar.
|
||||
my $Total = $Summary{ $Self->{Filter} }->{Count} || 0;
|
||||
|
||||
my $LinkPage =
|
||||
'Subaction=Element;Name=' . $Self->{Name}
|
||||
. ';Filter=' . $Self->{Filter}
|
||||
. ';SortBy=' . ( $Param{SortBy} || '' )
|
||||
. ';OrderBy=' . ( $Param{OrderBy} || '' )
|
||||
. ';';
|
||||
|
||||
if ( $Param{CustomerID} ) {
|
||||
$LinkPage .= 'CustomerID=' . $LayoutObject->LinkEncode( $Param{CustomerID} ) . ';';
|
||||
}
|
||||
if ( $Param{CustomerUserID} ) {
|
||||
$LinkPage .= 'CustomerUserID=' . $LayoutObject->LinkEncode( $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 => 'ContentLargeITSMConfigItemGenericFilterNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
|
||||
# Show config items if there are some.
|
||||
my $Counter = 0;
|
||||
if (@AssignedConfigItemIDs) {
|
||||
|
||||
# To store all data.
|
||||
my %Data;
|
||||
|
||||
CONFIGITEMID:
|
||||
for my $ConfigItemID (@AssignedConfigItemIDs) {
|
||||
$Counter++;
|
||||
if (
|
||||
$Counter >= $Self->{StartHit}
|
||||
&& $Counter < ( $Self->{PageShown} + $Self->{StartHit} )
|
||||
)
|
||||
{
|
||||
|
||||
# Get config item data.
|
||||
my $ConfigItem = $ConfigItemObject->VersionGet(
|
||||
ConfigItemID => $ConfigItemID,
|
||||
XMLDataGet => 1,
|
||||
);
|
||||
|
||||
next CONFIGITEMID if !$ConfigItem;
|
||||
|
||||
# Convert the XML data into a hash.
|
||||
my $ExtendedVersionData = $LayoutObject->XMLData2Hash(
|
||||
XMLDefinition => $ConfigItem->{XMLDefinition},
|
||||
XMLData => $ConfigItem->{XMLData}->[1]->{Version}->[1],
|
||||
Attributes => \@ShowColumns,
|
||||
);
|
||||
|
||||
# Store config item data.
|
||||
%Data = %{$ConfigItem};
|
||||
|
||||
# Build record block.
|
||||
$LayoutObject->Block(
|
||||
Name => 'Record',
|
||||
Data => {
|
||||
%Param,
|
||||
%Data,
|
||||
},
|
||||
);
|
||||
|
||||
# Build column record blocks.
|
||||
if (@ShowColumns) {
|
||||
|
||||
COLUMN:
|
||||
for my $Column (@ShowColumns) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'Record' . $Column,
|
||||
Data => {
|
||||
%Param,
|
||||
%Data,
|
||||
CurInciSignal => $InciSignals{ $Data{CurInciStateType} },
|
||||
CurDeplSignal => $DeplSignals{ $Data{CurDeplState} },
|
||||
},
|
||||
);
|
||||
|
||||
# Show links if available.
|
||||
$LayoutObject->Block(
|
||||
Name => 'Record' . $Column . 'LinkStart',
|
||||
Data => {
|
||||
%Param,
|
||||
%Data,
|
||||
},
|
||||
);
|
||||
$LayoutObject->Block(
|
||||
Name => 'Record' . $Column . 'LinkEnd',
|
||||
Data => {
|
||||
%Param,
|
||||
%Data,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
COLUMN:
|
||||
for my $Column (@XMLShowColumns) {
|
||||
|
||||
# Check if column exists in CI-Data.
|
||||
next COLUMN if !$ExtendedVersionData->{$Column}->{Name};
|
||||
|
||||
# Convert to ascii text in case the value contains html.
|
||||
my $Value = $Kernel::OM->Get('Kernel::System::HTMLUtils')
|
||||
->ToAscii( String => $ExtendedVersionData->{$Column}->{Value} // '' )
|
||||
// '';
|
||||
|
||||
# Convert all whitespace and newlines to single spaces.
|
||||
$Value =~ s{ \s+ }{ }gxms;
|
||||
|
||||
# Show the xml attribute data.
|
||||
$LayoutObject->Block(
|
||||
Name => 'RecordXMLAttribute',
|
||||
Data => {
|
||||
%Param,
|
||||
XMLAttributeData => $Value,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If there are no config items to show, a no data found message is displayed in the table.
|
||||
else {
|
||||
$LayoutObject->Block(
|
||||
Name => 'NoDataFoundMsg',
|
||||
Data => {
|
||||
TotalColumns => scalar @ShowColumns,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
# Send data to JS.
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'ITSMConfigItemGeneric',
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
},
|
||||
);
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardITSMConfigItemGeneric',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
StyleClasses => $StyleClasses,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
sub _AssignedCIsGet {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# Define cache key.
|
||||
my $CacheKey;
|
||||
if ( $Self->{CustomerID} ) {
|
||||
$CacheKey = 'AssignedCIs-' . $Param{Filter} . '-' . $Self->{CustomerID};
|
||||
}
|
||||
elsif ( $Self->{CustomerUserID} ) {
|
||||
$CacheKey = 'AssignedCIs-' . $Param{Filter} . '-' . $Self->{CustomerUserID};
|
||||
}
|
||||
else {
|
||||
$CacheKey = 'AssignedCIs-' . $Param{Filter} . '-' . $Self->{UserID};
|
||||
}
|
||||
|
||||
my $Content = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
||||
Type => 'DashboardITSMConfigItemGeneric',
|
||||
Key => $CacheKey,
|
||||
);
|
||||
return $Content if defined $Content && $Param{Count};
|
||||
|
||||
# Get the defined param.
|
||||
my @Params = split /;/, $Self->{Config}->{Attributes};
|
||||
|
||||
# Define result.
|
||||
my @ConfigItemIDs;
|
||||
my %Result;
|
||||
|
||||
# Get key for CI search.
|
||||
my $ConfigItemKey = $Self->{Config}->{ConfigItemKey} // '';
|
||||
|
||||
return if !$ConfigItemKey;
|
||||
|
||||
# Get config item object.
|
||||
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
|
||||
|
||||
STRING:
|
||||
for my $String (@Params) {
|
||||
next STRING if !$String;
|
||||
my ( $Key, $Value ) = split /=/, $String;
|
||||
|
||||
if ( $Key eq 'CustomerCompany' ) {
|
||||
|
||||
CLASS:
|
||||
for my $Class ( sort keys %{$ConfigItemKey} ) {
|
||||
|
||||
# Skip other classes if filter is set.
|
||||
next CLASS if $Param{Filter} && ( $Param{Filter} ne $Class && $Param{Filter} ne 'All' );
|
||||
|
||||
my $ClassID = $Self->_ClassIDByNameGet(
|
||||
Value => $Class,
|
||||
);
|
||||
|
||||
# Skip if we have no class id.
|
||||
next CLASS if !$ClassID;
|
||||
|
||||
my @SearchKey = (
|
||||
{
|
||||
"[1]{'Version'}[1]{'$ConfigItemKey->{$Class}'}[%]{'Content'}" => $Self->{CustomerID},
|
||||
}
|
||||
);
|
||||
|
||||
# Perform config item search (extended).
|
||||
my $ConfigItemIDs = $ConfigItemObject->ConfigItemSearchExtended(
|
||||
ClassIDs => [$ClassID],
|
||||
What => \@SearchKey,
|
||||
);
|
||||
|
||||
next CLASS if !IsArrayRefWithData($ConfigItemIDs);
|
||||
|
||||
push @ConfigItemIDs, @{$ConfigItemIDs};
|
||||
|
||||
# Count config items per class.
|
||||
if ( $Param{Count} ) {
|
||||
$Result{$Class} = scalar @{$ConfigItemIDs};
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ( $Key eq 'Customer' ) {
|
||||
|
||||
CLASS:
|
||||
for my $Class ( sort keys %{$ConfigItemKey} ) {
|
||||
|
||||
# Skip other classes if filter is set.
|
||||
next CLASS if $Param{Filter} && ( $Param{Filter} ne $Class && $Param{Filter} ne 'All' );
|
||||
|
||||
my $ClassID = $Self->_ClassIDByNameGet(
|
||||
Value => $Class,
|
||||
);
|
||||
|
||||
# Skip if we have no class id.
|
||||
next CLASS if !$ClassID;
|
||||
|
||||
my @SearchKey = (
|
||||
{
|
||||
"[1]{'Version'}[1]{'$ConfigItemKey->{$Class}'}[%]{'Content'}" => $Self->{CustomerUserID},
|
||||
}
|
||||
);
|
||||
|
||||
# Perform config item search (extended).
|
||||
my $ConfigItemIDs = $ConfigItemObject->ConfigItemSearchExtended(
|
||||
ClassIDs => [$ClassID],
|
||||
What => \@SearchKey,
|
||||
);
|
||||
|
||||
next CLASS if !IsArrayRefWithData($ConfigItemIDs);
|
||||
|
||||
push @ConfigItemIDs, @{$ConfigItemIDs};
|
||||
|
||||
# Count config items per class.
|
||||
if ( $Param{Count} ) {
|
||||
$Result{$Class} = scalar @{$ConfigItemIDs};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
||||
Priority => 'error',
|
||||
Message => 'Need CustomerCompany or Customer as attribute.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Remove duplicated config item ids.
|
||||
my %TempConfigItemIDs = map { $_ => 1 } @ConfigItemIDs;
|
||||
@ConfigItemIDs = sort keys %TempConfigItemIDs;
|
||||
|
||||
$Result{ConfigItemIDs} = \@ConfigItemIDs;
|
||||
|
||||
# Add count for all items.
|
||||
if ( $Param{Count} ) {
|
||||
$Result{All} = scalar @ConfigItemIDs;
|
||||
}
|
||||
|
||||
if ( $Self->{Config}->{CacheTTLLocal} && $Param{Count} ) {
|
||||
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
||||
Type => 'DashboardITSMConfigItemGeneric',
|
||||
Key => $CacheKey,
|
||||
Value => \%Result,
|
||||
TTL => 2 * 60,
|
||||
);
|
||||
}
|
||||
|
||||
return \%Result;
|
||||
}
|
||||
|
||||
sub _ClassIDByNameGet {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# Check needed stuff.
|
||||
if ( !$Param{Value} ) {
|
||||
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
||||
Priority => 'error',
|
||||
Message => "Need class name!",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
# Get general catalog object.
|
||||
my $GeneralCatalogObject = $Kernel::OM->Get('Kernel::System::GeneralCatalog');
|
||||
|
||||
# Get class list.
|
||||
my $ClassList = $GeneralCatalogObject->ItemList(
|
||||
Class => 'ITSM::ConfigItem::Class',
|
||||
);
|
||||
|
||||
# Reverse class list.
|
||||
my %ClassName2ID = reverse %{$ClassList};
|
||||
|
||||
# Get class id.
|
||||
my $ClassID = $ClassName2ID{ $Param{Value} };
|
||||
|
||||
return if !$ClassID;
|
||||
|
||||
#Initiate permission check.
|
||||
my $ConfigItemObject = $Kernel::OM->Get('Kernel::System::ITSMConfigItem');
|
||||
|
||||
my $HasAccess = $ConfigItemObject->Permission(
|
||||
Scope => 'Class',
|
||||
ClassID => $ClassID,
|
||||
UserID => $Self->{UserID},
|
||||
Type => $Self->{Config}->{Permission},
|
||||
);
|
||||
return if !$HasAccess;
|
||||
|
||||
# Return class id.
|
||||
return $ClassID;
|
||||
}
|
||||
|
||||
1;
|
||||
61
Perl OTRS/Kernel/Output/HTML/Dashboard/Image.pm
Normal file
61
Perl OTRS/Kernel/Output/HTML/Dashboard/Image.pm
Normal file
@@ -0,0 +1,61 @@
|
||||
# --
|
||||
# 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::Image;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# prevent 'Used once' warning
|
||||
use Kernel::System::ObjectManager;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $Content = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Output(
|
||||
TemplateFile => 'AgentDashboardImage',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
61
Perl OTRS/Kernel/Output/HTML/Dashboard/MOTD.pm
Normal file
61
Perl OTRS/Kernel/Output/HTML/Dashboard/MOTD.pm
Normal file
@@ -0,0 +1,61 @@
|
||||
# --
|
||||
# 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::MOTD;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# prevent 'Used once' warning
|
||||
use Kernel::System::ObjectManager;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $Content = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->Output(
|
||||
TemplateFile => 'Motd',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
174
Perl OTRS/Kernel/Output/HTML/Dashboard/News.pm
Normal file
174
Perl OTRS/Kernel/Output/HTML/Dashboard/News.pm
Normal file
@@ -0,0 +1,174 @@
|
||||
# --
|
||||
# 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::News;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
CacheKey => 'RSSNewsFeed-'
|
||||
. $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage},
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# check if cloud services are disabled
|
||||
my $CloudServicesDisabled = $Kernel::OM->Get('Kernel::Config')->Get('CloudServices::Disabled');
|
||||
|
||||
return '' if $CloudServicesDisabled;
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $Language = $LayoutObject->{UserLanguage};
|
||||
|
||||
# cleanup main language for languages like es_MX (es in this case)
|
||||
$Language = substr $Language, 0, 2;
|
||||
|
||||
my $CloudService = 'PublicFeeds';
|
||||
my $Operation = 'NewsFeed';
|
||||
|
||||
# prepare cloud service request
|
||||
my %RequestParams = (
|
||||
RequestData => {
|
||||
$CloudService => [
|
||||
{
|
||||
Operation => $Operation,
|
||||
Data => {
|
||||
Language => $Language,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
# get cloud service object
|
||||
my $CloudServiceObject = $Kernel::OM->Get('Kernel::System::CloudService::Backend::Run');
|
||||
|
||||
# dispatch the cloud service request
|
||||
my $RequestResult = $CloudServiceObject->Request(%RequestParams);
|
||||
|
||||
# as this is the only operation an unsuccessful request means that the operation was also
|
||||
# unsuccessful
|
||||
if ( !IsHashRefWithData($RequestResult) ) {
|
||||
return $LayoutObject->{LanguageObject}->Translate('Can\'t connect to OTRS News server!');
|
||||
}
|
||||
|
||||
my $OperationResult = $CloudServiceObject->OperationResultGet(
|
||||
RequestResult => $RequestResult,
|
||||
CloudService => $CloudService,
|
||||
Operation => $Operation,
|
||||
);
|
||||
|
||||
if ( !IsHashRefWithData($OperationResult) ) {
|
||||
return $LayoutObject->{LanguageObject}->Translate('Can\'t get OTRS News from server!');
|
||||
}
|
||||
elsif ( !$OperationResult->{Success} ) {
|
||||
return $OperationResult->{ErrorMessage} ||
|
||||
$LayoutObject->{LanguageObject}->Translate('Can\'t get OTRS News from server!');
|
||||
}
|
||||
|
||||
my $NewsFeed = $OperationResult->{Data}->{News};
|
||||
|
||||
return if !IsArrayRefWithData($NewsFeed);
|
||||
|
||||
my $Count = 0;
|
||||
ITEM:
|
||||
for my $Item ( @{$NewsFeed} ) {
|
||||
$Count++;
|
||||
|
||||
last ITEM if $Count > $Self->{Config}->{Limit};
|
||||
|
||||
my $Time = $Item->{Time};
|
||||
my $Ago;
|
||||
|
||||
if ($Time) {
|
||||
|
||||
my $SystemDateTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $Time,
|
||||
},
|
||||
);
|
||||
|
||||
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
|
||||
$Ago = $CurSystemDateTimeObject->ToEpoch() - $SystemDateTimeObject->ToEpoch();
|
||||
$Ago = $LayoutObject->CustomerAge(
|
||||
Age => $Ago,
|
||||
Space => ' ',
|
||||
);
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSSOverviewRow',
|
||||
Data => { %{$Item} },
|
||||
);
|
||||
if ($Ago) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSSTimeStamp',
|
||||
Data => {
|
||||
Ago => $Ago,
|
||||
%{$Item},
|
||||
},
|
||||
);
|
||||
}
|
||||
else {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSS',
|
||||
Data => {
|
||||
Ago => $Ago,
|
||||
%{$Item},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardRSSOverview',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
241
Perl OTRS/Kernel/Output/HTML/Dashboard/ProductNotify.pm
Normal file
241
Perl OTRS/Kernel/Output/HTML/Dashboard/ProductNotify.pm
Normal file
@@ -0,0 +1,241 @@
|
||||
# --
|
||||
# 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::ProductNotify;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# check if cloud services are disabled
|
||||
my $CloudServicesDisabled = $ConfigObject->Get('CloudServices::Disabled') || 0;
|
||||
|
||||
return '' if $CloudServicesDisabled;
|
||||
|
||||
# get current
|
||||
my $Product = $ConfigObject->Get('Product');
|
||||
my $Version = $ConfigObject->Get('Version');
|
||||
|
||||
my $CloudService = 'PublicFeeds';
|
||||
my $Operation = 'ProductFeed';
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
# check cache
|
||||
my $CacheKey = "CloudService::" . $CloudService . "::Operation::" . $Operation . "::Language::"
|
||||
. $LayoutObject->{UserLanguage} . "::Product::" . $Product . "::Version::$Version";
|
||||
|
||||
# get cache object
|
||||
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
||||
|
||||
my $CacheContent = $CacheObject->Get(
|
||||
Type => 'DashboardProductNotify',
|
||||
Key => $CacheKey,
|
||||
);
|
||||
|
||||
return $CacheContent if defined $CacheContent;
|
||||
|
||||
# prepare cloud service request
|
||||
my %RequestParams = (
|
||||
RequestData => {
|
||||
$CloudService => [
|
||||
{
|
||||
Operation => $Operation,
|
||||
Data => {
|
||||
Product => $Product,
|
||||
Version => $Version,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
# get cloud service object
|
||||
my $CloudServiceObject = $Kernel::OM->Get('Kernel::System::CloudService::Backend::Run');
|
||||
|
||||
# dispatch the cloud service request
|
||||
my $RequestResult = $CloudServiceObject->Request(%RequestParams);
|
||||
|
||||
# as this is the only operation an unsuccessful request means that the operation was also
|
||||
# unsuccessful
|
||||
if ( !IsHashRefWithData($RequestResult) ) {
|
||||
return $LayoutObject->{LanguageObject}->Translate('Can\'t connect to Product News server!');
|
||||
}
|
||||
|
||||
my $OperationResult = $CloudServiceObject->OperationResultGet(
|
||||
RequestResult => $RequestResult,
|
||||
CloudService => $CloudService,
|
||||
Operation => $Operation,
|
||||
);
|
||||
|
||||
if ( !IsHashRefWithData($OperationResult) ) {
|
||||
return $LayoutObject->{LanguageObject}->Translate('Can\'t get Product News from server!');
|
||||
}
|
||||
elsif ( !$OperationResult->{Success} ) {
|
||||
return $OperationResult->{ErrorMessage} ||
|
||||
$LayoutObject->{LanguageObject}->Translate('Can\'t get Product News from server!');
|
||||
}
|
||||
|
||||
my $ProductFeed = $OperationResult->{Data};
|
||||
|
||||
# remember if content got shown
|
||||
my $ContentFound = 0;
|
||||
|
||||
# show messages
|
||||
if ( IsArrayRefWithData( $ProductFeed->{Message} ) ) {
|
||||
|
||||
MESSAGE:
|
||||
for my $Message ( @{ $ProductFeed->{Message} } ) {
|
||||
|
||||
next MESSAGE if !$Message;
|
||||
|
||||
# remember if content got shown
|
||||
$ContentFound = 1;
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentProductMessage',
|
||||
Data => {
|
||||
Message => $Message,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# show release updates
|
||||
if ( IsArrayRefWithData( $ProductFeed->{Release} ) ) {
|
||||
|
||||
RELEASE:
|
||||
for my $Release ( @{ $ProductFeed->{Release} } ) {
|
||||
|
||||
next RELEASE if !$Release;
|
||||
|
||||
# check if release is newer then the installed one
|
||||
next RELEASE if !$Self->_CheckVersion(
|
||||
Version1 => $Version,
|
||||
Version2 => $Release->{Version},
|
||||
);
|
||||
|
||||
# remember if content got shown
|
||||
$ContentFound = 1;
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentProductRelease',
|
||||
Data => {
|
||||
Name => $Release->{Name},
|
||||
Version => $Release->{Version},
|
||||
Link => $Release->{Link},
|
||||
Severity => $Release->{Severity},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# check if content got shown, if true, render block
|
||||
my $Content;
|
||||
if ($ContentFound) {
|
||||
$Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardProductNotify',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# check if we need to set CacheTTL based on feed
|
||||
if ( $ProductFeed->{CacheTTL} ) {
|
||||
$Self->{Config}->{CacheTTLLocal} = $ProductFeed->{CacheTTL};
|
||||
}
|
||||
|
||||
# cache result
|
||||
if ( $Self->{Config}->{CacheTTLLocal} ) {
|
||||
$CacheObject->Set(
|
||||
Type => 'DashboardProductNotify',
|
||||
Key => $CacheKey,
|
||||
Value => $Content || '',
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
}
|
||||
|
||||
# return content
|
||||
return $Content;
|
||||
}
|
||||
|
||||
sub _CheckVersion {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# check needed stuff
|
||||
for my $Needed (qw(Version1 Version2)) {
|
||||
if ( !defined $Param{$Needed} ) {
|
||||
$Kernel::OM->Get('Kernel::System::Log')->Log(
|
||||
Priority => 'error',
|
||||
Message => "$Needed not defined!",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
for my $Type (qw(Version1 Version2)) {
|
||||
$Param{$Type} =~ s/\s/\./g;
|
||||
$Param{$Type} =~ s/[A-z]/0/g;
|
||||
|
||||
my @Parts = split /\./, $Param{$Type};
|
||||
$Param{$Type} = 0;
|
||||
for ( 0 .. 4 ) {
|
||||
if ( IsNumber( $Parts[$_] ) ) {
|
||||
$Param{$Type} .= sprintf( "%04d", $Parts[$_] );
|
||||
}
|
||||
else {
|
||||
$Param{$Type} .= '0000';
|
||||
}
|
||||
}
|
||||
$Param{$Type} = int( $Param{$Type} );
|
||||
}
|
||||
|
||||
return 1 if ( $Param{Version2} > $Param{Version1} );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
1;
|
||||
177
Perl OTRS/Kernel/Output/HTML/Dashboard/RSS.pm
Normal file
177
Perl OTRS/Kernel/Output/HTML/Dashboard/RSS.pm
Normal file
@@ -0,0 +1,177 @@
|
||||
# --
|
||||
# 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::RSS;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use LWP::UserAgent;
|
||||
use XML::FeedPP;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
CacheKey => 'RSS'
|
||||
. $Self->{Config}->{URL} . '-'
|
||||
. $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{UserLanguage},
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# Default URL
|
||||
my $FeedURL = $Self->{Config}->{URL};
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $Language = $LayoutObject->{UserLanguage};
|
||||
|
||||
# Check if URL for UserLanguage is available
|
||||
if ( $Self->{Config}->{"URL_$Language"} ) {
|
||||
$FeedURL = $Self->{Config}->{"URL_$Language"};
|
||||
}
|
||||
else {
|
||||
|
||||
# Check for main language for languages like es_MX (es in this case)
|
||||
($Language) = split /_/, $Language;
|
||||
if ( $Self->{Config}->{"URL_$Language"} ) {
|
||||
$FeedURL = $Self->{Config}->{"URL_$Language"};
|
||||
}
|
||||
}
|
||||
|
||||
# Configure a local instance of LWP::UserAgent for passing to XML::FeedPP.
|
||||
my $UserAgent = LWP::UserAgent->new();
|
||||
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# In some scenarios like transparent HTTPS proxies, it can be necessary to turn off
|
||||
# SSL certificate validation.
|
||||
if ( $ConfigObject->Get('WebUserAgent::DisableSSLVerification') ) {
|
||||
$UserAgent->ssl_opts(
|
||||
verify_hostname => 0,
|
||||
);
|
||||
}
|
||||
|
||||
# Set proxy settings if configured, and make sure to allow all supported protocols
|
||||
# (please see bug#12512 for more information).
|
||||
my $Proxy = $ConfigObject->Get('WebUserAgent::Proxy');
|
||||
if ($Proxy) {
|
||||
$UserAgent->proxy( [ 'http', 'https', 'ftp' ], $Proxy );
|
||||
}
|
||||
|
||||
# get content
|
||||
my $Feed;
|
||||
|
||||
TRY:
|
||||
for ( 1 .. 3 ) {
|
||||
$Feed = eval {
|
||||
XML::FeedPP->new(
|
||||
$FeedURL,
|
||||
'xml_deref' => 1,
|
||||
'utf8_flag' => 1,
|
||||
'lwp_useragent' => $UserAgent,
|
||||
);
|
||||
};
|
||||
last TRY if $Feed;
|
||||
}
|
||||
|
||||
if ( !$Feed ) {
|
||||
return $LayoutObject->{LanguageObject}->Translate( 'Can\'t connect to %s!', $FeedURL );
|
||||
}
|
||||
|
||||
my $Count = 0;
|
||||
ITEM:
|
||||
for my $Item ( $Feed->get_item() ) {
|
||||
$Count++;
|
||||
last ITEM if $Count > $Self->{Config}->{Limit};
|
||||
my $Time = $Item->pubDate();
|
||||
my $Ago;
|
||||
if ($Time) {
|
||||
my $SystemDateTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $Time,
|
||||
},
|
||||
);
|
||||
|
||||
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
|
||||
$Ago = $CurSystemDateTimeObject->ToEpoch() - $SystemDateTimeObject->ToEpoch();
|
||||
$Ago = $LayoutObject->CustomerAge(
|
||||
Age => $Ago,
|
||||
Space => ' ',
|
||||
);
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSSOverviewRow',
|
||||
Data => {
|
||||
Title => $Item->title(),
|
||||
Link => $Item->link(),
|
||||
},
|
||||
);
|
||||
if ($Ago) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSSTimeStamp',
|
||||
Data => {
|
||||
Ago => $Ago,
|
||||
Title => $Item->title(),
|
||||
Link => $Item->link(),
|
||||
},
|
||||
);
|
||||
}
|
||||
else {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallRSS',
|
||||
Data => {
|
||||
Ago => $Ago,
|
||||
Title => $Item->title(),
|
||||
Link => $Item->link(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardRSSOverview',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
282
Perl OTRS/Kernel/Output/HTML/Dashboard/Stats.pm
Normal file
282
Perl OTRS/Kernel/Output/HTML/Dashboard/Stats.pm
Normal file
@@ -0,0 +1,282 @@
|
||||
# --
|
||||
# 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::Stats;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::VariableCheck qw(:all);
|
||||
|
||||
use Kernel::System::Stats;
|
||||
use Kernel::Output::HTML::Statistics::View;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
# Settings
|
||||
$Self->{PrefKeyStatsConfiguration} = 'UserDashboardStatsStatsConfiguration' . $Self->{Name};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get StatID
|
||||
my $StatID = $Self->{Config}->{StatID};
|
||||
|
||||
my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet( StatID => $StatID );
|
||||
|
||||
# get the object name
|
||||
if ( $Stat->{StatType} eq 'static' ) {
|
||||
$Stat->{ObjectName} = $Stat->{File};
|
||||
}
|
||||
|
||||
# if no object name is defined use an empty string
|
||||
$Stat->{ObjectName} ||= '';
|
||||
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
||||
|
||||
# check if the user has preferences for this widget
|
||||
my %Preferences = $UserObject->GetPreferences(
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
my $StatsSettings;
|
||||
if ( $Preferences{ $Self->{PrefKeyStatsConfiguration} } ) {
|
||||
$StatsSettings = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
|
||||
Data => $Preferences{ $Self->{PrefKeyStatsConfiguration} },
|
||||
);
|
||||
}
|
||||
|
||||
my %Format = %{ $Kernel::OM->Get('Kernel::Config')->Get('Stats::Format') || {} };
|
||||
|
||||
my %FilteredFormats;
|
||||
for my $Key ( sort keys %Format ) {
|
||||
$FilteredFormats{$Key} = $Format{$Key} if $Key =~ m{^D3}smx;
|
||||
}
|
||||
|
||||
my @Errors;
|
||||
|
||||
my %GetParam = eval {
|
||||
$Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsParamsGet(
|
||||
Stat => $Stat,
|
||||
UserGetParam => $StatsSettings,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
};
|
||||
|
||||
if ( $@ || ref $@ eq 'ARRAY' ) {
|
||||
@Errors = @{$@};
|
||||
}
|
||||
|
||||
# Fetch the stat again as StatsParamGet might have modified it in between.
|
||||
$Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
|
||||
StatID => $StatID,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
my $StatsParamsWidget = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsParamsWidget(
|
||||
Stat => $Stat,
|
||||
UserGetParam => $StatsSettings,
|
||||
Formats => \%FilteredFormats,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
|
||||
# This indicates that there are configuration errors in the statistic.
|
||||
# In that case we show a warning in Run() and no configuration here.
|
||||
if ( !$StatsParamsWidget ) {
|
||||
return;
|
||||
}
|
||||
|
||||
my $SettingsHTML = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardStatsSettings',
|
||||
Data => {
|
||||
%{$Stat},
|
||||
Errors => \@Errors,
|
||||
JSONFieldName => $Self->{PrefKeyStatsConfiguration},
|
||||
NamePref => $Self->{Name},
|
||||
StatsParamsWidget => $StatsParamsWidget,
|
||||
},
|
||||
);
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => 'Stats Configuration',
|
||||
Name => $Self->{PrefKeyStatsConfiguration},
|
||||
Block => 'RawHTML',
|
||||
HTML => $SettingsHTML,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} }
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $StatID = $Self->{Config}->{StatID};
|
||||
|
||||
my %Preferences = $Kernel::OM->Get('Kernel::System::User')->GetPreferences(
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
my $StatsSettings = {};
|
||||
|
||||
# get JSON object
|
||||
my $JSONObject = $Kernel::OM->Get('Kernel::System::JSON');
|
||||
|
||||
if ( $Preferences{ $Self->{PrefKeyStatsConfiguration} } ) {
|
||||
$StatsSettings = $JSONObject->Decode(
|
||||
Data => $Preferences{ $Self->{PrefKeyStatsConfiguration} },
|
||||
);
|
||||
}
|
||||
|
||||
my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet( StatID => $StatID );
|
||||
|
||||
my $StatConfigurationValid = $Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsConfigurationValidate(
|
||||
Stat => $Stat,
|
||||
Errors => {},
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
|
||||
my $StatParametersValid;
|
||||
eval {
|
||||
$Kernel::OM->Get('Kernel::Output::HTML::Statistics::View')->StatsParamsGet(
|
||||
Stat => $Stat,
|
||||
UserGetParam => $StatsSettings,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
$StatParametersValid = 1;
|
||||
};
|
||||
|
||||
my $CachedData;
|
||||
|
||||
if ( $StatConfigurationValid && $StatParametersValid ) {
|
||||
$CachedData = $Kernel::OM->Get('Kernel::System::Stats')->StatsResultCacheGet(
|
||||
StatID => $StatID,
|
||||
UserGetParam => $StatsSettings,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
}
|
||||
|
||||
my $Format = $StatsSettings->{Format};
|
||||
if ( !$Format ) {
|
||||
my $Stat = $Kernel::OM->Get('Kernel::System::Stats')->StatsGet(
|
||||
StatID => $StatID,
|
||||
UserID => $Self->{UserID},
|
||||
);
|
||||
STATFORMAT:
|
||||
for my $StatFormat ( @{ $Stat->{Format} || [] } ) {
|
||||
if ( $StatFormat =~ m{^D3}smx ) {
|
||||
$Format = $StatFormat;
|
||||
last STATFORMAT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
# check permission for AgentStatistics
|
||||
my $StatsReg = $Kernel::OM->Get('Kernel::Config')->Get('Frontend::Module')->{'AgentStatistics'};
|
||||
my $AgentStatisticsFrontendPermission = 0;
|
||||
if ( !$StatsReg->{GroupRo} && !$StatsReg->{Group} ) {
|
||||
$AgentStatisticsFrontendPermission = 1;
|
||||
}
|
||||
else {
|
||||
my $GroupObject = $Kernel::OM->Get('Kernel::System::Group');
|
||||
TYPE:
|
||||
for my $Type (qw(GroupRo Group)) {
|
||||
my $StatsGroups = ref $StatsReg->{$Type} eq 'ARRAY' ? $StatsReg->{$Type} : [ $StatsReg->{$Type} ];
|
||||
GROUP:
|
||||
for my $StatsGroup ( @{$StatsGroups} ) {
|
||||
next GROUP if !$StatsGroup;
|
||||
next GROUP if !$GroupObject->PermissionCheck(
|
||||
UserID => $Self->{UserID},
|
||||
GroupName => $StatsGroup,
|
||||
Type => 'ro',
|
||||
);
|
||||
|
||||
$AgentStatisticsFrontendPermission = 1;
|
||||
last TYPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $StatsResultDataJSON = $LayoutObject->JSONEncode(
|
||||
Data => $CachedData,
|
||||
NoQuotes => 1,
|
||||
);
|
||||
|
||||
my $StatsFormatJSON = $LayoutObject->JSONEncode(
|
||||
Data => $Format,
|
||||
NoQuotes => 1,
|
||||
);
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'StatsData' . $StatID,
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
Format => $StatsFormatJSON,
|
||||
StatResultData => $StatsResultDataJSON,
|
||||
Preferences => $Preferences{ 'GraphWidget' . $Self->{Name} } || '{}',
|
||||
MaxXaxisAttributes => $Kernel::OM->Get('Kernel::Config')->Get('Stats::MaxXaxisAttributes'),
|
||||
},
|
||||
);
|
||||
|
||||
if ( $Self->{UserRefreshTime} ) {
|
||||
my $Refresh = 60 * $Self->{UserRefreshTime};
|
||||
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'WidgetRefreshStat' . $StatID,
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $Self->{Name},
|
||||
RefreshTime => $Refresh,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardStats',
|
||||
Data => {
|
||||
Name => $Self->{Name},
|
||||
StatConfigurationValid => $StatConfigurationValid,
|
||||
StatParametersValid => $StatParametersValid,
|
||||
StatResultData => $CachedData,
|
||||
Stat => $Stat,
|
||||
Format => $Format,
|
||||
AgentStatisticsFrontendPermission => $AgentStatisticsFrontendPermission,
|
||||
Preferences => $Preferences{ 'GraphWidget' . $Self->{Name} } || '{}',
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
2611
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketGeneric.pm
Normal file
2611
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketGeneric.pm
Normal file
File diff suppressed because it is too large
Load Diff
306
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketQueueOverview.pm
Normal file
306
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketQueueOverview.pm
Normal file
@@ -0,0 +1,306 @@
|
||||
# --
|
||||
# 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::TicketQueueOverview;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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} );
|
||||
}
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
$Self->{CacheKey} = $Self->{Name} . '-' . $Self->{UserID};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $LimitGroup = $Self->{Config}->{QueuePermissionGroup} || 0;
|
||||
my $CacheKey = 'User' . '-' . $Self->{UserID} . '-' . $LimitGroup;
|
||||
|
||||
# get cache object
|
||||
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
||||
|
||||
my $Content = $CacheObject->Get(
|
||||
Type => 'DashboardQueueOverview',
|
||||
Key => $CacheKey,
|
||||
);
|
||||
return $Content if defined $Content;
|
||||
|
||||
# get configured states, get their state ID and test if they exist while we do it
|
||||
my %States;
|
||||
my $StateIDURL;
|
||||
my %ConfiguredStates = %{ $Self->{Config}->{States} };
|
||||
for my $StateOrder ( sort { $a <=> $b } keys %ConfiguredStates ) {
|
||||
my $State = $ConfiguredStates{$StateOrder};
|
||||
|
||||
# check if state is found, to record StateID
|
||||
my $StateID = $Kernel::OM->Get('Kernel::System::State')->StateLookup(
|
||||
State => $State,
|
||||
) || '';
|
||||
if ($StateID) {
|
||||
$States{$State} = $StateID;
|
||||
|
||||
# append StateID to URL for search string
|
||||
$StateIDURL .= "StateIDs=$StateID;";
|
||||
}
|
||||
else {
|
||||
|
||||
# state does not exist, skipping
|
||||
delete $ConfiguredStates{$StateOrder};
|
||||
}
|
||||
}
|
||||
|
||||
# get queue object
|
||||
my $QueueObject = $Kernel::OM->Get('Kernel::System::Queue');
|
||||
|
||||
# get all queues
|
||||
my %Queues = $QueueObject->GetAllQueues(
|
||||
UserID => $Self->{UserID},
|
||||
Type => 'ro',
|
||||
);
|
||||
|
||||
# limit them by QueuePermissionGroup if needed
|
||||
my $LimitGroupID;
|
||||
if ($LimitGroup) {
|
||||
$LimitGroupID = $Kernel::OM->Get('Kernel::System::Group')->GroupLookup(
|
||||
Group => $LimitGroup,
|
||||
);
|
||||
}
|
||||
|
||||
my $Sort = $Self->{Config}->{Sort} || '';
|
||||
|
||||
my %QueueToID;
|
||||
my $QueueIDURL;
|
||||
|
||||
# lookup queues, add their QueueID to new hash (needed for Search)
|
||||
QUEUES:
|
||||
for my $QueueID ( sort keys %Queues ) {
|
||||
|
||||
# see if we have to remove the queue based on LimitGroup
|
||||
if ($LimitGroup) {
|
||||
my $GroupID = $QueueObject->GetQueueGroupID(
|
||||
QueueID => $QueueID,
|
||||
);
|
||||
if ( $GroupID != $LimitGroupID ) {
|
||||
delete $Queues{$QueueID};
|
||||
next QUEUES;
|
||||
}
|
||||
}
|
||||
|
||||
# add queue to reverse hash
|
||||
$QueueToID{ $Queues{$QueueID} } = $QueueID;
|
||||
|
||||
# add queue to SearchURL
|
||||
$QueueIDURL .= "QueueIDs=$QueueID;";
|
||||
}
|
||||
|
||||
# Prepare ticket count.
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
my @QueueIDs = sort keys %Queues;
|
||||
if ( !@QueueIDs ) {
|
||||
@QueueIDs = (999_999);
|
||||
}
|
||||
|
||||
my %Results;
|
||||
for my $StateOrderID ( sort { $a <=> $b } keys %ConfiguredStates ) {
|
||||
|
||||
# Run ticket search for all Queues and appropriate available State.
|
||||
my @StateOrderTicketIDs = $TicketObject->TicketSearch(
|
||||
UserID => $Self->{UserID},
|
||||
Result => 'ARRAY',
|
||||
QueueIDs => \@QueueIDs,
|
||||
States => [ $ConfiguredStates{$StateOrderID} ],
|
||||
Limit => 100_000,
|
||||
);
|
||||
|
||||
# Count of tickets per QueueID.
|
||||
my $TicketCountByQueueID = $TicketObject->TicketCountByAttribute(
|
||||
Attribute => 'QueueID',
|
||||
TicketIDs => \@StateOrderTicketIDs,
|
||||
);
|
||||
|
||||
# Gather ticket count for corresponding Queue <-> State.
|
||||
for my $QueueID (@QueueIDs) {
|
||||
push @{ $Results{ $Queues{$QueueID} } },
|
||||
$TicketCountByQueueID->{$QueueID} ? $TicketCountByQueueID->{$QueueID} : 0;
|
||||
}
|
||||
}
|
||||
|
||||
# build header
|
||||
my @Headers = ( 'Queue', );
|
||||
for my $StateOrder ( sort { $a <=> $b } keys %ConfiguredStates ) {
|
||||
push @Headers, $ConfiguredStates{$StateOrder};
|
||||
}
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
for my $HeaderItem (@Headers) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewHeaderStatus',
|
||||
Data => {
|
||||
Text => $HeaderItem,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
my $HasContent;
|
||||
|
||||
# iterate over all queues, print results;
|
||||
my @StatusTotal;
|
||||
QUEUE:
|
||||
for my $Queue ( sort values %Queues ) {
|
||||
|
||||
# Hide empty queues
|
||||
if ( !grep { defined $_ && $_ > 0 } @{ $Results{$Queue} } ) {
|
||||
next QUEUE;
|
||||
}
|
||||
|
||||
$HasContent++;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewQueueName',
|
||||
Data => {
|
||||
QueueID => $QueueToID{$Queue},
|
||||
QueueName => $Queue,
|
||||
}
|
||||
);
|
||||
|
||||
# iterate over states
|
||||
my $Counter = 0;
|
||||
my $RowTotal;
|
||||
for my $StateOrderID ( sort { $a <=> $b } keys %ConfiguredStates ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewQueueResults',
|
||||
Data => {
|
||||
Number => $Results{$Queue}->[$Counter],
|
||||
QueueID => $QueueToID{$Queue},
|
||||
StateID => $States{ $ConfiguredStates{$StateOrderID} },
|
||||
State => $ConfiguredStates{$StateOrderID},
|
||||
Sort => $Sort,
|
||||
},
|
||||
);
|
||||
$RowTotal += $Results{$Queue}->[$Counter] || 0;
|
||||
$StatusTotal[$StateOrderID] += $Results{$Queue}->[$Counter] || 0;
|
||||
$Counter++;
|
||||
}
|
||||
|
||||
# print row (queue) total
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewQueueTotal',
|
||||
Data => {
|
||||
Number => $RowTotal,
|
||||
QueueID => $QueueToID{$Queue},
|
||||
StateIDs => $StateIDURL,
|
||||
Sort => $Sort,
|
||||
},
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ($HasContent) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewStatusTotalRow',
|
||||
);
|
||||
|
||||
for my $StateOrderID ( sort { $a <=> $b } keys %ConfiguredStates ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewStatusTotal',
|
||||
Data => {
|
||||
Number => $StatusTotal[$StateOrderID],
|
||||
QueueIDs => $QueueIDURL,
|
||||
StateID => $States{ $ConfiguredStates{$StateOrderID} },
|
||||
Sort => $Sort,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentLargeTicketQueueOverviewNone',
|
||||
Data => {
|
||||
ColumnCount => ( scalar keys %ConfiguredStates ) + 2,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
# check for refresh time
|
||||
my $Refresh = '';
|
||||
if ( $Self->{UserRefreshTime} ) {
|
||||
$Refresh = 60 * $Self->{UserRefreshTime};
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'QueueOverview',
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
$Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardTicketQueueOverview',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
# cache result
|
||||
if ( $Self->{Config}->{CacheTTLLocal} ) {
|
||||
$CacheObject->Set(
|
||||
Type => 'DashboardQueueOverview',
|
||||
Key => $CacheKey,
|
||||
Value => $Content || '',
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
}
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
223
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketStatsGeneric.pm
Normal file
223
Perl OTRS/Kernel/Output/HTML/Dashboard/TicketStatsGeneric.pm
Normal file
@@ -0,0 +1,223 @@
|
||||
# --
|
||||
# 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::TicketStatsGeneric;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Kernel::System::DateTime qw(:all);
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# Don't cache this globally as it contains JS that is not inside of the HTML.
|
||||
CacheTTL => undef,
|
||||
CacheKey => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
my $Key = $LayoutObject->{UserLanguage} . '-' . $Self->{Name};
|
||||
my $CacheKey = 'TicketStats' . '-' . $Self->{UserID} . '-' . $Key;
|
||||
|
||||
my $Cache = $Kernel::OM->Get('Kernel::System::Cache')->Get(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKey,
|
||||
);
|
||||
|
||||
if ( ref $Cache ) {
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'DashboardTicketStats',
|
||||
Value => $Cache,
|
||||
);
|
||||
|
||||
return $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardTicketStats',
|
||||
Data => $Cache,
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
}
|
||||
|
||||
my %Axis = (
|
||||
'7Day' => {
|
||||
0 => 'Sun',
|
||||
1 => 'Mon',
|
||||
2 => 'Tue',
|
||||
3 => 'Wed',
|
||||
4 => 'Thu',
|
||||
5 => 'Fri',
|
||||
6 => 'Sat',
|
||||
},
|
||||
);
|
||||
|
||||
my $ClosedText = $LayoutObject->{LanguageObject}->Translate('Closed');
|
||||
my $CreatedText = $LayoutObject->{LanguageObject}->Translate('Created');
|
||||
my $StateText = $LayoutObject->{LanguageObject}->Translate('State');
|
||||
my @TicketsCreated = ();
|
||||
my @TicketsClosed = ();
|
||||
my @TicketWeekdays = ();
|
||||
my $Max = 0;
|
||||
|
||||
my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
|
||||
|
||||
my $TimeZone = $Self->{UserTimeZone} || OTRSTimeZoneGet();
|
||||
|
||||
for my $DaysBack ( 0 .. 6 ) {
|
||||
|
||||
# cache results for 30 min. for todays stats
|
||||
my $CacheTTL = 60 * 30;
|
||||
|
||||
my $DateTimeObject = $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
TimeZone => $TimeZone,
|
||||
}
|
||||
);
|
||||
if ($DaysBack) {
|
||||
$DateTimeObject->Subtract( Days => $DaysBack );
|
||||
|
||||
# for past 6 days cache results for 8 days (should not change)
|
||||
$CacheTTL = 60 * 60 * 24 * 8;
|
||||
}
|
||||
$DateTimeObject->ToOTRSTimeZone();
|
||||
|
||||
my $DateTimeValues = $DateTimeObject->Get();
|
||||
my $WeekDay = $DateTimeValues->{DayOfWeek} == 7 ? 0 : $DateTimeValues->{DayOfWeek};
|
||||
|
||||
unshift(
|
||||
@TicketWeekdays,
|
||||
$LayoutObject->{LanguageObject}->Translate( $Axis{'7Day'}->{$WeekDay} )
|
||||
);
|
||||
|
||||
my $TimeStart = $DateTimeObject->Format( Format => '%Y-%m-%d 00:00:00' );
|
||||
my $TimeStop = $DateTimeObject->Format( Format => '%Y-%m-%d 23:59:59' );
|
||||
|
||||
my $CountCreated = $TicketObject->TicketSearch(
|
||||
|
||||
# cache search result
|
||||
CacheTTL => $CacheTTL,
|
||||
|
||||
# tickets with create time after ... (ticket newer than this date) (optional)
|
||||
TicketCreateTimeNewerDate => $TimeStart,
|
||||
|
||||
# tickets with created time before ... (ticket older than this date) (optional)
|
||||
TicketCreateTimeOlderDate => $TimeStop,
|
||||
|
||||
CustomerID => $Param{Data}->{UserCustomerID},
|
||||
Result => 'COUNT',
|
||||
|
||||
# search with user permissions
|
||||
Permission => $Self->{Config}->{Permission} || 'ro',
|
||||
UserID => $Self->{UserID},
|
||||
) || 0;
|
||||
if ( $CountCreated && $CountCreated > $Max ) {
|
||||
$Max = $CountCreated;
|
||||
}
|
||||
push @TicketsCreated, $CountCreated;
|
||||
|
||||
my $CountClosed = $TicketObject->TicketSearch(
|
||||
|
||||
# cache search result
|
||||
CacheTTL => $CacheTTL,
|
||||
|
||||
# tickets with create time after ... (ticket newer than this date) (optional)
|
||||
TicketCloseTimeNewerDate => $TimeStart,
|
||||
|
||||
# tickets with created time before ... (ticket older than this date) (optional)
|
||||
TicketCloseTimeOlderDate => $TimeStop,
|
||||
|
||||
CustomerID => $Param{Data}->{UserCustomerID},
|
||||
Result => 'COUNT',
|
||||
|
||||
# search with user permissions
|
||||
Permission => $Self->{Config}->{Permission} || 'ro',
|
||||
UserID => $Self->{UserID},
|
||||
) || 0;
|
||||
if ( $CountClosed && $CountClosed > $Max ) {
|
||||
$Max = $CountClosed;
|
||||
}
|
||||
push @TicketsClosed, $CountClosed;
|
||||
}
|
||||
|
||||
unshift(
|
||||
@TicketWeekdays,
|
||||
$StateText
|
||||
);
|
||||
|
||||
my @ChartData = (
|
||||
$LayoutObject->{LanguageObject}->Translate('7 Day Stats'),
|
||||
\@TicketWeekdays,
|
||||
[ $CreatedText, reverse @TicketsCreated ],
|
||||
[ $ClosedText, reverse @TicketsClosed ],
|
||||
);
|
||||
|
||||
my %Data = (
|
||||
%{ $Self->{Config} },
|
||||
Key => int rand 99999,
|
||||
ChartData => \@ChartData,
|
||||
);
|
||||
|
||||
if ( $Self->{Config}->{CacheTTLLocal} ) {
|
||||
$Kernel::OM->Get('Kernel::System::Cache')->Set(
|
||||
Type => 'Dashboard',
|
||||
Key => $CacheKey,
|
||||
Value => \%Data,
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
}
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'DashboardTicketStats',
|
||||
Value => \%Data
|
||||
);
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardTicketStats',
|
||||
Data => \%Data,
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
487
Perl OTRS/Kernel/Output/HTML/Dashboard/UserOnline.pm
Normal file
487
Perl OTRS/Kernel/Output/HTML/Dashboard/UserOnline.pm
Normal file
@@ -0,0 +1,487 @@
|
||||
# --
|
||||
# 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::UserOnline;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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');
|
||||
|
||||
# get current filter
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'UserDashboardUserOnlineFilter' . $Self->{Name};
|
||||
if ( $Self->{Name} eq $Name ) {
|
||||
$Self->{Filter} = $ParamObject->GetParam( Param => 'Filter' ) || '';
|
||||
}
|
||||
|
||||
# get config object
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
|
||||
# remember filter
|
||||
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') ) {
|
||||
$Kernel::OM->Get('Kernel::System::User')->SetPreferences(
|
||||
UserID => $Self->{UserID},
|
||||
Key => $PreferencesKey,
|
||||
Value => $Self->{Filter},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !$Self->{Filter} ) {
|
||||
$Self->{Filter} = $Self->{$PreferencesKey} || $Self->{Config}->{Filter} || 'Agent';
|
||||
}
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKey} }
|
||||
|| $Self->{Config}->{Limit} || 10;
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name} . '::' . $Self->{Filter};
|
||||
|
||||
# get configuration for the full name order for user names
|
||||
# and append it to the cache key to make sure, that the
|
||||
# correct data will be displayed every time
|
||||
my $FirstnameLastNameOrder = $ConfigObject->Get('FirstnameLastnameOrder') || 0;
|
||||
$Self->{CacheKey} .= '::' . $FirstnameLastNameOrder;
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown'),
|
||||
Name => $Self->{PrefKey},
|
||||
Block => 'Option',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
CanRefresh => 1,
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
|
||||
my $SessionObject = $Kernel::OM->Get('Kernel::System::AuthSession');
|
||||
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
||||
|
||||
# Get the session max idle time in seconds.
|
||||
my $SessionMaxIdleTime = $ConfigObject->Get('SessionMaxIdleTime') || 0;
|
||||
|
||||
my $SortBy = $Self->{Config}->{SortBy} || 'UserFullname';
|
||||
|
||||
my $Online = {
|
||||
User => {
|
||||
Agent => {},
|
||||
Customer => {},
|
||||
},
|
||||
UserCount => {
|
||||
Agent => 0,
|
||||
Customer => 0,
|
||||
},
|
||||
UserData => {
|
||||
Agent => {},
|
||||
Customer => {},
|
||||
},
|
||||
};
|
||||
|
||||
# Get all session ids, to generate the logged-in user list.
|
||||
my @Sessions = $SessionObject->GetAllSessionIDs();
|
||||
|
||||
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
my $SystemTime = $CurSystemDateTimeObject->ToEpoch();
|
||||
|
||||
SESSIONID:
|
||||
for my $SessionID (@Sessions) {
|
||||
|
||||
next SESSIONID if !$SessionID;
|
||||
|
||||
# get session data
|
||||
my %Data = $SessionObject->GetSessionIDData( SessionID => $SessionID );
|
||||
|
||||
next SESSIONID if !%Data;
|
||||
next SESSIONID if !$Data{UserID};
|
||||
|
||||
# use agent instead of user
|
||||
my %AgentData;
|
||||
if ( $Data{UserType} eq 'User' ) {
|
||||
$Data{UserType} = 'Agent';
|
||||
|
||||
# get user data
|
||||
%AgentData = $UserObject->GetUserData(
|
||||
UserID => $Data{UserID},
|
||||
NoOutOfOffice => 1,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$Data{UserFullname} ||= $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerName(
|
||||
UserLogin => $Data{UserLogin},
|
||||
);
|
||||
}
|
||||
|
||||
# Skip session, if no last request exists.
|
||||
next SESSIONID if !$Data{UserLastRequest};
|
||||
|
||||
# Check the last request / idle time, only if the user is not already shown.
|
||||
if ( !$Online->{User}->{ $Data{UserType} }->{ $Data{UserID} } ) {
|
||||
next SESSIONID if $Data{UserLastRequest} + $SessionMaxIdleTime < $SystemTime;
|
||||
|
||||
# Count only unique agents and customers, please see bug#13429 for more information.
|
||||
$Online->{UserCount}->{ $Data{UserType} }++;
|
||||
}
|
||||
|
||||
# Remember the user data, if the user not already exists in the online list or the last request time is newer.
|
||||
if (
|
||||
!$Online->{User}->{ $Data{UserType} }->{ $Data{UserID} }
|
||||
|| $Online->{UserData}->{ $Data{UserType} }->{ $Data{UserID} }->{UserLastRequest} < $Data{UserLastRequest}
|
||||
)
|
||||
{
|
||||
$Online->{User}->{ $Data{UserType} }->{ $Data{UserID} } = $Data{$SortBy};
|
||||
$Online->{UserData}->{ $Data{UserType} }->{ $Data{UserID} } = { %Data, %AgentData };
|
||||
}
|
||||
}
|
||||
|
||||
# Set the selected css class for the current filter ('Agents' or 'Customers').
|
||||
my %Summary;
|
||||
$Summary{ $Self->{Filter} . '::Selected' } = 'Selected';
|
||||
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
# Generate the output block for the filter bar.
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOnlineFilter',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%{ $Online->{UserCount} },
|
||||
%Summary,
|
||||
},
|
||||
);
|
||||
|
||||
# Add the page nav bar block to the output.
|
||||
my $Total = $Online->{UserCount}->{ $Self->{Filter} } || 0;
|
||||
my $LinkPage = 'Subaction=Element;Name=' . $Self->{Name} . ';Filter=' . $Self->{Filter} . ';';
|
||||
my %PageNav = $LayoutObject->PageNavBar(
|
||||
StartHit => $Self->{StartHit},
|
||||
PageShown => $Self->{PageShown},
|
||||
AllHits => $Total || 1,
|
||||
Action => 'Action=' . $LayoutObject->{Action},
|
||||
Link => $LinkPage,
|
||||
WindowSize => 5,
|
||||
AJAXReplace => 'Dashboard' . $Self->{Name},
|
||||
IDPrefix => 'Dashboard' . $Self->{Name},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallTicketGenericFilterNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
|
||||
# Show agent and ustomer user in the widget.
|
||||
my %OnlineUser = %{ $Online->{User}->{ $Self->{Filter} } };
|
||||
my %OnlineData = %{ $Online->{UserData}->{ $Self->{Filter} } };
|
||||
my $Count = 0;
|
||||
my $Limit = $LayoutObject->{ $Self->{PrefKey} } || $Self->{Config}->{Limit};
|
||||
|
||||
# Check if agent has permission to start chats with the listed users
|
||||
my $EnableChat = 1;
|
||||
my $ChatStartingAgentsGroup = $ConfigObject->Get('ChatEngine::PermissionGroup::ChatStartingAgents') || 'users';
|
||||
my $ChatReceivingAgentsGroup = $ConfigObject->Get('ChatEngine::PermissionGroup::ChatReceivingAgents') || 'users';
|
||||
|
||||
my $ChatStartingAgentsGroupPermission = $Kernel::OM->Get('Kernel::System::Group')->PermissionCheck(
|
||||
UserID => $Self->{UserID},
|
||||
GroupName => $ChatStartingAgentsGroup,
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
if ( !$ConfigObject->Get('ChatEngine::Active') || !$ChatStartingAgentsGroupPermission ) {
|
||||
$EnableChat = 0;
|
||||
}
|
||||
if ( $EnableChat && $Self->{Filter} eq 'Agent' && !$ConfigObject->Get('ChatEngine::ChatDirection::AgentToAgent') ) {
|
||||
$EnableChat = 0;
|
||||
}
|
||||
if (
|
||||
$EnableChat
|
||||
&& $Self->{Filter} eq 'Customer'
|
||||
&& !$ConfigObject->Get('ChatEngine::ChatDirection::AgentToCustomer')
|
||||
)
|
||||
{
|
||||
$EnableChat = 0;
|
||||
}
|
||||
|
||||
my $VideoChatEnabled = 0;
|
||||
my $VideoChatAgentsGroup = $ConfigObject->Get('ChatEngine::PermissionGroup::VideoChatAgents') || 'users';
|
||||
my $VideoChatAgentsGroupPermission = $Kernel::OM->Get('Kernel::System::Group')->PermissionCheck(
|
||||
UserID => $Self->{UserID},
|
||||
GroupName => $VideoChatAgentsGroup,
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
# Enable the video chat feature if system is entitled and agent is a member of configured group.
|
||||
if ( $ConfigObject->Get('ChatEngine::Active') && $VideoChatAgentsGroupPermission ) {
|
||||
if ( $Kernel::OM->Get('Kernel::System::Main')->Require( 'Kernel::System::VideoChat', Silent => 1 ) ) {
|
||||
$VideoChatEnabled = $Kernel::OM->Get('Kernel::System::VideoChat')->IsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
# Online thresholds for agents and customers (default 5 min).
|
||||
my $OnlineThreshold = (
|
||||
$Self->{Filter} eq 'Agent'
|
||||
? $Kernel::OM->Get('Kernel::Config')->Get('SessionAgentOnlineThreshold')
|
||||
: $Kernel::OM->Get('Kernel::Config')->Get('SessionCustomerOnlineThreshold')
|
||||
)
|
||||
|| 5;
|
||||
|
||||
# Translate the diffrent user state descriptions.
|
||||
my $UserOfflineDescription = $LayoutObject->{LanguageObject}->Translate('User is currently offline.');
|
||||
my $UserActiveDescription = $LayoutObject->{LanguageObject}->Translate('User is currently active.');
|
||||
my $UserAwayDescription = $LayoutObject->{LanguageObject}->Translate('User was inactive for a while.');
|
||||
my $UserUnavailableDescription
|
||||
= $LayoutObject->{LanguageObject}->Translate('User set their status to unavailable.');
|
||||
|
||||
USERID:
|
||||
for my $UserID ( sort { $OnlineUser{$a} cmp $OnlineUser{$b} } keys %OnlineUser ) {
|
||||
|
||||
$Count++;
|
||||
|
||||
next USERID if !$UserID;
|
||||
next USERID if $Count < $Self->{StartHit};
|
||||
last USERID if $Count >= ( $Self->{StartHit} + $Self->{PageShown} );
|
||||
|
||||
my $UserData = $OnlineData{$UserID};
|
||||
my $AgentEnableChat = 0;
|
||||
my $CustomerEnableChat = 0;
|
||||
my $ChatAccess = 0;
|
||||
my $VideoChatAvailable = 0;
|
||||
my $VideoChatSupport = 0;
|
||||
|
||||
# Set Default status to active, because a offline user is not visible in the list.
|
||||
my $UserState = Translatable('Active');
|
||||
my $UserStateDescription = $UserActiveDescription;
|
||||
|
||||
# We also need to check if the receiving agent has chat permissions.
|
||||
if ( $EnableChat && $Self->{Filter} eq 'Agent' ) {
|
||||
|
||||
my %UserGroups = $Kernel::OM->Get('Kernel::System::Group')->PermissionUserGet(
|
||||
UserID => $UserData->{UserID},
|
||||
Type => 'rw',
|
||||
);
|
||||
|
||||
my %UserGroupsReverse = reverse %UserGroups;
|
||||
$ChatAccess = $UserGroupsReverse{$ChatReceivingAgentsGroup} ? 1 : 0;
|
||||
|
||||
my %User = $Kernel::OM->Get('Kernel::System::User')->GetUserData(
|
||||
UserID => $UserID,
|
||||
);
|
||||
$VideoChatSupport = $User{VideoChatHasWebRTC};
|
||||
|
||||
# Check agents availability.
|
||||
if ($ChatAccess) {
|
||||
my $AgentChatAvailability = $Kernel::OM->Get('Kernel::System::Chat')->AgentAvailabilityGet(
|
||||
UserID => $UserID,
|
||||
External => 0,
|
||||
);
|
||||
|
||||
if ( $AgentChatAvailability == 3 ) {
|
||||
$UserState = Translatable('Active');
|
||||
$AgentEnableChat = 1;
|
||||
$UserStateDescription = $UserActiveDescription;
|
||||
$VideoChatAvailable = 1;
|
||||
}
|
||||
elsif ( $AgentChatAvailability == 2 ) {
|
||||
$UserState = Translatable('Away');
|
||||
$AgentEnableChat = 1;
|
||||
$UserStateDescription = $UserAwayDescription;
|
||||
}
|
||||
elsif ( $AgentChatAvailability == 1 ) {
|
||||
$UserState = Translatable('Unavailable');
|
||||
$UserStateDescription = $UserUnavailableDescription;
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($EnableChat) {
|
||||
$ChatAccess = 1;
|
||||
|
||||
my $CustomerChatAvailability = $Kernel::OM->Get('Kernel::System::Chat')->CustomerAvailabilityGet(
|
||||
UserID => $UserData->{UserID},
|
||||
);
|
||||
|
||||
my %CustomerUser = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
|
||||
User => $UserID,
|
||||
);
|
||||
$VideoChatSupport = 1 if $CustomerUser{VideoChatHasWebRTC};
|
||||
|
||||
if ( $CustomerChatAvailability == 3 ) {
|
||||
$UserState = Translatable('Active');
|
||||
$CustomerEnableChat = 1;
|
||||
$UserStateDescription = $UserActiveDescription;
|
||||
$VideoChatAvailable = 1;
|
||||
}
|
||||
elsif ( $CustomerChatAvailability == 2 ) {
|
||||
$UserState = Translatable('Away');
|
||||
$CustomerEnableChat = 1;
|
||||
$UserStateDescription = $UserAwayDescription;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $UserData->{UserLastRequest} + ( 60 * $OnlineThreshold ) < $SystemTime ) {
|
||||
$UserState = Translatable('Away');
|
||||
$UserStateDescription = $UserAwayDescription;
|
||||
}
|
||||
}
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOnlineRow',
|
||||
Data => {
|
||||
%{$UserData},
|
||||
ChatAccess => $ChatAccess,
|
||||
AgentEnableChat => $AgentEnableChat,
|
||||
CustomerEnableChat => $CustomerEnableChat,
|
||||
UserState => $UserState,
|
||||
UserStateDescription => $UserStateDescription,
|
||||
VideoChatEnabled => $VideoChatEnabled,
|
||||
VideoChatAvailable => $VideoChatAvailable,
|
||||
VideoChatSupport => $VideoChatSupport,
|
||||
},
|
||||
);
|
||||
|
||||
if ( $Self->{Config}->{ShowEmail} ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOnlineRowEmail',
|
||||
Data => $UserData,
|
||||
);
|
||||
}
|
||||
|
||||
next USERID if !$UserData->{OutOfOffice};
|
||||
|
||||
my $CreateOutOfOfficeDTObject = sub {
|
||||
my $Type = shift;
|
||||
|
||||
my $DTString = sprintf(
|
||||
'%04d-%02d-%02d ' . ( $Type eq 'End' ? '23:59:59' : '00:00:00' ),
|
||||
$UserData->{"OutOfOffice${Type}Year"},
|
||||
$UserData->{"OutOfOffice${Type}Month"},
|
||||
$UserData->{"OutOfOffice${Type}Day"}
|
||||
);
|
||||
|
||||
return $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $DTString,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
my $OOOStartDTObject = $CreateOutOfOfficeDTObject->('Start');
|
||||
my $OOOEndDTObject = $CreateOutOfOfficeDTObject->('End');
|
||||
|
||||
next USERID if $OOOStartDTObject > $CurSystemDateTimeObject || $OOOEndDTObject < $CurSystemDateTimeObject;
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOnlineRowOutOfOffice',
|
||||
);
|
||||
}
|
||||
|
||||
if ( !%OnlineUser ) {
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOnlineNone',
|
||||
);
|
||||
}
|
||||
|
||||
# check for refresh time
|
||||
my $Refresh = 30; # 30 seconds
|
||||
my $NameHTML = $Self->{Name};
|
||||
$NameHTML =~ s{-}{_}xmsg;
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardUserOnline',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh,
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
# send data to JS
|
||||
$LayoutObject->AddJSData(
|
||||
Key => 'UserOnline',
|
||||
Value => {
|
||||
Name => $Self->{Name},
|
||||
NameHTML => $NameHTML,
|
||||
RefreshTime => $Refresh,
|
||||
},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
248
Perl OTRS/Kernel/Output/HTML/Dashboard/UserOutOfOffice.pm
Normal file
248
Perl OTRS/Kernel/Output/HTML/Dashboard/UserOutOfOffice.pm
Normal file
@@ -0,0 +1,248 @@
|
||||
# --
|
||||
# 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::UserOutOfOffice;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
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');
|
||||
|
||||
# get current start-hit and preferences
|
||||
my $Name = $ParamObject->GetParam( Param => 'Name' ) || '';
|
||||
my $PreferencesKey = 'UserDashboardUserOutOfOffice' . $Self->{Name};
|
||||
|
||||
$Self->{PrefKey} = 'UserDashboardPref' . $Self->{Name} . '-Shown';
|
||||
|
||||
$Self->{PageShown} = $Kernel::OM->Get('Kernel::Output::HTML::Layout')->{ $Self->{PrefKey} }
|
||||
|| $Self->{Config}->{Limit} || 10;
|
||||
|
||||
$Self->{StartHit} = int( $ParamObject->GetParam( Param => 'StartHit' ) || 1 );
|
||||
|
||||
$Self->{CacheKey} = $Self->{Name};
|
||||
|
||||
return $Self;
|
||||
}
|
||||
|
||||
sub Preferences {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
my @Params = (
|
||||
{
|
||||
Desc => Translatable('Shown'),
|
||||
Name => $Self->{PrefKey},
|
||||
Block => 'Option',
|
||||
Data => {
|
||||
5 => ' 5',
|
||||
10 => '10',
|
||||
15 => '15',
|
||||
20 => '20',
|
||||
25 => '25',
|
||||
30 => '30',
|
||||
35 => '35',
|
||||
40 => '40',
|
||||
45 => '45',
|
||||
50 => '50',
|
||||
},
|
||||
SelectedID => $Self->{PageShown},
|
||||
Translation => 0,
|
||||
},
|
||||
);
|
||||
|
||||
return @Params;
|
||||
}
|
||||
|
||||
sub Config {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
return (
|
||||
%{ $Self->{Config} },
|
||||
|
||||
# remember, do not allow to use page cache
|
||||
# (it's not working because of internal filter)
|
||||
CacheKey => undef,
|
||||
CacheTTL => undef,
|
||||
);
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my ( $Self, %Param ) = @_;
|
||||
|
||||
# get config settings
|
||||
my $SortBy = $Self->{Config}->{SortBy} || 'UserFullname';
|
||||
|
||||
# get cache object
|
||||
my $CacheObject = $Kernel::OM->Get('Kernel::System::Cache');
|
||||
|
||||
# check cache
|
||||
my $OutOfOffice = $CacheObject->Get(
|
||||
Type => 'Dashboard',
|
||||
Key => $Self->{CacheKey},
|
||||
);
|
||||
|
||||
# get session info
|
||||
my $CacheUsed = 1;
|
||||
if ( !$OutOfOffice ) {
|
||||
|
||||
$CacheUsed = 0;
|
||||
$OutOfOffice = {
|
||||
User => {},
|
||||
UserCount => 0,
|
||||
UserData => {},
|
||||
};
|
||||
|
||||
# get user object
|
||||
my $UserObject = $Kernel::OM->Get('Kernel::System::User');
|
||||
|
||||
my %UserList = $UserObject->SearchPreferences(
|
||||
Key => 'OutOfOffice',
|
||||
Value => 1,
|
||||
);
|
||||
|
||||
USERID:
|
||||
for my $UserID ( sort keys %UserList ) {
|
||||
|
||||
next USERID if !$UserID;
|
||||
|
||||
# get user data
|
||||
my %Data = $UserObject->GetUserData(
|
||||
UserID => $UserID,
|
||||
NoOutOfOffice => 1,
|
||||
Valid => 1,
|
||||
);
|
||||
|
||||
next USERID if !%Data;
|
||||
|
||||
my $CurSystemDateTimeObject = $Kernel::OM->Create('Kernel::System::DateTime');
|
||||
my $CreateOutOfOfficeDTObject = sub {
|
||||
my $Type = shift;
|
||||
|
||||
my $DTString = sprintf(
|
||||
'%04d-%02d-%02d ' . ( $Type eq 'End' ? '23:59:59' : '00:00:00' ),
|
||||
$Data{"OutOfOffice${Type}Year"}, $Data{"OutOfOffice${Type}Month"},
|
||||
$Data{"OutOfOffice${Type}Day"}
|
||||
);
|
||||
|
||||
return $Kernel::OM->Create(
|
||||
'Kernel::System::DateTime',
|
||||
ObjectParams => {
|
||||
String => $DTString,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
my $OOOStartDTObject = $CreateOutOfOfficeDTObject->('Start');
|
||||
my $OOOEndDTObject = $CreateOutOfOfficeDTObject->('End');
|
||||
|
||||
next USERID if $OOOStartDTObject > $CurSystemDateTimeObject
|
||||
|| $OOOEndDTObject < $CurSystemDateTimeObject;
|
||||
|
||||
$Data{OutOfOfficeUntil} = $OOOEndDTObject->ToString();
|
||||
|
||||
# remember user and data
|
||||
$OutOfOffice->{User}->{ $Data{UserID} } = $Data{$SortBy};
|
||||
$OutOfOffice->{UserCount}++;
|
||||
$OutOfOffice->{UserData}->{ $Data{UserID} } = \%Data;
|
||||
}
|
||||
}
|
||||
|
||||
# do not show widget if there are no users out-of-office
|
||||
return if !$OutOfOffice->{UserCount};
|
||||
|
||||
# set cache
|
||||
if ( !$CacheUsed && $Self->{Config}->{CacheTTLLocal} ) {
|
||||
$CacheObject->Set(
|
||||
Type => 'Dashboard',
|
||||
Key => $Self->{CacheKey},
|
||||
Value => $OutOfOffice,
|
||||
TTL => $Self->{Config}->{CacheTTLLocal} * 60,
|
||||
);
|
||||
}
|
||||
|
||||
# get layout object
|
||||
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
|
||||
|
||||
# add page nav bar if needed
|
||||
my $Total = $OutOfOffice->{UserCount} || 0;
|
||||
if ( $OutOfOffice->{UserCount} > $Self->{PageShown} ) {
|
||||
|
||||
my $LinkPage = 'Subaction=Element;Name=' . $Self->{Name} . ';';
|
||||
my %PageNav = $LayoutObject->PageNavBar(
|
||||
StartHit => $Self->{StartHit},
|
||||
PageShown => $Self->{PageShown},
|
||||
AllHits => $Total || 1,
|
||||
Action => 'Action=' . $LayoutObject->{Action},
|
||||
Link => $LinkPage,
|
||||
WindowSize => 5,
|
||||
AJAXReplace => 'Dashboard' . $Self->{Name},
|
||||
IDPrefix => 'Dashboard' . $Self->{Name},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallTicketGenericNavBar',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
%PageNav,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
# show data
|
||||
my %OutOfOfficeUser = %{ $OutOfOffice->{User} };
|
||||
my %OutOfOfficeData = %{ $OutOfOffice->{UserData} };
|
||||
my $Count = 0;
|
||||
my $Limit = $LayoutObject->{ $Self->{PrefKey} } || $Self->{Config}->{Limit};
|
||||
|
||||
USERID:
|
||||
for my $UserID ( sort { $OutOfOfficeUser{$a} cmp $OutOfOfficeUser{$b} } keys %OutOfOfficeUser )
|
||||
{
|
||||
|
||||
$Count++;
|
||||
|
||||
next USERID if !$UserID;
|
||||
next USERID if $Count < $Self->{StartHit};
|
||||
last USERID if $Count >= ( $Self->{StartHit} + $Self->{PageShown} );
|
||||
|
||||
$LayoutObject->Block(
|
||||
Name => 'ContentSmallUserOutOfOfficeRow',
|
||||
Data => $OutOfOfficeData{$UserID},
|
||||
);
|
||||
}
|
||||
|
||||
my $Content = $LayoutObject->Output(
|
||||
TemplateFile => 'AgentDashboardUserOutOfOffice',
|
||||
Data => {
|
||||
%{ $Self->{Config} },
|
||||
Name => $Self->{Name},
|
||||
},
|
||||
AJAX => $Param{AJAX},
|
||||
);
|
||||
|
||||
return $Content;
|
||||
}
|
||||
|
||||
1;
|
||||
Reference in New Issue
Block a user