diff --git a/docs/changelog/6.x.x.txt b/docs/changelog/6.x.x.txt
index 861bcea74..05372d4c1 100644
--- a/docs/changelog/6.x.x.txt
+++ b/docs/changelog/6.x.x.txt
@@ -53,8 +53,10 @@
- Added a better user search mechanism, which works well even with 100,000
users.
- Made some minor changes to grouping lookups that resulted in an average of
- 14 less database queries per page when in admin mode which results in a 30%
+ 18 less database queries per page when in admin mode which results in a 32%
performance gain while in admin mode.
+ - Changed the group and user management interfaces to work and scale better
+ with thousands of users and groups.
6.0.3
diff --git a/lib/WebGUI/Grouping.pm b/lib/WebGUI/Grouping.pm
index 47d58061d..483b2c807 100755
--- a/lib/WebGUI/Grouping.pm
+++ b/lib/WebGUI/Grouping.pm
@@ -233,18 +233,14 @@ sub getGroupsForUser {
my $clause = "and expireDate>".time() if ($withoutExpired);
if ($userId eq "") {
return [];
- } elsif ($session{gotGroupsForUser}{$userId} == 1) {
- my @groups;
- foreach my $gid (keys %{$session{isInGroup}{$userId}}) {
- push(@groups,$gid);
- }
- return \@groups;
+ } elsif (exists $session{gotGroupsForUser}{$userId}) {
+ return $session{gotGroupsForUser}{$userId};
} else {
my @groups = WebGUI::SQL->buildArray("select groupId from groupings where userId=$userId $clause");
foreach my $gid (@groups) {
$session{isInGroup}{$userId}{$gid} = 1;
}
- $session{gotGroupsForUser}{$userId} = 1;
+ $session{gotGroupsForUser}{$userId} = \@groups;
return \@groups;
}
}
@@ -275,9 +271,12 @@ sub getGroupsInGroup {
my $groupId = shift;
my $isRecursive = shift;
my $loopCount = shift;
- my $groupsLookedUp = shift;
- my $extraWhere = "and groupId not in (".join(",",@{$groupsLookedUp}).")" if (defined @{$groupsLookedUp});
- my $groups = WebGUI::SQL->buildArrayRef("select groupId from groupGroupings where inGroup=$groupId $extraWhere");
+ if ($isRecursive && exists $session{gotGroupsInGroup}{recursive}{$groupId}) {
+ return $session{gotGroupsInGroup}{recursive}{$groupId};
+ } elsif (exists $session{gotGroupsInGroup}{recursive}{$groupId}) {
+ return $session{gotGroupsInGroup}{direct}{$groupId};
+ }
+ my $groups = WebGUI::SQL->buildArrayRef("select groupId from groupGroupings where inGroup=$groupId");
if ($isRecursive) {
$loopCount++;
if ($loopCount > 99) {
@@ -286,11 +285,13 @@ sub getGroupsInGroup {
}
my @groupsOfGroups = @$groups;
foreach my $group (@$groups) {
- my $gog = getGroupsInGroup($group,1,$loopCount,\@groupsOfGroups);
+ my $gog = getGroupsInGroup($group,1,$loopCount);
push(@groupsOfGroups, @$gog);
}
+ $session{gotGroupsInGroup}{recursive}{$groupId} = \@groupsOfGroups;
return \@groupsOfGroups;
- }
+ }
+ $session{gotGroupsInGroup}{direct}{$groupId} = $groups;
return $groups;
}
diff --git a/lib/WebGUI/Operation/Group.pm b/lib/WebGUI/Operation/Group.pm
index 128869455..5d444ef28 100644
--- a/lib/WebGUI/Operation/Group.pm
+++ b/lib/WebGUI/Operation/Group.pm
@@ -17,12 +17,14 @@ use WebGUI::DatabaseLink;
use WebGUI::DateTime;
use WebGUI::Group;
use WebGUI::Grouping;
+use WebGUI::Form;
use WebGUI::FormProcessor;
use WebGUI::HTMLForm;
use WebGUI::Icon;
use WebGUI::International;
use WebGUI::Mail;
use WebGUI::Operation::Shared;
+use WebGUI::Operation::User;
use WebGUI::Paginator;
use WebGUI::Privilege;
use WebGUI::Session;
@@ -67,6 +69,75 @@ sub _submenu {
return menuWrapper($_[0],\%menu);
}
+
+#-------------------------------------------------------------------
+sub doGroupSearch {
+ my $op = shift;
+ my $returnPaginator = shift;
+ my $groupFilter = shift;
+ push(@{$groupFilter},0);
+ my $keyword = $session{scratch}{groupSearchKeyword};
+ if ($session{scratch}{groupSearchModifier} eq "startsWith") {
+ $keyword .= "%";
+ } elsif ($session{scratch}{groupSearchModifier} eq "contains") {
+ $keyword = "%".$keyword."%";
+ } else {
+ $keyword = "%".$keyword;
+ }
+ $keyword = quote($keyword);
+ my $sql = "select groupId,groupName,description from groups where isEditable=1 and (groupName like $keyword or description like $keyword)
+ and groupId not in (".join(",",@{$groupFilter}).") order by groupName";
+ if ($returnPaginator) {
+ my $p = WebGUI::Paginator->new(WebGUI::URL::page($op));
+ $p->setDataByQuery($sql);
+ return $p;
+ } else {
+ my $sth = WebGUI::SQL->read($sql);
+ return $sth;
+ }
+}
+
+
+#-------------------------------------------------------------------
+sub getGroupSearchForm {
+ my $op = shift;
+ my $params = shift;
+ WebGUI::Session::setScratch("groupSearchKeyword",$session{form}{keyword});
+ WebGUI::Session::setScratch("groupSearchModifier",$session{form}{modifier});
+ my $output = '
';
+ my $f = WebGUI::HTMLForm->new(1);
+ foreach my $key (keys %{$params}) {
+ $f->hidden(
+ -name=>$key,
+ -value=>$params->{$key}
+ );
+ }
+ $f->hidden("op",$op);
+ $f->hidden(
+ -name=>"doit",
+ -value=>1
+ );
+ $f->selectList(
+ -name=>"modifier",
+ -value=>([$session{scratch}{groupSearchModifier}] || ["contains"]),
+ -options=>{
+ startsWith=>WebGUI::International::get("starts with"),
+ contains=>WebGUI::International::get("contains"),
+ endsWith=>WebGUI::International::get("ends with")
+ }
+ );
+ $f->text(
+ -name=>"keyword",
+ -value=>$session{scratch}{groupSearchKeyword},
+ -size=>15
+ );
+ $f->submit(WebGUI::International::get(170));
+ $output .= $f->print;
+ $output .= '
';
+ return $output;
+}
+
+
#-------------------------------------------------------------------
sub www_addGroupsToGroupSave {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
@@ -297,6 +368,35 @@ sub www_emailGroupSend {
#-------------------------------------------------------------------
sub www_listGroups {
+ return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
+ my $output = helpIcon("groups manage");
+ $output .= ''.WebGUI::International::get(89).'
';
+ $output .= getGroupSearchForm("listGroups");
+ my ($groupCount) = WebGUI::SQL->quickArray("select count(*) from groups");
+ return _submenu($output) unless ($session{form}{doit} || $groupCount<250);
+ $output .= '';
+ $output .= '
';
+ my $p = doGroupSearch("op=listGroups",1);
+ foreach my $row (@{$p->getPageData}) {
+ my ($userCount) = WebGUI::SQL->quickArray("select count(*) from groupings where groupId=".$row->{groupId});
+ $output .= '
+
+ | {groupId}).'">'.$row->{groupName}.' |
+ '.$row->{description}.' |
+ '.$userCount.' |
+
+ ';
+ }
+ $output .= '
';
+ $output .= $p->getBarTraditional;
+ return _submenu($output);
+}
+
+
+#-------------------------------------------------------------------
+sub www_listGroups2 {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
my ($output, $p, $sth, @data, @row, $i, $userCount);
$output = helpIcon("groups manage");
@@ -398,41 +498,67 @@ sub www_manageGroupsInGroup {
#-------------------------------------------------------------------
sub www_manageUsersInGroup {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
- my ($output, $sth, %hash);
- tie %hash, 'Tie::CPHash';
- $output = ''.WebGUI::International::get(88).'
';
+ my $output = ''.WebGUI::International::get(88).'
';
+ $output .= WebGUI::Form::formHeader()
+ .WebGUI::Form::hidden({
+ name=>"gid",
+ value=>$session{form}{gid}
+ })
+ .WebGUI::Form::hidden({
+ name=>"return",
+ value=>"manageUsersInGroup"
+ })
+ .WebGUI::Form::hidden({
+ name=>"op",
+ value=>"deleteGrouping"
+ });
+ $output .= '
+
+
';
+ my $p = WebGUI::Paginator->new("op=manageUsersInGroup&gid=".$session{form}{gid});
+ $p->setDataByQuery("select users.username,users.userId,groupings.expireDate
+ from groupings,users where groupings.groupId=$session{form}{gid} and groupings.userId=users.userId
+ order by users.username");
+ foreach my $row (@{$p->getPageData}) {
+ $output .= '| '
+ .WebGUI::Form::checkbox({
+ name=>"uid",
+ value=>$row->{userId}
+ })
+ .deleteIcon('op=deleteGrouping&return=manageUsersInGroup&uid='.$row->{userId}.'&gid='.$session{form}{gid})
+ .editIcon('op=editGrouping&uid='.$row->{userId}.'&gid='.$session{form}{gid})
+ .' | ';
+ $output .= ''.$row->{username}.' | ';
+ $output .= ''.epochToHuman($row->{expireDate},"%z").' |
';
+ }
+ $output .= '
';
+ $output .= ''.WebGUI::International::get(976).'
';
+ $output .= WebGUI::Operation::User::getUserSearchForm("manageUsersInGroup",{gid=>$session{form}{gid}});
+ my ($userCount) = WebGUI::SQL->quickArray("select count(*) from users");
+ return _submenu($output) unless ($session{form}{doit} || $userCount < 250);
my $f = WebGUI::HTMLForm->new;
$f->hidden("gid",$session{form}{gid});
$f->hidden("op","addUsersToGroupSave");
my $existingUsers = WebGUI::Grouping::getUsersInGroup($session{form}{gid});
push(@{$existingUsers},"1");
- my $users = WebGUI::SQL->buildHashRef("select userId,username from users where status='Active' and userId not in (".join(",",@{$existingUsers}).") order by username");
+ my %users;
+ tie %users, "Tie::IxHash";
+ my $sth = WebGUI::Operation::User::doUserSearch("op=manageUsersInGroup&gid=".$session{form}{gid},0,$existingUsers);
+ while (my $data = $sth->hashRef) {
+ $users{$data->{userId}} = $data->{username};
+ $users{$data->{userId}} .= " (".$data->{email}.")" if ($data->{email});
+ }
+ $sth->finish;
$f->selectList(
-name=>"users",
-label=>WebGUI::International::get(976),
- -options=>$users,
+ -options=>\%users,
-multiple=>1,
-size=>7
);
$f->submit;
$output .= $f->print;
- $output .= '
-
-
';
- $sth = WebGUI::SQL->read("select users.username,users.userId,groupings.expireDate
- from groupings,users where groupings.groupId=$session{form}{gid} and groupings.userId=users.userId
- order by users.username");
- while (%hash = $sth->hash) {
- $output .= '| '
- .deleteIcon('op=deleteGrouping&return=manageUsersInGroup&uid='.$hash{userId}.'&gid='.$session{form}{gid})
- .editIcon('op=editGrouping&uid='.$hash{userId}.'&gid='.$session{form}{gid})
- .' | ';
- $output .= ''
- .$hash{username}.' | ';
- $output .= ''.epochToHuman($hash{expireDate},"%z").' |
';
- }
- $sth->finish;
- $output .= '
';
return _submenu($output);
}
diff --git a/lib/WebGUI/Operation/User.pm b/lib/WebGUI/Operation/User.pm
index 09f7bfef7..179ec2ce7 100644
--- a/lib/WebGUI/Operation/User.pm
+++ b/lib/WebGUI/Operation/User.pm
@@ -26,6 +26,7 @@ use WebGUI::Paginator;
use WebGUI::Privilege;
use WebGUI::Session;
use WebGUI::SQL;
+use WebGUI::Style;
use WebGUI::URL;
use WebGUI::User;
use WebGUI::Utility;
@@ -60,13 +61,101 @@ sub _submenu {
return menuWrapper($_[0],\%menu);
}
+
+#-------------------------------------------------------------------
+sub doUserSearch {
+ my $op = shift;
+ my $returnPaginator = shift;
+ my $userFilter = shift;
+ push(@{$userFilter},0);
+ my $selectedStatus;
+ if ($session{scratch}{userSearchStatus}) {
+ $selectedStatus = "status='".$session{scratch}{userSearchStatus}."'";
+ } else {
+ $selectedStatus = "status like '%'";
+ }
+ my $keyword = $session{scratch}{userSearchKeyword};
+ if ($session{scratch}{userSearchModifier} eq "startsWith") {
+ $keyword .= "%";
+ } elsif ($session{scratch}{userSearchModifier} eq "contains") {
+ $keyword = "%".$keyword."%";
+ } else {
+ $keyword = "%".$keyword;
+ }
+ $keyword = quote($keyword);
+ my $sql = "select users.userId, users.username, users.status, users.dateCreated, users.lastUpdated,
+ email.fieldData as email from users left join userProfileData email on users.userId=email.userId and email.fieldName='email'
+ where $selectedStatus and (users.username like ".$keyword." or email.fieldData like ".$keyword.")
+ and users.userId not in (".join(",",@{$userFilter}).") order by users.username";
+ if ($returnPaginator) {
+ my $p = WebGUI::Paginator->new(WebGUI::URL::page($op));
+ $p->setDataByQuery($sql);
+ return $p;
+ } else {
+ my $sth = WebGUI::SQL->read($sql);
+ return $sth;
+ }
+}
+
+
+#-------------------------------------------------------------------
+sub getUserSearchForm {
+ my $op = shift;
+ my $params = shift;
+ WebGUI::Session::setScratch("userSearchKeyword",$session{form}{keyword});
+ WebGUI::Session::setScratch("userSearchStatus",$session{form}{status});
+ WebGUI::Session::setScratch("userSearchModifier",$session{form}{modifier});
+ my $output = '';
+ my $f = WebGUI::HTMLForm->new(1);
+ $f->hidden("op",$op);
+ foreach my $key (keys %{$params}) {
+ $f->hidden(
+ -name=>$key,
+ -value=>$params->{$key}
+ );
+ }
+ $f->hidden(
+ -name=>"doit",
+ -value=>1
+ );
+ $f->selectList(
+ -name=>"modifier",
+ -value=>([$session{scratch}{userSearchModifier}] || ["contains"]),
+ -options=>{
+ startsWith=>WebGUI::International::get("starts with"),
+ contains=>WebGUI::International::get("contains"),
+ endsWith=>WebGUI::International::get("ends with")
+ }
+ );
+ $f->text(
+ -name=>"keyword",
+ -value=>$session{scratch}{userSearchKeyword},
+ -size=>15
+ );
+ $f->selectList(
+ -name => "status",
+ -value => [$session{scratch}{userSearchStatus} || "users.status like '%'"],
+ -options=> {
+ "" => WebGUI::International::get(821),
+ Active => WebGUI::International::get(817),
+ Deactivated => WebGUI::International::get(818),
+ Selfdestructed => WebGUI::International::get(819)
+ }
+ );
+ $f->submit(WebGUI::International::get(170));
+ $output .= $f->print;
+ $output .= '
';
+ return $output;
+}
+
+
#-------------------------------------------------------------------
sub www_addUser {
my ($output, $f, $cmd, $html, %status);
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3) || WebGUI::Grouping::isInGroup(11));
$output .= helpIcon("user add/edit");
$output .= ''.WebGUI::International::get(163).'
';
- $output .= WebGUI::Form::_javascriptFile("swapLayers.js");
+ WebGUI::Style::setScript($session{config}{extrasURL}."/swapLayers.js", {language=>"JavaScript"});
$output .= '';
if ($session{form}{op} eq "addUserSave") {
@@ -167,8 +256,12 @@ sub www_deleteGrouping {
if (($session{user}{userId} == $session{form}{uid} || $session{form}{uid} == 3) && $session{form}{gid} == 3) {
return WebGUI::Privilege::vitalComponent();
}
- my $u = WebGUI::User->new($session{form}{uid});
- $u->deleteFromGroups([$session{form}{gid}]);
+ my @users = $session{cgi}->param('uid');
+ my @groups = $session{cgi}->param("gid");
+ foreach my $user (@users) {
+ my $u = WebGUI::User->new($user);
+ $u->deleteFromGroups(\@groups);
+ }
if ($session{form}{return} eq "manageUsersInGroup") {
return WebGUI::Operation::Group::www_manageUsersInGroup();
}
@@ -242,7 +335,7 @@ sub www_editUser {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
my ($output, $f, $u, $cmd, $html, %status);
$u = WebGUI::User->new($session{form}{uid});
- $output .= WebGUI::Form::_javascriptFile("swapLayers.js");
+ WebGUI::Style::setScript($session{config}{extrasURL}."/swapLayers.js", {language=>"JavaScript"});
$output .= '';
$output .= helpIcon("user add/edit");
$output .= ''.WebGUI::International::get(168).'
';
@@ -315,41 +408,64 @@ sub www_editUserSave {
#-------------------------------------------------------------------
sub www_editUserGroup {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
- my ($output, $f, $groups, $sth, %hash);
+ my %hash;
tie %hash, 'Tie::CPHash';
- $output .= ''.WebGUI::International::get(372).'
';
- $f = WebGUI::HTMLForm->new;
+ my $output = ''.WebGUI::International::get(372).'
';
+ $output .= WebGUI::Form::formHeader()
+ .WebGUI::Form::hidden({
+ name=>"uid",
+ value=>$session{form}{uid}
+ })
+ .WebGUI::Form::hidden({
+ name=>"op",
+ value=>"deleteGrouping"
+ });
+ $output .= '
';
+ my $p = WebGUI::Paginator->new("op=editUserGroups&uid=".$session{form}{uid});
+ $p->setDataByQuery("select groups.groupId,groups.groupName,groupings.expireDate
+ from groupings,groups where groupings.groupId=groups.groupId and groupings.userId=$session{form}{uid} order by groups.groupName");
+ foreach my $row (@{$p->getPageData}) {
+ $output .= '| '
+ .WebGUI::Form::checkbox({
+ name=>"gid",
+ value=>$row->{groupId}
+ })
+ .deleteIcon('op=deleteGrouping&uid='.$session{form}{uid}.'&gid='.$row->{groupId})
+ .editIcon('op=editGrouping&uid='.$session{form}{uid}.'&gid='.$row->{groupId})
+ .' | ';
+ $output .= ''.$row->{groupName}.' | ';
+ $output .= ''.epochToHuman($row->{expireDate},"%z").' |
';
+ }
+ $output .= '
';
+ $output .= ''.WebGUI::International::get(605).'
';
+ $output .= WebGUI::Operation::Group::getGroupSearchForm("editUserGroup",{uid=>$session{form}{uid}});
+ my ($groupCount) = WebGUI::SQL->quickArray("select count(*) from users");
+ return _submenu($output) unless ($session{form}{doit} || $groupCount < 250);
+ my $f = WebGUI::HTMLForm->new;
$f->hidden("op","addUserToGroupSave");
$f->hidden("uid",$session{form}{uid});
- $groups = WebGUI::Grouping::getGroupsForUser($session{form}{uid});
- push(@$groups,1); #visitors
- push(@$groups,2); #registered users
- push(@$groups,7); #everyone
- $f->group(
+ my $existingGroups = WebGUI::Grouping::getGroupsForUser($session{form}{uid});
+ push(@$existingGroups,1); #visitors
+ push(@$existingGroups,2); #registered users
+ push(@$existingGroups,7); #everyone
+ my %groups;
+ tie %groups, "Tie::IxHash";
+ my $sth = WebGUI::Operation::Group::doGroupSearch("op=editUserGroup&uid=".$session{form}{uid},0,$existingGroups);
+ while (my $data = $sth->hashRef) {
+ $groups{$data->{groupId}} = $data->{groupName};
+ }
+ $sth->finish;
+ $f->selectList(
-name=>"groups",
- -excludeGroups=>$groups,
-label=>WebGUI::International::get(605),
- -size=>5,
- -multiple=>1
+ -size=>7,
+ -multiple=>1,
+ -options=>\%groups
);
$f->submit;
$output .= $f->print;
- $output .= '
';
- $sth = WebGUI::SQL->read("select groups.groupId,groups.groupName,groupings.expireDate
- from groupings,groups where groupings.groupId=groups.groupId and
- groupings.userId=$session{form}{uid} order by groups.groupName");
- while (%hash = $sth->hash) {
- $output .= '| '
- .deleteIcon('op=deleteGrouping&uid='.$session{form}{uid}.'&gid='.$hash{groupId})
- .editIcon('op=editGrouping&uid='.$session{form}{uid}.'&gid='.$hash{groupId})
- .' | ';
- $output .= ''.$hash{groupName}.' | ';
- $output .= ''.epochToHuman($hash{expireDate},"%z").' |
';
- }
- $sth->finish;
- $output .= '
';
return _submenu($output);
}
@@ -466,48 +582,18 @@ sub www_editUserProfileSave {
#-------------------------------------------------------------------
sub www_listUsers {
return WebGUI::Privilege::adminOnly() unless (WebGUI::Grouping::isInGroup(3));
- WebGUI::Session::setScratch("userSearchKeyword",$session{form}{keyword});
- WebGUI::Session::setScratch("userSearchStatus",$session{form}{status});
- my ($data, $rows, $p, %status, $selectedStatus);
+ my %status;
my $output = helpIcon("users manage");
$output .= ''.WebGUI::International::get(149).'
';
- $output .= '';
+ $output .= getUserSearchForm("listUsers");
+ my ($userCount) = WebGUI::SQL->quickArray("select count(*) from users");
+ return _submenu($output) unless ($session{form}{doit} || $userCount<250);
tie %status, 'Tie::IxHash';
%status = (
- "" => WebGUI::International::get(821),
Active => WebGUI::International::get(817),
Deactivated => WebGUI::International::get(818),
Selfdestructed => WebGUI::International::get(819)
);
- my $f = WebGUI::HTMLForm->new(1);
- $f->hidden("op","listUsers");
- $f->hidden(
- -name=>"doit",
- -value=>1
- );
- $f->selectList(
- -name=>"modifier",
- -value=>([$session{form}{modifier}] || ["contains"]),
- -options=>{
- startsWith=>WebGUI::International::get("starts with"),
- contains=>WebGUI::International::get("contains"),
- endsWith=>WebGUI::International::get("ends with")
- }
- );
- $f->text(
- -name=>"keyword",
- -value=>$session{scratch}{userSearchKeyword},
- -size=>15
- );
- $f->selectList(
- -name => "status",
- -value => [$session{form}{status} || "users.status like '%'"],
- -options=> \%status
- );
- $f->submit(WebGUI::International::get(170));
- $output .= $f->print;
- $output .= '
';
- return _submenu($output) unless ($session{form}{doit});
$output .= '';
$output .= '
@@ -518,25 +604,8 @@ sub www_listUsers {
';
- if ($session{scratch}{userSearchStatus}) {
- $selectedStatus = "status='".$session{scratch}{userSearchStatus}."'";
- } else {
- $selectedStatus = "status like '%'";
- }
- my $keyword = $session{scratch}{userSearchKeyword};
- if ($session{form}{modifier} eq "startsWith") {
- $keyword .= "%";
- } elsif ($session{form}{modifier} eq "contains") {
- $keyword = "%".$keyword."%";
- } else {
- $keyword = "%".$keyword;
- }
- $p = WebGUI::Paginator->new(WebGUI::URL::page("op=listUsers"));
- $p->setDataByQuery("select users.userId, users.username, users.status, users.dateCreated, users.lastUpdated,
- email.fieldData as email from users left join userProfileData email on users.userId=email.userId and email.fieldName='email'
- where $selectedStatus and (users.username like ".quote($keyword)." or email.fieldData like ".quote($keyword).") order by users.username");
- $rows = $p->getPageData;
- foreach $data (@$rows) {
+ my $p = doUserSearch("listUsers",1);
+ foreach my $data (@{$p->getPageData}) {
$output .= '';
$output .= '| '.$status{$data->{status}}.' | ';
$output .= ' |