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