mirror of
https://github.com/OPSnet/Gazelle.git
synced 2026-01-16 18:04:34 -05:00
642 lines
25 KiB
PHP
642 lines
25 KiB
PHP
<?php
|
|
// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
|
|
/** @phpstan-var \Gazelle\User $Viewer */
|
|
/** @phpstan-var \Gazelle\Cache $Cache */
|
|
/** @phpstan-var \Twig\Environment $Twig */
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Gazelle;
|
|
|
|
use Gazelle\Enum\UserAuditEvent;
|
|
use Gazelle\Enum\UserStatus;
|
|
use Gazelle\Util\Mail;
|
|
use Gazelle\Util\Time;
|
|
|
|
authorize();
|
|
|
|
function enabledStatus(string $status): string {
|
|
return match ($status) {
|
|
'0' => 'Disabled',
|
|
'1' => 'Enabled',
|
|
default => $status,
|
|
};
|
|
}
|
|
|
|
function revoked(bool $state): string {
|
|
return $state ? 'revoked' : 'restored';
|
|
}
|
|
|
|
if (!$Viewer->permitted('users_mod')) {
|
|
Error403::error();
|
|
}
|
|
|
|
$userMan = new Manager\User();
|
|
$user = $userMan->findById((int)$_POST['userid']);
|
|
if (is_null($user)) {
|
|
header("Location: log.php?search=User+" . (int)$_POST['userid']);
|
|
exit;
|
|
}
|
|
if ($_POST['checkpoint'] != $user->checkpoint()) {
|
|
Error400::error(
|
|
"Somebody else has moderated this user since you loaded it. Please go back and refresh the page."
|
|
);
|
|
}
|
|
|
|
$ownProfile = $user->id === $Viewer->id;
|
|
|
|
// Variables for database input
|
|
$class = (int)($_POST['Class'] ?? 0);
|
|
$title = trim($_POST['Title']);
|
|
$adminComment = trim($_POST['admincomment'] ?? '');
|
|
$visible = isset($_POST['Visible']) ? '1' : '0';
|
|
$unlimitedDownload = isset($_POST['unlimitedDownload']);
|
|
$invites = (int)$_POST['Invites'];
|
|
$slogan = trim($_POST['slogan']);
|
|
$changePassword = isset($_POST['ChangePassword']);
|
|
$uploaded = 0;
|
|
$downloaded = 0;
|
|
$bonusPoints = null;
|
|
|
|
if (isset($_POST['Uploaded']) && isset($_POST['Downloaded'])) {
|
|
$uploaded = max(0, byte_arithmetic($_POST['Uploaded']));
|
|
$downloaded = max(0, byte_arithmetic($_POST['Downloaded']));
|
|
}
|
|
if (isset($_POST['BonusPoints'])) {
|
|
$bonusPoints = (float)$_POST['BonusPoints'];
|
|
}
|
|
$Collages = (int)($_POST['Collages'] ?? 0);
|
|
$flTokens = (int)($_POST['FLTokens'] ?? 0);
|
|
|
|
$userReason = trim($_POST['UserReason']);
|
|
$disableAvatar = isset($_POST['DisableAvatar']);
|
|
$disableForums = isset($_POST['DisableForums']);
|
|
$disableInvites = isset($_POST['DisableInvites']);
|
|
$disableIRC = isset($_POST['DisableIRC']);
|
|
$disableLeech = isset($_POST['DisableLeech']) ? 0 : 1;
|
|
$disablePM = isset($_POST['DisablePM']);
|
|
$disablePoints = isset($_POST['DisablePoints']);
|
|
$disablePosting = isset($_POST['DisablePosting']);
|
|
$disableRequests = isset($_POST['DisableRequests']);
|
|
$disableTagging = isset($_POST['DisableTagging']);
|
|
$disableUpload = isset($_POST['DisableUpload']);
|
|
$disableWiki = isset($_POST['DisableWiki']);
|
|
$editWikiEditReadable = isset($_POST['wiki-edit-readable']);
|
|
$resetRatioWatch = $_POST['ResetRatioWatch'] ?? 0 ? 1 : 0;
|
|
$resetIPHistory = $_POST['ResetIPHistory'] ?? 0;
|
|
$resetPasskey = isset($_POST['ResetPasskey']) ? 1 : 0;
|
|
$resetAuthkey = isset($_POST['ResetAuthkey']) ? 1 : 0;
|
|
$logoutSession = isset($_POST['Logout']) ? 1 : 0;
|
|
$sendHackedMail = isset($_POST['SendHackedMail']) ? 1 : 0;
|
|
if ($sendHackedMail && isset($_POST['HackedEmail'])) {
|
|
$hackedEmail = trim($_POST['HackedEmail']);
|
|
} else {
|
|
$sendHackedMail = false;
|
|
}
|
|
$mergeStatsFrom = trim($_POST['MergeStatsFrom']);
|
|
$reason = trim($_POST['Reason']);
|
|
|
|
$cur = $user->info();
|
|
$cur['PermittedForums'] = $user->privilege()->permittedUserForums();
|
|
$cur['RestrictedForums'] = $user->privilege()->forbiddenUserForums();
|
|
|
|
if ($mergeStatsFrom && ($downloaded != $user->downloadedSize() || $uploaded != $user->uploadedSize())) {
|
|
// Too make make-work code to deal with this unlikely eventuality
|
|
Error400::error("Do not transfer buffer and edit upload/download in the same operation.");
|
|
}
|
|
|
|
$tracker = new Tracker();
|
|
$needTrackerAdd = false;
|
|
$needTrackerRefresh = false;
|
|
|
|
// If we're deleting the user, we can ignore all the other crap
|
|
if ($_POST['UserStatus'] === 'delete' && $Viewer->permitted('users_delete_users')) {
|
|
$tracker->logger()->general(
|
|
"User account {$user->label()} was deleted by {$Viewer->username()}"
|
|
);
|
|
$tracker->removeUser($user);
|
|
$user->remove();
|
|
header("Location: log.php?search=User+{$user->id}");
|
|
exit;
|
|
}
|
|
|
|
// User was not deleted. Perform other stuff.
|
|
|
|
$editSummary = [];
|
|
|
|
$lockType = (int)$_POST['LockType'];
|
|
if ($user->lockType() != $lockType) {
|
|
// This is a pseudo-field that does not exist in the table,
|
|
// the modify() method knows how to deal with it.
|
|
$user->setField('lock-type', $lockType);
|
|
if (!$lockType) {
|
|
$editSummary[] = "Account unlocked";
|
|
} else {
|
|
$editSummary[] = $user->isLocked()
|
|
? "Account lock type changed to $lockType"
|
|
: "Account locked (type $lockType)";
|
|
}
|
|
}
|
|
|
|
if (isset($_POST['ResetRatioWatch']) && $Viewer->permitted('users_edit_reset_keys')) {
|
|
new User\History($user)->resetRatioWatch();
|
|
$editSummary[] = 'RatioWatch history reset';
|
|
}
|
|
|
|
if ($resetIPHistory && $Viewer->permitted('users_edit_reset_keys')) {
|
|
new User\History($user)->resetIp();
|
|
$editSummary[] = 'IP history cleared';
|
|
}
|
|
|
|
if (isset($_POST['ResetEmailHistory']) && $Viewer->permitted('users_edit_reset_keys')) {
|
|
new User\History($user)->resetEmail($user->username() . '@' . SITE_HOST, $resetIPHistory ? '127.0.0.1' : $user->ipaddr());
|
|
$editSummary[] = 'email history cleared';
|
|
}
|
|
|
|
if (isset($_POST['ResetSnatchList']) && $Viewer->permitted('users_edit_reset_keys')) {
|
|
new User\History($user)->resetSnatched();
|
|
$editSummary[] = 'snatch list cleared';
|
|
}
|
|
|
|
if (isset($_POST['ResetDownloadList']) && $Viewer->permitted('users_edit_reset_keys')) {
|
|
new User\History($user)->resetDownloaded();
|
|
$editSummary[] = 'download list cleared';
|
|
}
|
|
|
|
if ($logoutSession && $Viewer->permitted('users_logout')) {
|
|
$editSummary[] = "logged out of all sessions (n=" . new User\Session($user)->dropAll() . ")";
|
|
}
|
|
|
|
if ($visible != $user->isVisible() && $Viewer->permitted('users_make_invisible')) {
|
|
$user->setField('Visible', $visible ? '1' : '0');
|
|
$needTrackerRefresh = true;
|
|
$editSummary[] = 'swarm visibility ' . ($visible ? 'on' : 'off');
|
|
}
|
|
|
|
if ($slogan != $user->slogan() && ($Viewer->permitted('admin_manage_fls') || $ownProfile)) {
|
|
$user->setField('slogan', $slogan);
|
|
$editSummary[] = "First-Line Support status changed to \"$slogan\"";
|
|
}
|
|
|
|
$editRatio = $Viewer->permitted('users_edit_ratio') || ($Viewer->permitted('users_edit_own_ratio') && $ownProfile);
|
|
if ($flTokens != $user->tokenCount() && ($editRatio || $Viewer->permitted('admin_manage_user_fls'))) {
|
|
$editSummary[] = "freeleech tokens changed from {$user->tokenCount()} to $flTokens";
|
|
}
|
|
|
|
$newBonusPoints = false;
|
|
if (
|
|
!in_array($bonusPoints, [$user->bonusPointsTotal(), (float)($_POST['OldBonusPoints'])])
|
|
&& ($Viewer->permitted('users_edit_ratio') || ($Viewer->permitted('users_edit_own_ratio') && $ownProfile))
|
|
) {
|
|
$newBonusPoints = $bonusPoints;
|
|
$editSummary[] = "bonus points changed from {$user->bonusPointsTotal()} to {$bonusPoints}";
|
|
}
|
|
|
|
if ($unlimitedDownload !== $user->hasAttr('unlimited-download') && $Viewer->permitted('admin_rate_limit_manage')) {
|
|
if ($user->toggleAttr('unlimited-download', $unlimitedDownload)) {
|
|
$editSummary[] = "unlimited download " . strtolower(enabledStatus($unlimitedDownload ? '1' : '0'));
|
|
}
|
|
}
|
|
|
|
if (
|
|
$Collages != $user->paidPersonalCollages() && $Collages != (int)$_POST['OldCollages']
|
|
&& ($Viewer->permitted('users_edit_ratio') || ($Viewer->permitted('users_edit_own_ratio') && $ownProfile))
|
|
) {
|
|
$user->setField('collage_total', $Collages);
|
|
$user->ordinal()->set('personal-collage', $Collages);
|
|
$EditSummary[] = "personal collages changed from {$user->paidPersonalCollages()} to {$Collages}";
|
|
}
|
|
|
|
if ($invites != $user->unusedInviteTotal() && $Viewer->permitted('users_edit_invites')) {
|
|
$user->setField('Invites', $invites);
|
|
$editSummary[] = "number of invites changed from {$user->unusedInviteTotal()} to $invites";
|
|
}
|
|
|
|
if ($editRatio) {
|
|
if ($uploaded != $user->uploadedSize() && $uploaded != $_POST['OldUploaded']) {
|
|
$user->setField('leech_upload', $uploaded);
|
|
$editSummary[] = "uploaded changed from " . byte_format($user->uploadedSize())
|
|
. ' to ' . byte_format($uploaded)
|
|
. " (delta " . byte_format($uploaded - $user->uploadedSize()) . ")";
|
|
}
|
|
if ($downloaded != $user->downloadedSize() && $downloaded != $_POST['OldDownloaded']) {
|
|
$user->setField('leech_download', $downloaded);
|
|
$editSummary[] = "downloaded changed from " . byte_format($user->downloadedSize())
|
|
. ' to ' . byte_format($downloaded)
|
|
. " (delta " . byte_format($downloaded - $user->downloadedSize()) . ")";
|
|
}
|
|
}
|
|
|
|
if ($class) {
|
|
$Classes = $userMan->classList();
|
|
$newClass = $Classes[$class]['Level'];
|
|
if (
|
|
$newClass != $user->classLevel()
|
|
&& (
|
|
($newClass < $Viewer->classLevel() && $Viewer->permitted('users_promote_below'))
|
|
|| ($newClass <= $Viewer->classLevel() && $Viewer->permitted('users_promote_to'))
|
|
)
|
|
) {
|
|
$user->setField('PermissionID', $class);
|
|
$editSummary[] = 'class changed to ' . $userMan->userclassName($class);
|
|
|
|
if ($user->supportCount($class, $user->primaryClass()) === 2) {
|
|
if ($newClass < $user->primaryClass()) {
|
|
$slogan = null;
|
|
}
|
|
$Cache->delete_value('staff_ids');
|
|
}
|
|
$Cache->delete_value("donor_info_{$user->id}");
|
|
}
|
|
}
|
|
|
|
if ($Viewer->permitted('users_edit_usernames')) {
|
|
$username = trim($_POST['Username']);
|
|
if ($username !== $user->username()) {
|
|
if (in_array($username, ['0', '1'])) {
|
|
Error400::error('You cannot set a username of "0" or "1".');
|
|
} elseif (strtolower($username) !== strtolower($user->username())) {
|
|
$found = $userMan->findByUsername($username);
|
|
if ($found) {
|
|
Error400::error("Username already in use by $username");
|
|
}
|
|
}
|
|
$user->setField('Username', $username);
|
|
$editSummary[] = "username changed from {$user->username()} to $username";
|
|
}
|
|
}
|
|
|
|
if ($title != $user->title() && $Viewer->permitted('users_edit_titles')) {
|
|
// Using the unescaped value for the test to avoid confusion
|
|
if (mb_strlen($_POST['Title']) > 1024) {
|
|
Error400::error("Custom titles have a maximum length of 1,024 characters.");
|
|
} else {
|
|
$user->setField('Title', $title);
|
|
$editSummary[] = "title changed to [code]{$title}[/code]";
|
|
}
|
|
}
|
|
|
|
if ($Viewer->permitted('users_warn')) {
|
|
$weeks = (int)($_POST['WarnLength'] ?? 0);
|
|
$extend = (int)($_POST['ExtendWarning'] ?? 0);
|
|
$reduce = (int)($_POST['ReduceWarning'] ?? 0);
|
|
$warning = new User\Warning($user);
|
|
if (!isset($_POST['Warned'])) {
|
|
if ($user->isWarned()) {
|
|
$warning->clear();
|
|
$editSummary[] = 'warning removed';
|
|
}
|
|
} elseif ($reduce) {
|
|
$warning->clear(); // replace the current warning with the new duration
|
|
$duration = 'week' . plural($reduce);
|
|
$expiry = $warning->add($reason, "$reduce $duration", $Viewer);
|
|
$userMessage = trim($_POST['WarnReason'] ?? '');
|
|
$body = "Your warning has been reduced to $reduce $duration, set to expire at $expiry, by [user]{$Viewer->username()}[/user].";
|
|
if ($userMessage) {
|
|
$body .= " Reason:\n[quote]{$userMessage}[/quote].";
|
|
}
|
|
$user->inbox()->createSystem(
|
|
"Your warning has been reduced to $reduce $duration",
|
|
$body,
|
|
);
|
|
$editSummary[] = "warning reduced by $reduce $duration";
|
|
} elseif ($weeks || $extend) {
|
|
$staffReason = $reason ?: ($extend ? 'warning extension' : 'no reason');
|
|
$weeks = $extend ?: $weeks;
|
|
$user->warn($weeks, $staffReason, $Viewer, $_POST['WarnReason'] ?? 'none given');
|
|
$duration = 'week' . plural($weeks);
|
|
$editSummary[] = "warned for $weeks $duration";
|
|
}
|
|
}
|
|
|
|
$secondaryClasses = array_filter(
|
|
array_map('intval', $_POST['secondary_classes'] ?? [] ),
|
|
fn($id) => $id > 0
|
|
);
|
|
|
|
if ($Viewer->permitted('users_give_donor')) {
|
|
$donor = new User\Donor($user);
|
|
$value = (float)trim($_POST['donation_value']);
|
|
if ($value > 0.0) {
|
|
$donor->donate(
|
|
amount: $value,
|
|
xbtRate: new Manager\XBT()->latestRate('EUR'),
|
|
currency: $_POST['donation_currency'],
|
|
reason: trim($_POST['donation_reason']),
|
|
source: 'Add Points',
|
|
who: $Viewer,
|
|
);
|
|
// pretend the secondary_classes field was checked, otherwise the
|
|
// class will be removed below, and we just added it!
|
|
$secondaryClasses[] = DONOR;
|
|
} else {
|
|
// can add a donation or adjust points, not both
|
|
$rankDelta = (int)$_POST['donor_rank_delta'];
|
|
$totalDelta = (int)$_POST['total_donor_rank_delta'];
|
|
if ($rankDelta || $totalDelta) {
|
|
$donor->adjust(
|
|
rankDelta: $rankDelta,
|
|
totalDelta: $totalDelta,
|
|
reason: trim($_POST['reason']),
|
|
adjuster: $Viewer,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$removedClasses = [];
|
|
$addedClasses = [];
|
|
if ($Viewer->permittedAny('users_promote_below', 'users_promote_to')) {
|
|
$currentClasses = array_keys($user->privilege()->secondaryClassList());
|
|
sort($currentClasses);
|
|
sort($secondaryClasses);
|
|
if ($currentClasses != $secondaryClasses) {
|
|
$removedClasses = array_diff($currentClasses, $secondaryClasses);
|
|
$addedClasses = array_diff($secondaryClasses, $currentClasses);
|
|
if ($removedClasses !== []) {
|
|
$names = array_map(fn (int $c): string => $userMan->userclassName($c), $removedClasses);
|
|
$editSummary[] = 'secondary classes dropped: ' . implode(', ', $names);
|
|
}
|
|
if ($addedClasses !== []) {
|
|
$names = array_map(fn (int $c): string => $userMan->userclassName($c), $addedClasses);
|
|
$editSummary[] = "secondary classes added: " . implode(', ', $names);
|
|
}
|
|
}
|
|
}
|
|
|
|
$forumMan = new Manager\Forum();
|
|
$restricted = array_map('intval', array_unique(explode(',', trim($_POST['RestrictedForums']))));
|
|
sort($restricted);
|
|
$restrictedIds = [];
|
|
$restrictedNames = [];
|
|
foreach ($restricted as $forumId) {
|
|
$forum = $forumMan->findById($forumId);
|
|
if (!is_null($forum) && !isset($restrictedIds[$forumId])) {
|
|
$restrictedIds[$forumId] = true;
|
|
$restrictedNames[] = "{$forum->name()} ($forumId)";
|
|
}
|
|
}
|
|
|
|
$permitted = array_map('intval', array_unique(explode(',', trim($_POST['PermittedForums']))));
|
|
sort($permitted);
|
|
$permittedIds = [];
|
|
$permittedNames = [];
|
|
foreach ($permitted as $forumId) {
|
|
$forum = $forumMan->findById($forumId);
|
|
if (!is_null($forum) && !isset($permittedIds[$forumId])) {
|
|
$permittedIds[$forumId] = true;
|
|
$permittedNames[] = "{$forum->name()} ($forumId)";
|
|
}
|
|
}
|
|
|
|
$privChange = [];
|
|
if ($Viewer->permitted('users_disable_any')) {
|
|
if ($disableLeech != $user->canLeech()) {
|
|
$privChange[] = 'Your leeching privileges have been ' . revoked((bool)$disableLeech);
|
|
$user->setField('can_leech', $disableLeech ? 1 : 0);
|
|
$needTrackerRefresh = true;
|
|
$editSummary[] = "leeching status changed ("
|
|
. enabledStatus($user->canLeech() ? '1' : '0') . " → " . enabledStatus($disableLeech ? '1' : '0') . ")";
|
|
}
|
|
if ($disableInvites !== $user->disableInvites()) {
|
|
$privChange[] = 'Your invite privileges have been ' . revoked($disableInvites);
|
|
$editSummary[] = 'invites privileges ' . revoked($disableInvites);
|
|
$user->toggleAttr('disable-invites', $disableInvites);
|
|
if ($disableInvites) {
|
|
unset($permittedIds[INVITATION_FORUM_ID]);
|
|
$restrictedIds[INVITATION_FORUM_ID] = true;
|
|
$restrictedNames[] = $forumMan->findById(INVITATION_FORUM_ID)->name() . " (" . INVITATION_FORUM_ID . ")";
|
|
}
|
|
}
|
|
if ($disableAvatar !== $user->disableAvatar()) {
|
|
$privChange[] = 'Your avatar privileges have been ' . revoked($disableAvatar);
|
|
$editSummary[] = 'avatar privileges ' . revoked($disableAvatar);
|
|
$user->toggleAttr('disable-avatar', $disableAvatar);
|
|
}
|
|
if ($disablePoints !== $user->disableBonusPoints()) {
|
|
$privChange[] = 'Your bonus points acquisition has been ' . revoked($disablePoints);
|
|
$editSummary[] = 'points privileges ' . revoked($disablePoints);
|
|
$user->toggleAttr('disable-bonus-points', $disablePoints);
|
|
}
|
|
if ($disableTagging !== $user->disableTagging()) {
|
|
$privChange[] = 'Your tagging privileges have been ' . revoked($disableTagging);
|
|
$editSummary[] = 'tagging privileges ' . revoked($disableTagging);
|
|
$user->toggleAttr('disable-tagging', $disableTagging);
|
|
}
|
|
if ($disableUpload !== $user->disableUpload()) {
|
|
$privChange[] = 'Your upload privileges have been ' . revoked($disableUpload);
|
|
$editSummary[] = 'upload privileges ' . revoked($disableUpload);
|
|
$user->toggleAttr('disable-upload', $disableUpload);
|
|
}
|
|
if ($disableWiki !== $user->disableWiki()) {
|
|
$privChange[] = 'Your site editing privileges have been ' . revoked($disableWiki);
|
|
$editSummary[] = 'wiki privileges ' . revoked($disableWiki);
|
|
$user->toggleAttr('disable-wiki', $disableWiki);
|
|
}
|
|
if ($disablePM !== $user->disablePm()) {
|
|
$privChange[] = 'Your private messate (PM) privileges have been ' . revoked($disablePM);
|
|
$editSummary[] = 'PM privileges ' . revoked($disablePM);
|
|
$user->toggleAttr('disable-pm', $disablePM);
|
|
}
|
|
if ($disableRequests !== $user->disableRequests()) {
|
|
$privChange[] = 'Your request privileges have been ' . revoked($disableRequests);
|
|
$editSummary[] = 'request privileges ' . revoked($disableRequests);
|
|
$user->toggleAttr('disable-requests', $disableRequests);
|
|
}
|
|
if ($editWikiEditReadable !== $user->hasAttr('wiki-edit-readable')) {
|
|
$editSummary[] = 'edit any wiki article ' . revoked($editWikiEditReadable);
|
|
$user->toggleAttr('wiki-edit-readable', $editWikiEditReadable);
|
|
}
|
|
}
|
|
|
|
$permittedForums = implode(',', array_keys($permittedIds));
|
|
if ($permittedForums != $cur['PermittedForums']) {
|
|
$user->setField('PermittedForums', $permittedForums);
|
|
$editSummary[] = "permitted forum(s): " . ($permittedForums == '' ? 'none' : implode(', ', $permittedNames));
|
|
}
|
|
$restrictedForums = implode(',', array_keys($restrictedIds));
|
|
if ($restrictedForums != $cur['RestrictedForums']) {
|
|
$user->setField('RestrictedForums', $restrictedForums);
|
|
$editSummary[] = "prohibited forum(s): " . ($restrictedForums == '' ? 'none' : implode(', ', $restrictedNames));
|
|
}
|
|
|
|
if ($Viewer->permitted('users_disable_posts')) {
|
|
if ($disablePosting !== $user->disablePosting()) {
|
|
$privChange[] = 'Your forum posting privileges have been ' . revoked($disablePosting);
|
|
$editSummary[] = 'posting privileges ' . revoked($disablePosting);
|
|
$user->toggleAttr('disable-posting', $disablePosting);
|
|
}
|
|
|
|
if ($disableForums !== $user->disableForums()) {
|
|
$privChange[] = 'Your forum access has been ' . revoked($disableForums);
|
|
$editSummary[] = 'forums privileges ' . revoked($disableForums);
|
|
$user->toggleAttr('disable-forums', $disableForums);
|
|
}
|
|
}
|
|
|
|
if ($disableIRC !== $user->disableIRC()) {
|
|
$privChange[] = 'Your IRC privileges have been ' . revoked($disableIRC);
|
|
$editSummary[] = 'IRC privileges ' . revoked($disableIRC);
|
|
$user->toggleAttr('disable-irc', $disableIRC);
|
|
}
|
|
|
|
if ($privChange && $userReason) {
|
|
sort($privChange);
|
|
$user->inbox()->createSystem(
|
|
count($privChange) == 1 ? $privChange[0] : 'Multiple privileges have changed on your account',
|
|
$Twig->render('user/pm-privilege.twig', [
|
|
'privs' => $privChange,
|
|
'reason' => $userReason,
|
|
'url' => 'wiki.php?action=article&id=5',
|
|
])
|
|
);
|
|
$editSummary[] = 'PM sent';
|
|
}
|
|
|
|
$userStatus = match ($_POST['UserStatus']) {
|
|
'1' => UserStatus::enabled,
|
|
'2' => UserStatus::disabled,
|
|
default => UserStatus::unconfirmed,
|
|
};
|
|
if ($userStatus != $user->userStatus() && $Viewer->permitted('users_disable_users')) {
|
|
$enableStr = "account status {$user->userStatus()->label()} → {$userStatus->label()}";
|
|
if ($userStatus == UserStatus::disabled) {
|
|
$userMan->disableUserList(
|
|
[$user->id],
|
|
UserAuditEvent::activity,
|
|
"Disabled via moderation",
|
|
Manager\User::DISABLE_MANUAL,
|
|
);
|
|
Util\DisabledUserHistory::add($user, $reason);
|
|
$needTrackerRefresh = false;
|
|
} elseif ($userStatus == UserStatus::enabled) {
|
|
$needTrackerAdd = true;
|
|
if (($user->downloadedSize() == 0) || ($user->uploadedSize() / $user->downloadedSize() >= $user->requiredRatio())) {
|
|
$user->setField('can_leech', 1)
|
|
->setField('RatioWatchEnds', null)
|
|
->setField('RatioWatchDownload', 0);
|
|
} else {
|
|
$enableStr .= ' (Ratio: ' . ratio_html($user->uploadedSize(), $user->downloadedSize(), false)
|
|
. ', RR: ' . number_format($user->requiredRatio(), 2) . ')';
|
|
if ($cur['RatioWatchEnds']) {
|
|
$user->setField('can_leech', 1)
|
|
->setField('RatioWatchDownload', $user->downloadedSize())
|
|
->setFieldNow('RatioWatchEnds');
|
|
}
|
|
}
|
|
$user->setField('BanReason', '0');
|
|
}
|
|
$user->setField('Enabled', $userStatus->value);
|
|
$editSummary[] = $enableStr;
|
|
}
|
|
|
|
if ($Viewer->permitted('users_edit_reset_keys')) {
|
|
if ($resetAuthkey == 1) {
|
|
$user->setField('auth_key', authKey());
|
|
$editSummary[] = 'authkey reset';
|
|
}
|
|
if ($resetPasskey == 1) {
|
|
$oldPasskey = $user->announceKey();
|
|
$passkey = randomString();
|
|
$user->history()->modifyAnnounceKey(old: $oldPasskey, new: $passkey);
|
|
$tracker->modifyPasskey(old: $oldPasskey, new: $passkey);
|
|
$editSummary[] = 'passkey reset';
|
|
}
|
|
}
|
|
|
|
if ($sendHackedMail && $Viewer->permitted('users_disable_any')) {
|
|
new Mail()->send($hackedEmail, SITE_NAME . ' account - suspicious activity',
|
|
$Twig->render('email/hacked.twig', [
|
|
'user' => $user
|
|
])
|
|
);
|
|
$userMan->disableUserList(
|
|
[$user->id],
|
|
UserAuditEvent::activity,
|
|
"Disabled via hacked email",
|
|
Manager\User::DISABLE_MANUAL,
|
|
);
|
|
$editSummary[] = "hacked account email sent to $hackedEmail";
|
|
Util\DisabledUserHistory::add($user, "Disabled via hacked email");
|
|
}
|
|
|
|
if ($mergeStatsFrom && $Viewer->permitted('users_edit_ratio')) {
|
|
$stats = $user->mergeLeechStats($mergeStatsFrom, $Viewer->username());
|
|
if ($stats) {
|
|
$merge = new User($stats['userId']);
|
|
$merge->flush();
|
|
$user->setField('leech_uploaded', $user->uploadedSize() + $stats['up'])
|
|
->setField('leech_downloaded', $user->downloadedSize() + $stats['down']);
|
|
$editSummary[] = sprintf('leech stats (up: %s, down: %s, ratio: %s) merged from %s (%s) prior(up: %s, down: %s, ratio: %s)',
|
|
byte_format($stats['up']), byte_format($stats['down']), ratio($stats['up'], $stats['down']),
|
|
$merge->url(), $mergeStatsFrom,
|
|
byte_format($user->uploadedSize()), byte_format($user->downloadedSize()), ratio($user->uploadedSize(), $user->downloadedSize())
|
|
);
|
|
}
|
|
}
|
|
|
|
if ($changePassword && $Viewer->permitted('users_edit_password')) {
|
|
$editSummary[] = 'password reset';
|
|
}
|
|
|
|
if (!(count($editSummary)) && $reason) {
|
|
$editSummary[] = 'notes added';
|
|
}
|
|
|
|
// Because of the infinitely fucked up encoding/decoding of Gazelle, $adminComment !== $cur['admincomment']
|
|
// almost always evaluates to true, even if the user did not purposely change the field. This then means
|
|
// we do have a bug where if a mod changes something about a user AND changes the admin comment, we will lose
|
|
// that change, but until we never decode stuff coming out of the DB, not much can be done.
|
|
|
|
if ($editSummary) {
|
|
$summary = implode(', ', $editSummary) . " by {$Viewer->username()}";
|
|
if ($reason) {
|
|
$summary .= "\nReason: $reason";
|
|
}
|
|
$user->auditTrail()->addEvent(UserAuditEvent::staffNote, ucfirst($summary), $Viewer);
|
|
} elseif ($adminComment !== $cur['admincomment']) {
|
|
$user->auditTrail()->addEvent(UserAuditEvent::staffNote, $adminComment, $Viewer);
|
|
}
|
|
|
|
if ($removedClasses) {
|
|
$user->removeClasses($removedClasses);
|
|
}
|
|
if ($addedClasses) {
|
|
$user->addClasses($addedClasses);
|
|
}
|
|
|
|
if ($changePassword && $Viewer->permitted('users_edit_password')) {
|
|
$user->history()->modifyPassword($_POST['ChangePassword'], false);
|
|
new User\Session($user)->dropAll();
|
|
}
|
|
|
|
if ($newBonusPoints !== false) {
|
|
new User\Bonus($user)->setPoints($newBonusPoints);
|
|
}
|
|
|
|
if ($flTokens != $user->tokenCount()) {
|
|
$user->updateTokens($flTokens);
|
|
}
|
|
|
|
$user->modify();
|
|
$user->flush();
|
|
if ($needTrackerAdd) {
|
|
$tracker->addUser($user);
|
|
} elseif ($needTrackerRefresh) {
|
|
$tracker->refreshUser($user);
|
|
}
|
|
if ($Viewer->permitted('admin_tracker')) {
|
|
$tracker->traceUser($user, isset($_POST['tracker-trace']));
|
|
}
|
|
|
|
if (isset($_POST['invite_source_update'])) {
|
|
$idList = array_key_extract_suffix('source-', $_POST);
|
|
if ($idList) {
|
|
new Manager\InviteSource()->modifyInviterConfiguration($user, $idList);
|
|
header("Location: tools.php?action=invite_source");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
header('Location: ' . $user->location());
|