diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index bea643219..52fa91ab2 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -7,6 +7,7 @@ - Made Classname from control a subclass of ReadOnly. - Added query keys to WebGUI::Crud. - EMS Saved Address (#8864) + - Inclusion of UsersOnline macro into the core (#766) - WebGUI::Crud can now automatically resolve differences between its definition and the table schema. - Fixed a limit bug in the asset discovery service. diff --git a/docs/upgrades/packages-7.6.2/users-online-macro-templates.wgpkg b/docs/upgrades/packages-7.6.2/users-online-macro-templates.wgpkg new file mode 100644 index 000000000..5cb5e2a31 Binary files /dev/null and b/docs/upgrades/packages-7.6.2/users-online-macro-templates.wgpkg differ diff --git a/docs/upgrades/upgrade_7.6.1-7.6.2.pl b/docs/upgrades/upgrade_7.6.1-7.6.2.pl index 386b67384..b35e7bf1e 100644 --- a/docs/upgrades/upgrade_7.6.1-7.6.2.pl +++ b/docs/upgrades/upgrade_7.6.1-7.6.2.pl @@ -32,13 +32,22 @@ my $session = start(); # this line required repairManageWorkflows($session); addPreTextToThingyFields($session); updateAddressBook($session); +addUsersOnlineMacro($session); finish($session); # this line required +#---------------------------------------------------------------------------- +sub addUsersOnlineMacro { + my $session = shift; + print "\tMaking the UsersOnline macro available." unless $quiet; + $session->config->addToHash("macros","UsersOnline","UsersOnline"); + print "DONE!\n" unless $quiet; +} + #---------------------------------------------------------------------------- sub updateAddressBook { my $session = shift; - print "\tAdd Organization and Email Address to address book." unless $quiet; + print "\tAdding organization and email to address book." unless $quiet; my $db = $session->db; $db->write("alter table address add column organization char(255)"); $db->write("alter table address add column email char(255)"); @@ -58,6 +67,7 @@ sub repairManageWorkflows { print "DONE!\n" unless $quiet; } +#---------------------------------------------------------------------------- sub addPreTextToThingyFields { my $session = shift; print "\tAdding a pre-text property to Thingy fields... " unless $quiet; diff --git a/etc/WebGUI.conf.original b/etc/WebGUI.conf.original index e959452a0..e1163d4bb 100644 --- a/etc/WebGUI.conf.original +++ b/etc/WebGUI.conf.original @@ -691,6 +691,7 @@ "SpectreCheck" : "SpectreCheck", "Thumbnail" : "Thumbnail", "User" : "User", + "UsersOnline" : "UsersOnline", "u" : "u_companyUrl", "ViewCart" : "ViewCart" }, diff --git a/lib/WebGUI/Help/Macro_UsersOnline.pm b/lib/WebGUI/Help/Macro_UsersOnline.pm new file mode 100644 index 000000000..cd099c537 --- /dev/null +++ b/lib/WebGUI/Help/Macro_UsersOnline.pm @@ -0,0 +1,58 @@ +package WebGUI::Help::Macro_UsersOnline; + +our $HELP = { + + 'users online' => { + title => 'users online title', + body => 'users online body', + + variables => [ + { name => 'members' }, + { name => 'visitors' }, + { name => 'total' }, + { name => 'isVisitor' }, + { name => 'hasMembers' }, + + { name => 'member_loop', + variables => [ + { name => 'username' }, + { name => 'firstName' }, + { name => 'middleName' }, + { name => 'lastName' }, + { name => 'alias' }, + { name => 'avatar' }, + { name => 'uid' }, + { name => 'sessionId' }, + { name => 'ip' }, + { name => 'lastActivity' }, + ], + }, + + { name => "visitor_loop", + variables => [ + { name => 'sessionId' }, + { name => 'ip' }, + { name => 'lastActivity' }, + ], + }, + + { name => 'usersOnline_label' }, + { name => 'members_label' }, + { name => 'visitors_label' }, + { name => 'total_label' }, + { name => 'membersOnline_label' }, + { name => 'visitorsOnline_label' }, + { name => 'avatar_label' }, + { name => 'name_label' }, + { name => 'alias_label' }, + { name => 'session_label' }, + { name => 'ip_label' }, + { name => 'lastActivity_label' }, + ], + fields => [], + related => [], + }, + +}; + +1; diff --git a/lib/WebGUI/Macro/UsersOnline.pm b/lib/WebGUI/Macro/UsersOnline.pm new file mode 100644 index 000000000..e3b1d7cfb --- /dev/null +++ b/lib/WebGUI/Macro/UsersOnline.pm @@ -0,0 +1,299 @@ +package WebGUI::Macro::UsersOnline; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2004 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->userId == 1); + $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 > $epoch) AND (userId = 1) AND " . + "lastIp NOT LIKE '127.%.%.%'" . $ip_clause); + + # Query session IDs and IPs of visitors + my $query = $db->prepare("SELECT sessionId, lastIp, lastPageView FROM " . + "userSession WHERE (lastPageView > $epoch) AND (userId = 1) AND " . + "lastIp NOT LIKE '127.%.%.%' " . $ip_clause . "LIMIT $maxVisitors"); + $query->execute; + + # 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 > $epoch) and (userId != 1) and " . + "(userId != 3)"); + + # Query the names of registered users that are online. The showOnline flag + # in the user profile is respected. + my $query = $db->prepare("SELECT userId, sessionId, lastIp, lastPageView " . + "FROM userSession WHERE (lastPageView > $epoch) AND (userId != 1) " . + "AND (userId != 3) LIMIT $maxMembers"); + $query->execute; + + # 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; diff --git a/lib/WebGUI/i18n/English/Macro_UsersOnline.pm b/lib/WebGUI/i18n/English/Macro_UsersOnline.pm new file mode 100644 index 000000000..e4557d0e2 --- /dev/null +++ b/lib/WebGUI/i18n/English/Macro_UsersOnline.pm @@ -0,0 +1,223 @@ +package WebGUI::i18n::English::Macro_UsersOnline; +use utf8; + +our $I18N = { + + 'Users Online' => { + message => q|Users Online|, + lastUpdated => 1178783587 + }, + + 'Members' => { + message => q|Members|, + lastUpdated => 1178783587 + }, + + 'Visitors' => { + message => q|Visitors|, + lastUpdated => 1178783587 + }, + + 'Total' => { + message => q|Total|, + lastUpdated => 1178783587 + }, + + 'Members Online' => { + message => q|Members Online|, + lastUpdated => 1178783587 + }, + + 'Visitors Online' => { + message => q|Visitors Online|, + lastUpdated => 1178783587 + }, + + 'Avatar' => { + message => q|Avatar|, + lastUpdated => 1178783587 + }, + + 'Name' => { + message => q|Name|, + lastUpdated => 1178783587 + }, + + 'Alias' => { + message => q|Alias|, + lastUpdated => 1178783587 + }, + + 'Session' => { + message => q|Session|, + lastUpdated => 1178783587 + }, + + 'IP' => { + message => q|IP|, + lastUpdated => 1178783587 + }, + + 'Last Activity' => { + message => q|Last Activity|, + lastUpdated => 1178783587 + }, + + 'users online title' => { + message => q|Users Online Macro|, + lastUpdated => 1178783587 + }, + + 'users online body' => { + message => q|Macro for displaying users that are online. The number of online users, divided into members and visitors, is determined. Users are considered online if the last page request has occurred within a definable period of time. User-specific data is provided in two loops.|, + lastUpdated => 1178783587 + }, + + 'usersOnline_label' => { + message => q|The words "Users Online".|, + lastUpdated => 1178783587 + }, + + 'members_label' => { + message => q|The word "Members".|, + lastUpdated => 1178783587 + }, + + 'visitors_label' => { + message => q|The word "Visitors".|, + lastUpdated => 1178783587 + }, + + 'total_label' => { + message => q|The word "Total".|, + lastUpdated => 1178783587 + }, + + 'membersOnline_label' => { + message => q|The words "Members Online".|, + lastUpdated => 1178783587 + }, + + 'visitorsOnline_label' => { + message => q|The words "Visitors Online".|, + lastUpdated => 1178783587 + }, + + 'avatar_label' => { + message => q|The word "Avatar".|, + lastUpdated => 1178783587 + }, + + 'name_label' => { + message => q|The word "Name".|, + lastUpdated => 1178783587 + }, + + 'alias_label' => { + message => q|The word "Alias".|, + lastUpdated => 1178783587 + }, + + 'session_label' => { + message => q|The word "Session".|, + lastUpdated => 1178783587 + }, + + 'ip_label' => { + message => q|The word "IP".|, + lastUpdated => 1178783587 + }, + + 'lastActivity_label' => { + message => q|The words "Last Activity".|, + lastUpdated => 1178783587 + }, + + 'members' => { + message => q|The number of members online.|, + lastUpdated => 1178783587 + }, + + 'visitors' => { + message => q|The number of visitors online.|, + lastUpdated => 1178783587 + }, + + 'total' => { + message => q|The total number of users online.|, + lastUpdated => 1178783587 + }, + + 'isVisitor' => { + message => q|True if current user is visitor.|, + lastUpdated => 1178783587 + }, + + 'hasMembers' => { + message => q|True if member loop contains items.|, + lastUpdated => 1178783587 + }, + + 'members_loops' => { + message => q|Loop containing all members that are online and have agreed publication.|, + lastUpdated => 1178783587 + }, + + 'username' => { + message => q|The username.|, + lastUpdated => 1178783587 + }, + + 'firstName' => { + message => q|The first name.|, + lastUpdated => 1178783587 + }, + + 'middleName' => { + message => q|The middle name.|, + lastUpdated => 1178783587 + }, + + 'lastName' => { + message => q|The last name.|, + lastUpdated => 1178783587 + }, + + 'alias' => { + message => q|The alias of the user.|, + lastUpdated => 1178783587 + }, + + 'avatar' => { + message => q|The URL of the user's avatar.|, + lastUpdated => 1178783587 + }, + + 'uid' => { + message => q|The user Id.|, + lastUpdated => 1178783587 + }, + + 'sessionId' => { + message => q|The Id of the user session.|, + lastUpdated => 1178783587 + }, + + 'ip' => { + message => q|The IP adress of the user.|, + lastUpdated => 1178783587 + }, + + 'lastActivity' => { + message => q|The time of the last activity.|, + lastUpdated => 1178783587 + }, + + 'visitors_loops' => { + message => q|Loop containing all visitors that are online.|, + lastUpdated => 1178783587 + }, + +}; + +1; diff --git a/t/Macro/UsersOnline.t b/t/Macro/UsersOnline.t new file mode 100644 index 000000000..512a6692f --- /dev/null +++ b/t/Macro/UsersOnline.t @@ -0,0 +1,247 @@ +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2007 Plain Black Corporation. +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +use FindBin; +use strict; +use lib "$FindBin::Bin/../lib"; + +use JSON; + +use WebGUI::Test; +use WebGUI::International; +use WebGUI::Session; +use WebGUI::Session::Var; +use WebGUI::User; +use WebGUI::Macro::UsersOnline; + +use Test::More; # increment this value for each test you create + +my $session = WebGUI::Test->session; + +my $template = setupJSONtemplate($session); +my @users = setupUsers($session); +my $i18n = WebGUI::International->new($session,'Macro_UsersOnline'); + +my $numTests = 1; # Module loading test +$numTests += 31; # Static tests + +plan tests => $numTests; + +my $macro = 'WebGUI::Macro::UsersOnline'; +my $loaded = use_ok($macro); + +SKIP: { + +skip "Unable to load $macro", $numTests-1 unless $loaded; + + # Basic testing ----------------------------------------------------------- + + # Check for default template + my $defTemplate = WebGUI::Asset->new($session, 'pUwYHCjfQUMcRXRmKOlaRQ'); + is(defined $defTemplate, 1, 'default template is present'); + + # Call with default values + my $html = WebGUI::Macro::UsersOnline::process($session); + is((length $html) > 0, 1, 'call with default template and values returns some output'); + + + # Test labels ------------------------------------------------------------- + + $session->user({userId => 1}); + my $json = WebGUI::Macro::UsersOnline::process($session, $template->getId); + my $vars = JSON::from_json($json); + + is($vars->{'usersOnline_label'}, $i18n->get('Users Online'), 'usersOnline_label'); + is($vars->{'members_label'}, $i18n->get('Members'), 'members_label'); + is($vars->{'visitors_label'}, $i18n->get('Visitors'), 'visitors_label'); + is($vars->{'total_label'}, $i18n->get('Total'), 'total_label'); + is($vars->{'membersOnline_label'}, $i18n->get('Members Online'), 'membersOnline_label'); + is($vars->{'visitorsOnline_label'}, $i18n->get('Visitors Online'), 'visitorsOnline_label'); + is($vars->{'avatar_label'}, $i18n->get('Avatar'), 'avatar_label'); + is($vars->{'name_label'}, $i18n->get('Name'), 'name_label'); + is($vars->{'alias_label'}, $i18n->get('Alias'), 'alias_label'); + is($vars->{'session_label'}, $i18n->get('Session'), 'session_label'); + is($vars->{'ip_label'}, $i18n->get('IP'), 'ip_label'); + is($vars->{'lastActivity_label'}, $i18n->get('Last Activity'), 'lastActivity_label'); + + + # Test logic -------------------------------------------------------------- + + is($vars->{'visitors'} =~ /[0-9]+/, 1, 'visitors is numeric'); + is($vars->{'visitors'} > 0, 1, 'visitors > 0 when calling as visitor'); + is($vars->{'members'} =~ /[0-9]+/, 1, 'members is numeric'); + is($vars->{'visitors'} > 0, 1, 'members > 0 since we have created one visible active member'); + is($vars->{'total'}, $vars->{'visitors'} + $vars->{'members'}, 'total == visitors + members'); + is($vars->{'isVisitor'}, 1, 'isVisitor is defined when calling as visitor'); + is(defined $vars->{'hasMembers'}, 1, 'hasMembers is defined since we have created one visible active member'); + + + # Check member loop ------------------------------------------------------- + + my $allFieldsSet = 1; + my $firstUserPresent = 0; + my $secondUserAbsent = 1; + + foreach (@{$vars->{'member_loop'}}) { + # Check whether first user is present + if ($_->{'username'} eq 'tester1') { + # Indicate success + $firstUserPresent = 1; + # Check whether alias and name have been set correctly + $allFieldsSet = $_->{'alias'} eq 'alias1'; + $allFieldsSet &&= $_->{'firstName'} eq 'first1'; + $allFieldsSet &&= $_->{'middleName'} eq 'middle1'; + $allFieldsSet &&= $_->{'lastName'} eq 'last1'; + } + + # Check whether second user is present + if ($_->{'username'} eq 'tester2') { + # Indicate failure + $secondUserAbsent = 0; + } + + # Check whether remaining fields have been set. Note that we cannot + # check for the alias, name or avatar, since these values may be + # optional. + $allFieldsSet &&= $_->{'uid'} =~ /.*/; + $allFieldsSet &&= $_->{'sessionId'} =~ /.*/; +# $allFieldsSet &&= $_->{'ip'} =~ /.*/; # IP is not set for our test user + $allFieldsSet &&= $_->{'lastActivity'} =~ /.*/; + } + # Check booleans indicating errors + is($allFieldsSet, 1, 'fields in the member loop have been set correctly'); + is($firstUserPresent, 1, 'the first user is present in the member loop'); + is($secondUserAbsent, 1, 'the second user is absent from the member loop'); + + + # Check visitor loop ------------------------------------------------------ + + $allFieldsSet = 1; + + foreach (@{$vars->{'visitor_loop'}}) { +# $allFieldsSet &&= $_->{'ip'} =~ /.*/; # IP is not set for our test user + $allFieldsSet &&= $_->{'sessionId'} =~ /.*/; + $allFieldsSet &&= $_->{'lastActivity'} =~ /.*/; + } + + # Check booleans indicating errors + is($allFieldsSet, 1, 'fields in the visitor loop have been set correctly'); + + + # Test macro parameters --------------------------------------------------- + + # Call with zero max limits + $json = WebGUI::Macro::UsersOnline::process($session, $template->getId, undef, 0, 0); + $vars = JSON::from_json($json); + is(defined $vars->{'hasMembers'}, '', 'hasMembers undefined when display limit is set to zero'); + is(@{$vars->{'member_loop'}} == 0, 1, 'empty member loop when display limit is set to zero'); + is(@{$vars->{'visitor_loop'}} == 0, 1, 'empty visitor loop when display limit is set to zero'); + + # Call with zero max inactivity time + $json = WebGUI::Macro::UsersOnline::process($session, $template->getId, 0, undef, undef); + $vars = JSON::from_json($json); + is(defined $vars->{'hasMembers'}, '', 'hasMembers undefined when max inactivity time is set to zero'); + is(@{$vars->{'member_loop'}} == 0, 1, 'empty member loop when max inactivity time is set to zero'); + is(@{$vars->{'visitor_loop'}} == 0, 1, 'empty visitor loop when max inactivity time is set to zero'); +} + + +sub setupUsers { + my ($session) = @_; + my @users; + my $user; + + # Create first user + $user = WebGUI::User->new($session, 'new'); + $user->username("tester1"); + $user->profileField('showOnline', 1); + $user->profileField('alias', 'alias1'); + $user->profileField('firstName', 'first1'); + $user->profileField('middleName', 'middle1'); + $user->profileField('lastName', 'last1'); + unshift @users, $user; + + # Create second user + $user = WebGUI::User->new($session, 'new'); + $user->username("tester2"); + $user->profileField('showOnline', 0); + $user->profileField('alias', 'alias2'); + $user->profileField('firstName', 'first2'); + $user->profileField('middleName', 'middle2'); + $user->profileField('lastName', 'last2'); + unshift @users, $user; + + # Create sessions such that users are added to the userSession table + foreach (@users) { + $session->user({user => $_}); + WebGUI::Session::Var->new($session); + } + + return @users; +} + + +sub setupJSONtemplate { + my ($session) = @_; + my $templateBody = <, + "visitors":, + "total":, + "isVisitor":, + "hasMembers":, + "usersOnline_label":"", + "members_label":"", + "visitors_label":"", + "total_label":"", + "membersOnline_label":"", + "visitorsOnline_label":"", + "avatar_label":"", + "name_label":"", + "alias_label":"", + "session_label":"", + "ip_label":"", + "lastActivity_label":"", + "member_loop":[ + + { + "username":"", + "firstName":"", + "middleName":"", + "lastName":"", + "alias":"", + "avatar":"", + "uid":"", + "sessionId":"", + "ip":"", + "lastActivity":"" + }, + + ], + "visitor_loop":[ + + { + "sessionId":"", + "ip":"", + "lastActivity":"" + }, + + ] + } +EOTMPL + my $template = WebGUI::Asset->getImportNode($session)->addChild({className=>'WebGUI::Asset::Template', namespace => 'Shop/MiniCart', template=>$templateBody}); + return $template; +} + + +END { ##Clean-up after yourself, always + $template->purge; + $_->delete foreach (@users); +} diff --git a/www/extras/macro/UsersOnline/group-1.gif b/www/extras/macro/UsersOnline/group-1.gif new file mode 100644 index 000000000..34b802662 Binary files /dev/null and b/www/extras/macro/UsersOnline/group-1.gif differ diff --git a/www/extras/macro/UsersOnline/group-2.gif b/www/extras/macro/UsersOnline/group-2.gif new file mode 100644 index 000000000..1a5cbc738 Binary files /dev/null and b/www/extras/macro/UsersOnline/group-2.gif differ diff --git a/www/extras/macro/UsersOnline/members.gif b/www/extras/macro/UsersOnline/members.gif new file mode 100644 index 000000000..3d3c29125 Binary files /dev/null and b/www/extras/macro/UsersOnline/members.gif differ diff --git a/www/extras/macro/UsersOnline/total.gif b/www/extras/macro/UsersOnline/total.gif new file mode 100644 index 000000000..3c0025b8a Binary files /dev/null and b/www/extras/macro/UsersOnline/total.gif differ diff --git a/www/extras/macro/UsersOnline/visitors.gif b/www/extras/macro/UsersOnline/visitors.gif new file mode 100644 index 000000000..8a879b4b2 Binary files /dev/null and b/www/extras/macro/UsersOnline/visitors.gif differ