Files
tyforum/script/user_info.pl

608 lines
18 KiB
Perl
Executable File

#!/usr/bin/perl
#------------------------------------------------------------------------------
# mwForum - Web-based discussion forum
# Copyright (c) 1999-2015 Markus Wichitill
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#------------------------------------------------------------------------------
use strict;
use warnings;
no warnings qw(uninitialized redefine);
# Imports
use TyfMain;
#------------------------------------------------------------------------------
# Init
my ( $m, $cfg, $lng, $user, $userId ) = TyfMain->new( $_[0] );
# Check if access should be denied
!$cfg->{userInfoReg} || $userId or $m->error('errNoAccess');
# Get CGI parameters
my $infUserId = $m->paramInt('uid');
# Get user
my $infUser = $m->getUser($infUserId);
$infUser or $m->error('errUsrNotFnd');
my $userTitle =
$infUser->{title} ? $m->formatUserTitle( $infUser->{title} ) : "";
# Get GeoIP data
my $countryCode = "";
my $geoLocation = "";
if ( $cfg->{geoIp} && ( !$infUser->{privacy} || $user->{admin} ) ) {
my $ip =
$infUser->{lastIp}; # GeoIP doesn't like the tied hash when using Pg
my $geoIp = undef;
if ( eval { require Geo::IP } ) {
$geoIp = Geo::IP->open( $cfg->{geoIp} );
}
elsif ( eval { require Geo::IP::PurePerl } ) {
$geoIp = Geo::IP::PurePerl->open( $cfg->{geoIp} );
}
else {
$m->error("Module required for GeoIP not available.");
}
$geoIp or $m->error("Creating GeoIP object failed.");
if ( index( $cfg->{geoIp}, 'City' ) > -1 ) {
my $rec = $geoIp->record_by_addr($ip);
$rec
or $m->logError( "Fetching record from GeoLiteCity database failed.",
1 );
if ($rec) {
$countryCode = lc( $rec->country_code() );
$geoLocation = join(
", ",
grep( $_,
$rec->city(), $rec->region_name(), $rec->country_name() )
);
}
}
else {
$countryCode = lc( $geoIp->country_code_by_addr($ip) );
$geoLocation = $geoIp->country_name_by_addr($ip);
}
}
# Set Javascript parameters
my $jsParams = {};
if ( $cfg->{userInfoMap} ) {
$jsParams = {
location => $infUser->{location} || $geoLocation,
countryCode => $countryCode,
uaLangCode => $m->{uaLangCode},
lng_uifMapOthrMt => $lng->{uifMapOthrMt},
};
}
# Print header
$m->printHeader( undef, $jsParams );
# User button links
my @userLinks = ();
push @userLinks,
{
url => $m->url( 'message_add', uid => $infUserId ),
txt => 'uifMessage',
ico => 'write'
}
if $userId && $cfg->{messages};
push @userLinks,
{
url => $m->url( 'user_ignore', userId => $infUserId ),
txt => 'uifIgnore',
ico => 'ignore'
}
if $userId;
push @userLinks,
{
url => $m->url( 'user_watch', userId => $infUserId ),
txt => 'uifWatch',
ico => 'watch'
}
if $userId && $cfg->{watchUsers};
push @userLinks,
{
url => $m->url( 'forum_search', uid => $infUserId ),
txt => 'uifListPst',
ico => 'search'
}
if $cfg->{forumSearch};
push @userLinks,
{
url => $m->url( 'user_activity', uid => $infUserId ),
txt => 'uifActiv',
ico => 'poll'
}
if ( $cfg->{statForumActiv} || $user->{admin} ) && !$m->{sqlite};
for my $plugin ( @{ $cfg->{includePlg}{userUserLink} } ) {
$m->callPlugin( $plugin, links => \@userLinks, user => $infUser );
}
# Admin button links
my @adminLinks = ();
if ( $user->{admin} ) {
push @adminLinks,
{
url => $m->url( 'user_admopt', uid => $infUserId ),
txt => "Admin",
ico => 'admopt'
}
if $user->{admin};
push @adminLinks,
{
url => $m->url( 'user_profile', uid => $infUserId, ori => 1 ),
txt => "Profile",
ico => 'profile'
};
push @adminLinks,
{
url => $m->url( 'user_options', uid => $infUserId, ori => 1 ),
txt => "Options",
ico => 'options'
};
push @adminLinks,
{
url => $m->url( 'user_groups', uid => $infUserId, ori => 1 ),
txt => "Groups",
ico => 'group'
};
push @adminLinks,
{
url => $m->url( 'user_migrate', uid => $infUserId ),
txt => "Migrate",
ico => 'merge'
};
push @adminLinks,
{
url => $m->url( 'user_notify', uid => $infUserId ),
txt => "Notify",
ico => 'write'
};
push @adminLinks,
{
url => $m->url( 'user_ban', uid => $infUserId ),
txt => "Ban",
ico => 'ban'
};
push @adminLinks,
{
url => $m->url( 'user_wipe', uid => $infUserId ),
txt => "Wipe",
ico => 'wipe'
};
push @adminLinks,
{
url => $m->url(
'user_confirm',
uid => $infUserId,
script => 'user_delete',
name => $infUser->{userName}
),
txt => "Delete",
ico => 'delete'
};
for my $plugin ( @{ $cfg->{includePlg}{userAdminLink} } ) {
$m->callPlugin( $plugin, links => \@userLinks, user => $infUser );
}
}
# Print page bar
my @navLinks =
( { url => $m->url('forum_show'), txt => 'comUp', ico => 'up' } );
$m->printPageBar(
mainTitle => $lng->{uifTitle},
subTitle => "$infUser->{userName} $userTitle",
navLinks => \@navLinks,
userLinks => \@userLinks,
adminLinks => \@adminLinks
);
# Notices for admins
if ( $user->{admin} ) {
# Print whether user is banned
my $ban = $m->fetchHash( "
SELECT reason, intReason FROM userBans WHERE userId = ?", $infUserId );
print
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\"><em>Banned</em></span></div>\n",
"<div class=\"ccl\">\n",
$ban->{reason} ? "<p>Reason: $ban->{reason}</p>\n" : "",
$ban->{intReason} ? "<p>Internal reason: $ban->{intReason}</p>\n" : "",
"</div>\n", "</div>\n\n"
if $ban;
# Print admin comments
print
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">Administrator Comments</span></div>\n",
"<div class=\"ccl\">\n",
$infUser->{comment}, "\n", "</div>\n", "</div>\n\n"
if $infUser->{comment};
}
# Print user profile
print
"<table class=\"tbl\">\n",
"<tr class=\"hrw\"><th colspan=\"2\">$lng->{uifProfTtl}</th></tr>\n";
# Avatar
if ( $cfg->{avatars} && $infUser->{avatar} ) {
my $avatarUrl = "";
if ( index( $infUser->{avatar}, "gravatar:" ) == 0 ) {
my $md5 = $m->md5( substr( $infUser->{avatar}, 9 ) );
$avatarUrl = "//gravatar.com/avatar/$md5?s=$cfg->{avatarWidth}";
}
else {
$avatarUrl = "$cfg->{attachUrlPath}/avatars/$infUser->{avatar}";
}
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfAvat}</td><td><img src=\"$avatarUrl\" alt=\"\"></td>\n",
"</tr>\n"
if $avatarUrl;
}
# Email address
if ( $infUser->{email} && $user->{admin} ) {
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">Email</td><td>$infUser->{email}</td>\n",
"</tr>\n";
}
# Real name
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfRName}</td><td>$infUser->{realName}</td>\n",
"</tr>\n"
if $infUser->{realName};
# OpenID
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">OpenID</td>\n",
"<td><img class=\"bic bic_openid\" src=\"$cfg->{dataPath}/epx.png\" title=\"OpenID\" alt=\"\">",
" <a href=\"$infUser->{openId}\">$infUser->{openId}</a></td>\n", "</tr>\n"
if $infUser->{openId};
# Homepage
if ( my $homepage = $infUser->{homepage} ) {
$homepage =~ s!(https?://[^\\\s\[\]{}<>|^)`'"]+)!<a href="$1">$1</a>!g;
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfPage}</td><td>$homepage</td>\n",
"</tr>\n";
}
# Birthday
my $birthdate = "";
$birthdate = $infUser->{birthyear} . "-" if $infUser->{birthyear};
$birthdate .= $infUser->{birthday};
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfBdate}</td><td>$birthdate</td>\n", "</tr>\n"
if $birthdate;
# Occupation
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfOccup}</td><td>$infUser->{occupation}</td>\n",
"</tr>\n"
if $infUser->{occupation};
# Hobbies
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfHobby}</td><td>$infUser->{hobbies}</td>\n",
"</tr>\n"
if $infUser->{hobbies};
# Location
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfLocat}</td><td>$infUser->{location}</td>\n",
"</tr>\n"
if $infUser->{location};
# GeoIP
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfGeoIp}</td>\n",
$cfg->{userFlags}
? "<td>$geoLocation <img class=\"flg\" src=\"$cfg->{dataPath}/flags/$countryCode.png\" alt=\"\"></td>"
: "<td>$geoLocation</td>\n", "</tr>\n"
if $countryCode;
# Messengers
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfIcq}</td><td>$infUser->{icq}</td>\n",
"</tr>\n"
if $infUser->{icq} && $userId;
# Former usernames
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfOName}</td><td>$infUser->{oldNames}</td>\n",
"</tr>\n"
if $infUser->{oldNames};
# Custom user fields
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$cfg->{extra1}</td><td>$infUser->{extra1}</td>\n",
"</tr>\n"
if length( $infUser->{extra1} )
&& $cfg->{extra1}
&& ( $cfg->{showExtra1} || $user->{admin} );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$cfg->{extra2}</td><td>$infUser->{extra2}</td>\n",
"</tr>\n"
if length( $infUser->{extra2} )
&& $cfg->{extra2}
&& ( $cfg->{showExtra2} || $user->{admin} );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$cfg->{extra3}</td><td>$infUser->{extra3}</td>\n",
"</tr>\n"
if length( $infUser->{extra3} )
&& $cfg->{extra3}
&& ( $cfg->{showExtra3} || $user->{admin} );
# Signature
if ( $infUser->{signature} ) {
my $fakePost = { body => $infUser->{signature} };
$m->dbToDisplay( {}, $fakePost );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfSig}</td><td>$fakePost->{body}</td>\n",
"</tr>\n";
}
# Blurb
if ( $infUser->{blurb} ) {
my $fakePost = { isBlurb => 1, body => $infUser->{blurb} };
$m->dbToDisplay( {}, $fakePost );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifProfBlurb}</td><td>$fakePost->{body}</td>\n",
"</tr>\n";
}
print "</table>\n\n";
# Call user info include plugin
for my $plugin ( @{ $cfg->{includePlg}{userInfo} } ) {
$m->callPlugin( $plugin, user => $infUser );
}
# Google map
if ( $cfg->{userInfoMap} && ( $infUser->{location} || $geoLocation ) ) {
print
"<div class=\"frm\">\n",
"<div class=\"hcl\">\n",
"<span class=\"htt\">$lng->{uifMapTtl}</span>\n",
"<a class=\"clk\" id=\"loc\"></a>\n",
"</div>\n",
"<div class=\"ccl\">\n",
"<div id=\"map\" style=\"width: 98%; height: 350px; max-width: 600px\"></div>\n",
"</div>\n",
"</div>\n\n",
"<script src=\"//maps.googleapis.com/maps/api/js?v=3&amp;sensor=false\"></script>\n",
"<script src=\"$cfg->{dataPath}/google.js\"></script>\n\n";
}
# Print public user stats
print
"<table class=\"tbl\">\n",
"<tr class=\"hrw\"><th colspan=\"2\">$lng->{uifStatTtl}</th></tr>\n";
# Number of posts
my $postExistNum = $m->fetchArray( "
SELECT COUNT(*) FROM posts WHERE userId = ?", $infUserId );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifStatPNum}</td>\n",
"<td>$infUser->{postNum} $lng->{uifStatPONum}, $postExistNum $lng->{uifStatPENum}",
@{ $cfg->{userRanks} } ? $m->formatUserRank( $infUser->{postNum} ) : "",
"</td>\n",
"</tr>\n";
# Registration time
my $regTimeStr = $m->formatTime( $infUser->{regTime}, $user->{timezone} );
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifStatRegTm}</td><td>$regTimeStr</td>\n",
"</tr>\n";
# Last-on and previous-on time
if ( $userId == $infUserId || $user->{admin} ) {
my $lastOnTimeStr =
$infUser->{lastOnTime}
? $m->formatTime( $infUser->{lastOnTime}, $user->{timezone} )
: " - ";
my $prevOnTimeStr =
$infUser->{prevOnTime}
? $m->formatTime( $infUser->{prevOnTime}, $user->{timezone} )
: " - ";
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifStatLOTm}</td><td>$lastOnTimeStr</td>\n",
"</tr>\n",
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifStatLRTm}</td><td>$prevOnTimeStr</td>\n",
"</tr>\n";
}
# Admin-only user stats
if ( $user->{admin} ) {
my $lastIpStr = $infUser->{lastIp} ? $infUser->{lastIp} : " - ";
$lastIpStr .= " (" . $m->escHtml( $infUser->{host} ) . ")"
if $infUser->{host};
my $ignoredNum = $m->fetchArray( "
SELECT COUNT(*) FROM userIgnores WHERE ignoredId = ?", $infUserId );
my $watchedNum = $m->fetchArray( "
SELECT COUNT(*) FROM watchUsers WHERE watchedId = ?", $infUserId );
my $userAgentStr = $infUser->{userAgent} ? $infUser->{userAgent} : " - ";
print
"<tr class=\"crw\">\n",
"<td class=\"hco\">$lng->{uifStatLIp}</td><td>$lastIpStr</td>\n",
"</tr>\n",
"<tr class=\"crw\">\n",
"<td class=\"hco\">User Agent</td><td>$userAgentStr</td>\n",
"</tr>\n",
"<tr class=\"crw\">\n",
"<td class=\"hco\">Ignored By</td><td>$ignoredNum users</td>\n",
"</tr>\n",
"<tr class=\"crw\">\n",
"<td class=\"hco\">Watched By</td><td>$watchedNum users</td>\n",
"</tr>\n",
"<tr class=\"crw\">\n",
"<td class=\"hco\">Bounce Counter</td><td>$infUser->{bounceNum}</td>\n",
"</tr>\n";
}
print "</table>\n\n";
# Print badges
if ( @{ $cfg->{badges} } ) {
my $userBadges = $m->fetchAllArray( "
SELECT badge FROM userBadges WHERE userId = ?", $infUserId );
if (@$userBadges) {
my @badges = ();
for my $line ( @{ $cfg->{badges} } ) {
my ( $id, $bigIcon, $title, $desc ) =
$line =~ /(\w+)\s+\w+\s+\S+\s+(\S+)\s+"([^"]+)"\s+"([^"]+)/;
push @badges, [ $id, $title, $bigIcon, $desc ];
}
print
"<table class=\"tbl\">\n",
"<tr class=\"hrw\">\n",
"<th colspan=\"2\">$lng->{uifBadges}</th>\n",
"</tr>\n";
for my $badge (@badges) {
for my $userBadge (@$userBadges) {
if ( $userBadge->[0] eq $badge->[0] ) {
my $url = "$cfg->{dataPath}/$badge->[2]";
print
"<tr class=\"crw\">\n",
"<td class=\"hco\"><img class=\"uba\" src=\"$url\" alt=\"\"> $badge->[1]</td>\n",
"<td>$badge->[3]</td>\n",
"</tr>\n";
}
}
}
print "</table>\n\n";
}
}
# Print non-public admin and member status info
if ( $userId == $infUserId || $user->{admin} ) {
# Get groups
my $groups = $m->fetchAllArray( "
SELECT groups.id, groups.title,
CASE WHEN groupAdmins.userId IS NOT NULL THEN '\@' ELSE '' END
FROM groups AS groups
INNER JOIN groupMembers AS groupMembers
ON groupMembers.userId = :infUserId
AND groupMembers.groupId = groups.id
LEFT JOIN groupAdmins AS groupAdmins
ON groupAdmins.userId = :infUserId
AND groupAdmins.groupId = groups.id
ORDER BY groups.title",
{ infUserId => $infUserId } );
# Print groups
print
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">$lng->{uifGrpMbrTtl}</span></div>\n",
"<div class=\"ccl\">\n",
join(
",\n",
map( "<a href=\""
. $m->url( 'group_info', gid => $_->[0] )
. "\">$_->[2]$_->[1]</a>",
@$groups )
)
|| " - ", "\n",
"</div>\n",
"</div>\n\n";
# Get subscribed boards
my $boards = $m->fetchAllArray( "
SELECT boards.id, boards.title
FROM boardSubscriptions AS boardSubscriptions
INNER JOIN boards AS boards
ON boards.id = boardSubscriptions.boardId
INNER JOIN categories AS categories
ON categories.id = boards.categoryId
WHERE boardSubscriptions.userId = :infUserId
ORDER BY categories.pos, boards.pos",
{ infUserId => $infUserId } );
# Print subscribed boards
print
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">$lng->{uifBrdSubTtl}</span></div>\n",
"<div class=\"ccl\">\n",
join(
",\n",
map( "<a href=\""
. $m->url( 'board_info', bid => $_->[0] )
. "\">$_->[1]</a>",
@$boards )
)
|| " - ", "\n",
"</div>\n",
"</div>\n\n";
# Get subscribed topics
my $topics = $m->fetchAllArray( "
SELECT topics.id, topics.subject
FROM topicSubscriptions AS topicSubscriptions
INNER JOIN topics AS topics
ON topics.id = topicSubscriptions.topicId
WHERE topicSubscriptions.userId = :infUserId
ORDER BY topics.id DESC",
{ infUserId => $infUserId } );
# Print subscribed topics
print
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">$lng->{uifTpcSubTtl}</span></div>\n",
"<div class=\"ccl\">\n",
join(
",\n",
map( "<a href=\""
. $m->url( 'topic_show', tid => $_->[0] )
. "\">$_->[1]</a>",
@$topics )
)
|| " - ", "\n",
"</div>\n",
"</div>\n\n";
}
# Log action and finish
$m->logAction( 3, 'user', 'info', $userId, 0, 0, 0, $infUserId );
$m->printFooter();
$m->finish();