package WebGUI::Macro::UsersOnline; =head1 LEGAL ------------------------------------------------------------------- WebGUI is Copyright 2001-2009 Plain Black LLC. ------------------------------------------------------------------- Please read the legal notices (docs/legal.txt) and the license (docs/license.txt) that came with this distribution before using this software. ------------------------------------------------------------------- http://www.plainblack.com info@plainblack.com ------------------------------------------------------------------- Authors of Macro: Carlos Rivero (http://www.carlosrivero.com), Bernd Kalbfuss-Zimmermann ------------------------------------------------------------------- =cut use strict; use Apache2::ServerRec; use Net::DNS; use WebGUI::Asset::Template; use WebGUI::International; use WebGUI::Session::DateTime; use WebGUI::SQL; use WebGUI::User; =head1 NAME Package WebGUI::Macro::UsersOnline; =head1 DESCRIPTION Macro for displaying users that are online. =cut #------------------------------------------------------------------- =head2 process ( [templateId], [minutes], [maxMembers], [maxVisitors] ) Returns a fragment of HTML code showing the number of users online. Appearance can be altered by specifying an alternative template. =head3 templateId The ID of the template to be processed. The default is 'h_T2xtOxGRQ9QJOR6ebLpQ'. =head3 minutes The number of minutes the last activity may date back for a user to count as online.cThe default is 5 minutes. =head3 maxMembers The maximum number of members to be returned in the members template loop. The default is 10. =head3 maxVisitors The maximum number of visitors to be returned in the visitors template loop. The default is 10. =cut sub process { # Assign parameters my $session = shift; my $templateId = shift; my $minutes = shift; my $maxMembers = shift; my $maxVisitors = shift; # Assign default values $templateId ||= "h_T2xtOxGRQ9QJOR6ebLpQ"; $minutes = 5 unless defined $minutes; $maxMembers = 10 unless defined $maxMembers; $maxVisitors = 10 unless defined $maxVisitors; # Create hash of template variables my %var; # Obtain internationalization instance my $i18n = WebGUI::International->new($session, "Macro_UsersOnline"); # Get preferred time format of current user my $time_format = $session->user->profileField("timeFormat"); # Calculate epoch time for comparison to last activity my $dt = $session->datetime; my $epoch = $dt->time(); $epoch = $session->datetime->addToTime($epoch, 0, -$minutes, 0); # Let private subroutines do the work _visitors($session, \%var, $epoch, $maxVisitors); _members($session, \%var, $epoch, $maxMembers); # Calculate the total number of active users $var{'total'} = $var{'members'} + $var{'visitors'}; # Set some flags $var{'isVisitor'} = 1 if ($session->user->isVisitor); $var{'hasMembers'} = 1 if $var{'member_loop'}; # Assign labels $var{'usersOnline_label'} = $i18n->get("Users Online"); $var{'members_label'} = $i18n->get("Members"); $var{'visitors_label'} = $i18n->get("Visitors"); $var{'total_label'} = $i18n->get("Total"); $var{'membersOnline_label'} = $i18n->get("Members Online"); $var{'visitorsOnline_label'} = $i18n->get("Visitors Online"); $var{'avatar_label'} = $i18n->get("Avatar"); $var{'name_label'} = $i18n->get("Name"); $var{'alias_label'} = $i18n->get("Alias"); $var{'session_label'} = $i18n->get("Session"); $var{'ip_label'} = $i18n->get("IP"); $var{'lastActivity_label'} = $i18n->get("Last Activity"); # Process Template return WebGUI::Asset::Template->new($session,$templateId)->process(\%var); } #------------------------------------------------------------------- =head2 _visitors ( session, varRef, epoch, maxVisitors ) Fills template variables concerning visitors, i.e. the number of visitors and all variables in the visitors loop. Private subroutine used by process(). =head3 session A reference to the current session. =head3 varRef A reference to the hash of template variables. =head3 epoch Epoch time for comparison to last activity. =head3 maxVisitors The maximum number of visitors to be returned in the visitors template loop. =cut sub _visitors { # Assign parameters my $session = shift; my $var = shift; my $epoch = shift; my $maxVisitors = shift; # Obtain some session-associated objects my $db = $session->db(); my $dt = $session->datetime; # Get preferred time format of current user my $time_format = $session->user->profileField("timeFormat"); # SQL conditional clause for filtering server IP my $ip_clause; # Check whether instance of Apache2::ServerRec is available if($session->server) { # Query hostname of server my $hostname = $session->server->server_hostname(); # Look up server IP addresses my $res = Net::DNS::Resolver->new(); my $query = $res->search($hostname); if ($query) { foreach my $rr ($query->answer) { next unless $rr->type eq "A"; # Generate SQL clause which excludes server IPs $ip_clause = $ip_clause . "AND STRCMP(lastIp, '" . $rr->address . "') "; } } } # Determine the number of visitors. Server IP adresses and the loopback # network are excluded. We only count IPs - not sessions. The reason is # that crawlers tend to open multiple sessions(e.g. googlebot) and thereby # increase the count artificially. Note, that the number determined here # may deviate from the number of items returned in the visitor loop. $var->{'visitors'} = $db->quickScalar("SELECT COUNT(DISTINCT lastIp) FROM " . "userSession WHERE (lastPageView > ?) AND (userId = 1) AND " . "lastIp NOT LIKE '127.%.%.%'" . $ip_clause, [$epoch]); # Query session IDs and IPs of visitors my $query = $db->read("SELECT sessionId, lastIp, lastPageView FROM " . "userSession WHERE (lastPageView > ?) AND (userId = 1) AND " . "lastIp NOT LIKE '127.%.%.%' " . $ip_clause . "LIMIT ?", [$epoch, $maxVisitors]); # Iterate through rows while (my %row = $query->hash) { # Add item to visitor template loop push(@{$var->{'visitor_loop'}}, { sessionId => $row{'sessionId'}, ip => $row{'lastIp'}, lastActivity => $dt->epochToHuman($row{'lastPageView'}, $time_format) }); } return; } #------------------------------------------------------------------- =head2 _members ( session, varRef, epoch, maxMembers ) Fills template variables concerning members, i.e. the number of members and all variables in the members loop. Private subroutine used by process(). =head3 session A reference to the current session. =head3 varRef A reference to the hash of template variables. =head3 epoch Epoch time for comparison to last activity. =head3 maxMembers The maximum number of members to be returned in the members template loop. =cut sub _members { # Assign parameters my $session = shift; my $var = shift; my $epoch = shift; my $maxMembers = shift; # Obtain some session-associated objects my $db = $session->db(); my $dt = $session->datetime; # Get preferred time format of current user my $time_format = $session->user->profileField("timeFormat"); # Determine the number of registered users that are online. The Admin # account is excluded from the list. $var->{'members'} = $db->quickScalar("SELECT COUNT(DISTINCT userId) FROM " . "userSession where (lastPageView > ?) and (userId != '1') and " . "(userId != '3')", [$epoch]); # Query the names of registered users that are online. The showOnline flag # in the user profile is respected. my $query = $db->read("SELECT userId, sessionId, lastIp, lastPageView " . "FROM userSession WHERE (lastPageView > ?) AND (userId != '1') " . "AND (userId != '3') LIMIT ?", [$epoch, $maxMembers]); # Iterate through rows while (my %row = $query->hash) { # Create instance of WebGUI::User for the user id from the current row my $user = WebGUI::User->new($session, $row{'userId'}); # Only show users with the "showOnline" flag set to true if ($user->profileField("showOnline")) { # Find URL of avatar if available my $avatar_url; my $avatar = $user->profileField("avatar"); if ($avatar) { my $storage = WebGUI::Storage->get($session, $avatar); my @files = @{ $storage->getFiles() }; # We assume it is the first file in the storage. But maybe # that is incorrect? $avatar_url = $storage->getUrl($files[0]); } # Add item to member template loop push(@{$var->{'member_loop'}}, { username => $user->username(), firstName => $user->profileField("firstName"), middleName => $user->profileField("middleName"), lastName => $user->profileField("lastName"), alias => $user->profileField("alias"), avatar => $avatar_url, uid => $row{'userId'}, sessionId => $row{'sessionId'}, ip => $row{'lastIp'}, lastActivity => $dt->epochToHuman($row{'lastPageView'}, $time_format) }); } } return; } 1;