###############################################################################
# Memberlist.pm #
# $Date: 01.09.2026 $ #
###############################################################################
# YaBBForum: Yet another Bulletin Board #
# Open-Source Community Software for Webmasters #
# Version: YaBBForum 3.1 #
# Packaged: 01 Sep, 2026 #
# Distributed by: https://yabbforum.nz #
# =========================================================================== #
# Copyright (c) 2000-2026 YaBBForum (yabbforum.nz) - All Rights Reserved. #
# Software by: The YaBBForum Development Team #
# with assistance from the YaBBForum community. #
###############################################################################
# use strict;
#use warnings;
#no warnings qw(uninitialized once redefine);
use CGI::Carp qw(fatalsToBrowser);
our $VERSION = '3.1';
$memberlistpmver = 'YaBBForum 3.1';
if ( $action eq 'detailedversion' ) { return 1; }
if ( $iamguest && $ML_Allowed ) { fatal_error('no_access'); }
if ( $ML_Allowed == 2 && !$staff ) {
fatal_error('no_access');
}
if ( ( $ML_Allowed == 3 && !$iamadmin && !$iamgmod )
|| ( $ML_Allowed == 4 && !$iamadmin && !$iamgmod && !$iamfmod ) )
{
fatal_error('no_access');
}
LoadLanguage('MemberList');
get_micon();
get_template('Memberlist');
$MembersPerPage = $TopAmmount;
$maxbar = 100;
$dr_warning = q{};
$forumstart = $forumstart ? stringtotime($forumstart) : '1104537600';
sub Ml {
# Decides how to sort memberlist, and gives default sort order
if ( !$barmaxnumb ) { $barmaxnumb = 500; }
if ( $barmaxdepend == 1 ) {
$barmax = 1;
ManageMemberinfo('load');
while ( ( $key, $value ) = each %memberinf ) {
( undef, undef, undef, $memposts ) = split /\|/xsm, $value;
if ( $memposts > $barmax ) { $barmax = $memposts; }
}
undef %memberinf;
}
else {
$barmax = $barmaxnumb;
}
$FORM{'sortform'} ||= $INFO{'sortform'}; # Fix for Javascript disabled
if ( $INFO{'sort'} eq q{} && $FORM{'sortform'} eq q{} ) {
$INFO{'sort'} = $defaultml;
$FORM{'sortform'} = $defaultml;
}
if ( $FORM{'sortform'} eq 'username'
|| $INFO{'sort'} eq 'mlletter'
|| $INFO{'sort'} eq 'username' )
{
$page = 'a';
$showpage = 'A';
while ( $page ne 'z' ) {
$LetterLinks .=
qq($showpage );
$page++;
$showpage++;
}
$LetterLinks .=
qq(Z $ml_txt{'800'} );
}
if ( $INFO{'start'} eq q{} ) { $start = 0; }
else { $start = "$INFO{'start'}"; }
if ( $FORM{'sortform'} eq 'posts' || $INFO{'sort'} eq 'posts' ) {
$selcPost .= q~ selected="selected"~;
$selPost .= qq~class="$header_class_selected"~;
}
else { $selPost .= qq~class="$header_class"~; }
if ( $FORM{'sortform'} eq 'regdate' || $INFO{'sort'} eq 'regdate' ) {
$selcReg .= q~ selected="selected"~;
$selReg .= qq~class="$header_class_selected"~;
}
else { $selReg .= qq~class="$header_class"~; }
if ( $FORM{'sortform'} eq 'position' || $INFO{'sort'} eq 'position' ) {
$selcPos .= q~ selected="selected"~;
$selPos .= qq~class="$header_class_selected"~;
}
else { $selPos .= qq~class="$header_class"~; }
if ( $FORM{'sortform'} eq 'username'
|| $INFO{'sort'} eq 'mlletter'
|| $INFO{'sort'} eq 'username' )
{
$selcUser .= q~ selected="selected"~;
$selUser .= qq~class="$header_class_selected"~;
}
else { $selUser .= qq~class="$header_class"~; }
if ( $FORM{'sortform'} eq 'posts' || $INFO{'sort'} eq 'posts' ) { MLTop(); }
if ( $FORM{'sortform'} eq 'regdate' || $INFO{'sort'} eq 'regdate' ) {
MLDate();
}
if ( $FORM{'sortform'} eq 'position' || $INFO{'sort'} eq 'position' ) {
MLPosition();
}
if ( $FORM{'sortform'} eq 'memsearch' || $INFO{'sort'} eq 'memsearch' ) {
FindMembers();
}
if ( $INFO{'sort'} eq q{}
|| $INFO{'sort'} eq 'mlletter'
|| $INFO{'sort'} eq 'username' )
{
MLByLetter();
}
return;
}
sub MLByLetter {
$letter = lc $INFO{'letter'};
$i = 0;
ManageMemberinfo('load');
foreach my $membername (
sort { lc $memberinf{$a} cmp lc $memberinf{$b} }
keys %memberinf
)
{
( $memrealname, $mememail, undef, undef ) =
split /\|/xsm, $memberinf{$membername};
if ($letter) {
$SearchName = lc( substr $memrealname, 0, 1 );
if ( $SearchName eq $letter ) { $ToShow[$i] = $membername; $i++; }
elsif ( $letter eq 'other'
&& ( ( $SearchName lt 'a' ) || ( $SearchName gt 'z' ) ) )
{
$ToShow[$i] = $membername;
$i++;
}
}
else {
$ToShow[$i] = $membername;
$i++;
}
}
undef %memberinf;
$memcount = @ToShow;
if ( !$memcount && $letter ) {
$pageindex1 =
q~
~;
$pageindex2 =
q~
~;
}
else {
buildIndex();
}
buildPages(1);
$bb = $start;
$numshown = 0;
if ($memcount) {
while ( $numshown < $MembersPerPage ) {
showRows( $ToShow[$bb] );
$numshown++;
$bb++;
}
}
else {
if ($letter) {
$yymain .= $my_letter;
$yymain =~ s/{yabb headercount}/$headercount/sm;
}
}
undef @ToShow;
buildPages(0);
$yytitle = "$ml_txt{'312'} $numshow";
template();
return;
}
sub MLTop {
%top_list = ();
ManageMemberinfo('load');
while ( ( $membername, $value ) = each %memberinf ) {
( $memrealname, undef, undef, $memposts ) = split /\|/xsm, $value;
$memposts = sprintf '%06d', ( 999_999 - $memposts );
$top_list{$membername} = qq~$memposts|$memrealname~;
}
undef %memberinf;
my @toplist = sort { lc $top_list{$a} cmp lc $top_list{$b} } keys %top_list;
$memcount = @toplist;
buildIndex();
buildPages(1);
$bb = $start;
$numshown = 0;
while ( $numshown < $MembersPerPage ) {
showRows( $toplist[$bb] );
$numshown++;
$bb++;
}
undef @toplist;
buildPages(0);
$yytitle = "$ml_txt{'313'} $ml_txt{'314'} $numshow";
template();
return;
}
sub MLPosition {
%TopMembers = ();
ManageMemberinfo('load');
my %nopostorder;
for my $i ( 0 .. $#nopostorder ) {
$nopostorder{ $nopostorder[$i] } = $i;
}
MEMBERPOSITION: while ( ( $membername, $value ) = each %memberinf ) {
( $memberrealname, undef, $memposition, $memposts ) =
split /\|/xsm, $value;
$memposts = 9_999_999_999 - $memposts;
foreach ( keys %Group ) {
if ( $memposition eq $_ ) {
if ( $_ eq 'Administrator' ) {
$TopMembers{$membername} = "a$memposts$memberrealname";
next MEMBERPOSITION;
}
elsif ( $_ eq 'Global Moderator' ) {
$TopMembers{$membername} = "b$memposts$memberrealname";
next MEMBERPOSITION;
}
elsif ( $_ eq 'Mid Moderator' ) {
$TopMembers{$membername} = "bc$memposts$memberrealname";
next MEMBERPOSITION;
}
}
}
foreach ( keys %NoPost ) {
if ( $_ == $memposition ) {
$memposition = sprintf '%06d', $nopostorder{$_};
$TopMembers{$membername} =
"d$memposition$memposts$memberrealname";
next MEMBERPOSITION;
}
}
$TopMembers{$membername} = "e$memposts$memberrealname";
}
my @toplist =
sort { lc( $TopMembers{$a} ) cmp lc $TopMembers{$b} } keys %TopMembers;
$memcount = @toplist;
buildIndex();
buildPages(1);
$bb = $start;
$numshown = 0;
while ( $numshown < $MembersPerPage ) {
showRows( $toplist[$bb] );
$numshown++;
$bb++;
}
undef @toplist;
undef %memberinf;
buildPages(0);
$yytitle = "$ml_txt{'313'} $ml_txt{'4'} $ml_txt{'87'} $numshow";
template();
return;
}
sub MLDate {
( $memcount, undef ) = MembershipGet();
buildIndex();
buildPages(1);
fopen( MEMBERLISTREAD, "$memberdir/memberlist.txt" );
$counter = 0;
while ( $counter < $start && ( $buffer = ) ) {
$counter++;
}
foreach my $counter ( 0 .. ( $MembersPerPage - 1 ) ) {
if ( $buffer = ) {
chomp $buffer;
if ($buffer) {
( $membername, undef ) = split /\t/xsm, $buffer, 2;
showRows($membername);
}
}
}
fclose(MEMBERLISTREAD);
buildPages(0);
$yytitle = "$ml_txt{'313'} $ml_txt{'4'} $ml_txt{'233'} $numshow";
template();
return;
}
sub showRows {
my ($user) = @_;
my $wwwshow = qq~
~;
if ( $user ne q{} ) {
LoadUser($user);
my $group_stars = q{};
if ($group_stars_ml) {
if ( $user eq $username ) { LoadMiniUser($user); }
$memberstar{$user} =~ s/
//gsm;
$group_stars = qq~
$memberstar{$user}~;
}
if ( ${ $uid . $user }{'realname'} eq q{} ) {
${ $uid . $user }{'realname'} = $user;
}
if ( !$minlinkweb ) { $minlinkweb = 0; }
if (
${ $uid . $user }{'weburl'}
&& ( ${ $uid . $user }{'postcount'} >= $minlinkweb
|| ${ $uid . $user }{'position'} eq 'Administrator'
|| ${ $uid . $user }{'position'} eq 'Global Moderator'
|| ${ $uid . $user }{'position'} eq 'Mid Moderator' )
)
{
$wwwshow =
qq~
~;
}
$barchart = ${ $uid . $user }{'postcount'};
$bartemp = ( ${ $uid . $user }{'postcount'} * $maxbar );
$barwidth = ( $bartemp / $barmax );
$barwidth = ( $barwidth + 0.5 );
$barwidth = int $barwidth;
if ( $barwidth > $maxbar ) { $barwidth = $maxbar }
if ( $barchart < 1 ) { $Bar = q{}; }
else {
$Bar =
qq~
~;
}
if ( $Bar eq q{} ) { $Bar = ' '; }
my $additional_tds =
$extendedprofiles ? ext_memberlist_tds($user) : q{};
$dr_regdate = q{};
if ( ${ $uid . $user }{'regtime'} ) {
$dr_regdate = timeformat( ${ $uid . $user }{'regtime'} );
$dr_regdate =~ s/(.*)(, 1?[0-9]):[0-9][0-9].*/$1/sm;
if ( $iamadmin && ${ $uid . $user }{'regtime'} < $forumstart ) {
$dr_regdate =
qq~$dr_regdate *~;
$dr_warning =
qq~$ml_txt{'dr_warning'} $ml_txt{'dr_warnurl'}~;
}
}
if ( $showuserpicml && $allowpics ) {
${ $uid . $user }{'userpic'} ||= $my_blank_avatar;
$my_userpic = q~
~;
if ( !$iamguest ) {
$my_userpic =
qq~$my_userpic~;
}
$userpic = $my_userpic_td;
$userpic =~ s/{yabb my_userpic}/$my_userpic/sm;
}
else {
$userpic = q{};
}
if ( ${ $uid . $user }{'hidemail'}
&& !$iamadmin
&& $allow_hide_email == 1 )
{
$lock = qq~
~;
}
else {
if ( !$iamguest ) {
$lock = enc_eMail(
qq~
~,
${ $uid . $user }{'email'},
q{}, q{}
);
}
else {
$lock = qq~
~;
}
}
$yypostcount = NumberFormat( ${ $uid . $user }{'postcount'} );
$yymain .= $my_memrow;
$yymain =~ s/{yabb add_tds}/$additional_tds/sm;
$yymain =~ s/{yabb userpic}/$userpic/sm;
$yymain =~ s/{yabb userlink}/$link{$user}/sm;
$yymain =~ s/{yabb lock}/$lock/sm;
$yymain =~ s/{yabb wwwshow}/$wwwshow/sm;
$yymain =~ s/{yabb meminfo}/$memberinfo{$user}$group_stars/sm;
$yymain =~ s/{yabb bar}/$Bar/sm;
$yymain =~ s/{yabb postcount}/$yypostcount/sm;
$yymain =~ s/{yabb dr_regdate}/$dr_regdate/sm;
## Mod Hook ##
}
return $yymain;
}
sub buildIndex {
if ( $memcount != 0 ) {
if ( !$iamguest ) {
( undef, undef, $usermemberpage, undef ) =
split /\|/xsm, ${ $uid . $username }{'pageindex'};
}
# Build the page links list.
my ( $pagetxtindex, $pagedropindex1, $pagedropindex2, $all,
$allselected );
$indexdisplaynum = 3;
$dropdisplaynum = 10;
if ( $FORM{'sortform'} eq q{} ) { $FORM{'sortform'} = $INFO{'sort'}; }
$postdisplaynum = 3;
$startpage = 0;
$max = $memcount;
if ( $SearchStr ne q{} ) { $findmember = qq~;member=$SearchStr~; }
if ( $INFO{'start'} eq 'all' ) {
$MembersPerPage = $max;
$all = 1;
$allselected = q~ selected="selected"~;
$start = 0;
}
else { $start = $INFO{'start'} || 0; }
$start = $start > $memcount - 1 ? $memcount - 1 : $start;
$start = ( int( $start / $MembersPerPage ) ) * $MembersPerPage;
$tmpa = 1;
$pagenumb = int( ( $memcount - 1 ) / $MembersPerPage ) + 1;
if ( $start >= ( ( $postdisplaynum - 1 ) * $MembersPerPage ) ) {
$startpage = $start - ( ( $postdisplaynum - 1 ) * $MembersPerPage );
$tmpa = int( $startpage / $MembersPerPage ) + 1;
}
if ( $memcount >= $start + ( $postdisplaynum * $MembersPerPage ) ) {
$endpage = $start + ( $postdisplaynum * $MembersPerPage );
}
else { $endpage = $memcount }
$lastpn = int( ( $memcount - 1 ) / $MembersPerPage ) + 1;
$lastptn = ( $lastpn - 1 ) * $MembersPerPage;
$pageindex1 =
q~
$ml_txt{'139'}: $pagenumb~;
$pageindex2 =
q~
$ml_txt{'139'}: $pagenumb~;
if ( $pagenumb > 1 || $all ) {
if ( $usermemberpage == 1 || $iamguest ) {
$pagetxtindexst = q~~;
if ( !$iamguest ) {
$pagetxtindexst .=
qq~
$ml_txt{'139'}: ~;
}
else {
$pagetxtindexst .=
qq~
$ml_txt{'139'}: ~;
}
if ( $startpage > 0 ) {
$pagetxtindex =
qq~1 ... ~;
}
if ( $startpage == $MembersPerPage ) {
$pagetxtindex =
qq~1 ~;
}
foreach my $counter ( $startpage .. ( $endpage - 1 ) ) {
if ( $counter % $MembersPerPage == 0 ) {
$pagetxtindex .=
$start == $counter
? qq~[$tmpa] ~
: qq~$tmpa ~;
$tmpa++;
}
}
if ( $endpage < $memcount - $MembersPerPage ) {
$pageindexadd = q~... ~;
}
if ( $endpage != $memcount ) {
$pageindexadd .=
qq~$lastpn~;
}
$pagetxtindex .= qq~$pageindexadd~;
$pageindex1 = qq~$pagetxtindexst$pagetxtindex~;
$pageindex2 = qq~$pagetxtindexst$pagetxtindex~;
}
else {
$pagedropindex1 = q~~;
$pagedropindex1 .=
qq~
~;
$pagedropindex2 = $pagedropindex1;
$tstart = $start;
if ( substr( $INFO{'start'}, 0, 3 ) eq 'all' ) {
( $tstart, $start ) = split /\-/xsm, $INFO{'start'};
}
$d_indexpages = $pagenumb / $dropdisplaynum;
$i_indexpages = int( $pagenumb / $dropdisplaynum );
if ( $d_indexpages > $i_indexpages ) {
$indexpages = int( $pagenumb / $dropdisplaynum ) + 1;
}
else { $indexpages = int( $pagenumb / $dropdisplaynum ) }
$selectedindex =
int( ( $start / $MembersPerPage ) / $dropdisplaynum );
if ( $pagenumb > $dropdisplaynum ) {
$pagedropindex1 .=
qq~\n~;
}
$pagedropindex1 .=
q~ ~;
$pagedropindex2 .=
q~ ~;
$tmpMembersPerPage = $MembersPerPage;
if ( substr( $INFO{'start'}, 0, 3 ) eq 'all' ) {
$MembersPerPage = $MembersPerPage * $dropdisplaynum;
}
$prevpage = $start - $tmpMembersPerPage;
$nextpage = $start + $MembersPerPage;
$pagedropindexpvbl =
qq~
~;
$pagedropindexnxbl =
qq~
~;
if ( $start < $MembersPerPage ) {
$pagedropindexpv .=
qq~
~;
}
else {
$pagedropindexpv .=
qq~
~;
}
if ( $nextpage > $lastptn ) {
$pagedropindexnx .=
qq~
~;
}
else {
$pagedropindexnx .=
qq~
~;
}
$pageindex1 = qq~$pagedropindex1~;
$pageindex2 = qq~$pagedropindex2~;
$pageindexjs = qq~
~;
}
}
}
return;
}
sub buildPages {
my ($inp) = @_;
$FindForm .= $my_findform;
$SortJump .= qq(
);
if ( $showuserpicml && $allowpics ) {
$headertop = 8;
}
else {
$headertop = 7;
}
my $additional_headers;
$headercount = $headertop;
if ($extendedprofiles) {
require Sources::ExtendedProfiles;
$additional_headers = ext_memberlist_tableheader();
$headercount += ext_memberlist_get_headercount($additional_headers);
}
if ( $showuserpicml && $allowpics ) {
$row_userpic = $my_row_userpic;
$col_userpic = q~~;
}
else {
$row_userpic = q{};
$col_userpic = q{};
}
$TableHeader .= $my_header;
$TableHeader =~ s/{yabb row_userpic}/$row_userpic/sm;
$TableHeader =~ s/{yabb selUser}/$selUser/sm;
$TableHeader =~ s/{yabb selPos}/$selPos/sm;
$TableHeader =~ s/{yabb selPost}/$selPost/sm;
$TableHeader =~ s/{yabb selReg}/$selReg/sm;
$TableHeader =~ s/{yabb add_headers}/$additional_headers/sm;
if ( $LetterLinks ne q{} ) {
$TableHeader .= $my_letterlinks;
$TableHeader =~ s/{yabb letterlinks}/$LetterLinks/sm;
$TableHeader =~ s/{yabb headercount}/$headercount/sm;
}
$numbegin = ( $start + 1 );
$numend = ( $start + $MembersPerPage );
if ( $numend > $memcount ) { $numend = $memcount; }
if ( $memcount == 0 ) { $numshow = q{}; }
else { $numshow = qq~($numbegin - $numend $ml_txt{'309'} $memcount)~; }
if ($inp) {
$yynavigation = qq~› $ml_txt{'331'} $numshow~;
$yymain .= qq~$my_memberlist_main
$TableHeader
~;
$yymain =~ s/{yabb col_userpic}/$col_userpic/sm;
$yymain =~ s/{yabb pageindex1}/$pageindex1/sm;
$yymain =~ s/{yabb findform}/$FindForm/sm;
$yymain =~ s/{yabb sortjump}/$SortJump/sm;
}
else {
$yymain .= $my_memberlist_bottom;
$yymain =~ s/{yabb headercount}/$headercount/gsm;
$yymain =~ s/{yabb pageindex2}/$pageindex2/sm;
$yymain =~ s/{yabb dr_warning}/$dr_warning/sm;
$yymain =~ s/{yabb pageindexjs}/$pageindexjs/sm;
}
return;
}
sub FindMembers {
$SearchStr = $FORM{'member'} || $INFO{'member'};
$LookFor = qq~^$SearchStr\$~;
$LookFor =~ s/\*+/.*?/gsm;
ManageMemberinfo('load');
my %memberfind = ();
while ( ( $membername, $value ) = each %memberinf ) {
( $memrealname, $mememail, undef ) = split /\|/xsm, $value, 3;
if ( $memrealname =~ /$LookFor/ism ) {
$memberfind{$membername} = $memrealname;
}
elsif ( $mememail =~ /$LookFor/ism ) {
if ( !$iamadmin && !$iamgmod ) { LoadUser($membername); }
if ( $iamadmin || $iamgmod || !${ $uid . $membername }{'hidemail'} )
{
$memberfind{$membername} = $memrealname;
}
}
}
@findmemlist =
sort { lc $memberfind{$a} cmp lc $memberfind{$b} } keys %memberfind;
undef %memberfind;
$memcount = @findmemlist;
buildIndex();
buildPages(1);
if ( $memcount > 0 ) {
my $i = $start;
$numshown = 0;
while ( $numshown < $MembersPerPage ) {
chomp $findmemlist[$i];
showRows( $findmemlist[$i] );
$numshown++;
$i++;
}
}
else {
$yymain .= $my_findmember;
$yymain =~ s/{yabb formmember}/$FORM{'member'}/sm;
}
undef @findmemlist;
undef %memberinf;
buildPages(0);
$yytitle = "$ml_txt{'313'} $ml_txt{'4'} $ml_txt{'87'} $numshow";
template();
return;
}
1;