This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View 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;

View 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;

View 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;

View File

@@ -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;

View 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;

View 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;

View File

@@ -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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

File diff suppressed because it is too large Load Diff

View 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;

View 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;

View 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;

View 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;