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

520 lines
16 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Output::HTML::Dashboard::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;