misuzu/public-legacy/profile.php

433 lines
20 KiB
PHP

<?php
namespace Misuzu;
use InvalidArgumentException;
use RuntimeException;
use Index\ByteFormat;
use Index\DateTime;
use Misuzu\Parsers\Parser;
use Misuzu\Users\User;
use Misuzu\Users\Assets\UserAvatarAsset;
use Misuzu\Users\Assets\UserBackgroundAsset;
$userId = !empty($_GET['u']) && is_string($_GET['u']) ? trim($_GET['u']) : 0;
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST);
$users = $msz->getUsers();
$forum = $msz->getForum();
$viewerInfo = $msz->getActiveUser();
$viewingAsGuest = $viewerInfo === null;
$viewerId = $viewingAsGuest ? '0' : $viewerInfo->getId();
try {
$userInfo = $users->getUser($userId, 'profile');
} catch(RuntimeException $ex) {
http_response_code(404);
Template::render('profile.index', [
'profile_is_guest' => $viewingAsGuest,
'profile_is_deleted' => true,
'profile_is_banned' => false,
]);
return;
}
if($userInfo->isDeleted()) {
http_response_code(404);
Template::render('profile.index', [
'profile_is_guest' => $viewingAsGuest,
'profile_is_deleted' => true,
'profile_is_banned' => false,
]);
return;
}
switch($profileMode) {
default:
echo render_error(404);
return;
case 'forum-topics':
url_redirect('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->getName()), 'section' => 'topics']);
return;
case 'forum-posts':
url_redirect('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->getName()), 'section' => 'posts']);
return;
case '':
break;
}
$notices = [];
$userRank = $users->getUserRank($userInfo);
$viewerRank = $viewingAsGuest ? 0 : $users->getUserRank($viewerInfo);
$viewerPerms = $msz->getAuthInfo()->getPerms('user');
$activeBanInfo = $msz->tryGetActiveBan($userInfo);
$isBanned = $activeBanInfo !== null;
$profileFields = $msz->getProfileFields();
$viewingOwnProfile = (string)$viewerId === $userInfo->getId();
$canManageWarnings = $viewerPerms->check(Perm::U_WARNINGS_MANAGE);
$canEdit = !$viewingAsGuest && ((!$isBanned && $viewingOwnProfile) || $viewerInfo->isSuperUser() || (
$viewerPerms->check(Perm::U_USERS_MANAGE) && ($viewingOwnProfile || $viewerRank > $userRank)
));
$avatarInfo = new UserAvatarAsset($userInfo);
$backgroundInfo = new UserBackgroundAsset($userInfo);
if($isEditing) {
if(!$canEdit) {
echo render_error(403);
return;
}
$perms = $viewerPerms->checkMany([
'edit_profile' => Perm::U_PROFILE_EDIT,
'edit_avatar' => Perm::U_AVATAR_CHANGE,
'edit_background' => PERM::U_PROFILE_BACKGROUND_CHANGE,
'edit_about' => Perm::U_PROFILE_ABOUT_EDIT,
'edit_birthdate' => Perm::U_PROFILE_BIRTHDATE_EDIT,
'edit_signature' => Perm::U_FORUM_SIGNATURE_EDIT,
]);
Template::set([
'perms' => $perms,
'background_attachments' => UserBackgroundAsset::getAttachmentStringOptions(),
]);
if(!empty($_POST) && is_array($_POST)) {
if(!CSRF::validateRequest()) {
$notices[] = 'Couldn\'t verify you, please refresh the page and retry.';
} else {
$profileFieldsSubmit = filter_input(INPUT_POST, 'profile', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
if(!empty($profileFieldsSubmit)) {
if(!$perms->edit_profile) {
$notices[] = 'You\'re not allowed to edit your profile';
} else {
$profileFieldInfos = $profileFields->getFields();
$profileFieldsSetInfos = [];
$profileFieldsSetValues = [];
$profileFieldsRemove = [];
foreach($profileFieldInfos as $fieldInfo) {
$fieldName = $fieldInfo->getName();
$fieldValue = empty($profileFieldsSubmit[$fieldName]) ? '' : (string)filter_var($profileFieldsSubmit[$fieldName]);
if(empty($profileFieldsSubmit[$fieldName])) {
$profileFieldsRemove[] = $fieldInfo;
continue;
}
if($fieldInfo->checkValue($fieldValue)) {
$profileFieldsSetInfos[] = $fieldInfo;
$profileFieldsSetValues[] = $fieldValue;
} else
$notices[] = sprintf('%s isn\'t properly formatted.', $fieldInfo->getTitle());
unset($fieldName, $fieldValue, $fieldInfo);
}
if(!empty($profileFieldsRemove))
$profileFields->removeFieldValues($userInfo, $profileFieldsRemove);
if(!empty($profileFieldsSetInfos))
$profileFields->setFieldValues($userInfo, $profileFieldsSetInfos, $profileFieldsSetValues);
}
}
if(!empty($_POST['about']) && is_array($_POST['about'])) {
if(!$perms->edit_about) {
$notices[] = 'You\'re not allowed to edit your about page.';
} else {
$aboutText = (string)($_POST['about']['text'] ?? '');
$aboutParse = (int)($_POST['about']['parser'] ?? Parser::PLAIN);
$aboutValid = User::validateProfileAbout($aboutParse, $aboutText);
if($aboutValid === '')
$users->updateUser($userInfo, aboutContent: $aboutText, aboutParser: $aboutParse);
else switch($aboutValid) {
case 'parser':
$notices[] = 'The selected about section parser is invalid.';
break;
case 'long':
$notices[] = sprintf('Please keep the length of your about section below %d characters.', User::PROFILE_ABOUT_MAX_LENGTH);
break;
default:
$notices[] = 'Failed to update about section, contact an administator.';
break;
}
}
}
if(!empty($_POST['signature']) && is_array($_POST['signature'])) {
if(!$perms->edit_signature) {
$notices[] = 'You\'re not allowed to edit your forum signature.';
} else {
$sigText = (string)($_POST['signature']['text'] ?? '');
$sigParse = (int)($_POST['signature']['parser'] ?? Parser::PLAIN);
$sigValid = User::validateForumSignature($sigParse, $sigText);
if($sigValid === '')
$users->updateUser($userInfo, signatureContent: $sigText, signatureParser: $sigParse);
else switch($sigValid) {
case 'parser':
$notices[] = 'The selected forum signature parser is invalid.';
break;
case 'long':
$notices[] = sprintf('Please keep the length of your signature below %d characters.', User::FORUM_SIGNATURE_MAX_LENGTH);
break;
default:
$notices[] = 'Failed to update signature, contact an administator.';
break;
}
}
}
if(!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) {
if(!$perms->edit_birthdate) {
$notices[] = "You aren't allow to change your birthdate.";
} else {
$birthYear = (int)($_POST['birthdate']['year'] ?? 0);
$birthMonth = (int)($_POST['birthdate']['month'] ?? 0);
$birthDay = (int)($_POST['birthdate']['day'] ?? 0);
$birthValid = User::validateBirthdate($birthYear, $birthMonth, $birthDay);
if($birthValid === '')
$users->updateUser($userInfo, birthYear: $birthYear, birthMonth: $birthMonth, birthDay: $birthDay);
else switch($birthValid) {
case 'year':
$notices[] = 'The given birth year is invalid.';
break;
case 'date':
$notices[] = 'The given birthdate is invalid.';
break;
default:
$notices[] = 'Something unexpected happened while setting your birthdate.';
break;
}
}
}
if(!empty($_FILES['avatar'])) {
if(!empty($_POST['avatar']['delete'])) {
$avatarInfo->delete();
} else {
if(!$perms->edit_avatar) {
$notices[] = 'You aren\'t allow to change your avatar.';
} elseif(!empty($_FILES['avatar'])
&& is_array($_FILES['avatar'])
&& !empty($_FILES['avatar']['name']['file'])) {
if($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
switch($_FILES['avatar']['error']['file']) {
case UPLOAD_ERR_NO_FILE:
$notices[] = 'Select a file before hitting upload!';
break;
case UPLOAD_ERR_PARTIAL:
$notices[] = 'The upload was interrupted, please try again!';
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$notices[] = sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes()));
break;
default:
$notices[] = 'Unable to save your avatar, contact an administator!';
break;
}
} else {
try {
$avatarInfo->setFromPath($_FILES['avatar']['tmp_name']['file']);
} catch(InvalidArgumentException $ex) {
$exMessage = $ex->getMessage();
$notices[] = match($exMessage) {
'$path is not a valid image.' => 'The file you uploaded was not an image!',
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
'Dimensions of $path are too large.' => sprintf('Your avatar can\'t be larger than %dx%d!', $avatarInfo->getMaxWidth(), $avatarInfo->getMaxHeight()),
'File size of $path is too large.' => sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes())),
default => $exMessage,
};
} catch(RuntimeException $ex) {
$notices[] = 'Unable to save your avatar, contact an administator!';
}
}
}
}
}
if(!empty($_FILES['background'])) {
if((int)($_POST['background']['attach'] ?? -1) === 0) {
$backgroundInfo->delete();
} else {
if(!$perms->edit_background) {
$notices[] = 'You aren\'t allow to change your background.';
} elseif(!empty($_FILES['background']) && is_array($_FILES['background'])) {
if(!empty($_FILES['background']['name']['file'])) {
if($_FILES['background']['error']['file'] !== UPLOAD_ERR_OK) {
switch($_FILES['background']['error']['file']) {
case UPLOAD_ERR_NO_FILE:
$notices[] = 'Select a file before hitting upload!';
break;
case UPLOAD_ERR_PARTIAL:
$notices[] = 'The upload was interrupted, please try again!';
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$notices[] = sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundProps['max_size']));
break;
default:
$notices[] = 'Unable to save your background, contact an administator!';
break;
}
} else {
try {
$backgroundInfo->setFromPath($_FILES['background']['tmp_name']['file']);
} catch(InvalidArgumentException $ex) {
$exMessage = $ex->getMessage();
$notices[] = match($exMessage) {
'$path is not a valid image.' => 'The file you uploaded was not an image!',
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
'Dimensions of $path are too large.' => sprintf('Your background can\'t be larger than %dx%d!', $backgroundInfo->getMaxWidth(), $backgroundInfo->getMaxHeight()),
'File size of $path is too large.' => sprintf('Your background is not allowed to be larger in file size than %2$s!', ByteFormat::format($backgroundInfo->getMaxBytes())),
default => $exMessage,
};
} catch(RuntimeException $ex) {
$notices[] = 'Unable to save your background, contact an administator!';
}
}
}
$backgroundInfo->setAttachment((int)($_POST['background']['attach'] ?? 0))
->setBlend(!empty($_POST['background']['attr']['blend']))
->setSlide(!empty($_POST['background']['attr']['slide']));
}
}
$users->updateUser($userInfo, backgroundSettings: $backgroundInfo->getSettings());
}
}
// Unset $isEditing and hope the user doesn't refresh their profile!
if(empty($notices))
$isEditing = false;
}
}
$profileStats = DB::prepare('
SELECT (
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `user_id` = u.`user_id`
AND `topic_deleted` IS NULL
) AS `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `user_id` = u.`user_id`
AND `post_deleted` IS NULL
) AS `forum_post_count`,
(
SELECT COUNT(`change_id`)
FROM `msz_changelog_changes`
WHERE `user_id` = u.`user_id`
) AS `changelog_count`,
(
SELECT COUNT(`comment_id`)
FROM `msz_comments_posts`
WHERE `user_id` = u.`user_id`
AND `comment_deleted` IS NULL
) AS `comments_count`
FROM `msz_users` AS u
WHERE `user_id` = :user_id
')->bind('user_id', $userInfo->getId())->fetch();
if(!$viewingAsGuest) {
Template::set('profile_warnings', $msz->getWarnings()->getWarningsWithDefaultBacklog($userInfo));
if((!$isBanned || $canEdit)) {
$unranked = $cfg->getValues([
'forum_leader.unranked.forum:a',
'forum_leader.unranked.topic:a',
]);
$activeCategoryStats = $forum->getMostActiveCategoryInfo(
$userInfo,
$unranked['forum_leader.unranked.forum'],
$unranked['forum_leader.unranked.topic'],
deleted: false
);
$activeCategoryInfo = $activeCategoryStats->success ? $forum->getCategory(categoryId: $activeCategoryStats->categoryId) : null;
$activeTopicStats = $forum->getMostActiveTopicInfo(
$userInfo,
$unranked['forum_leader.unranked.forum'],
$unranked['forum_leader.unranked.topic'],
deleted: false
);
$activeTopicInfo = $activeTopicStats->success ? $forum->getTopic(topicId: $activeTopicStats->topicId) : null;
$profileFieldValues = $profileFields->getFieldValues($userInfo);
$profileFieldInfos = $profileFieldInfos ?? $profileFields->getFields(fieldValueInfos: $isEditing ? null : $profileFieldValues);
$profileFieldFormats = $profileFields->getFieldFormats(fieldValueInfos: $profileFieldValues);
$profileFieldRawValues = [];
$profileFieldLinkValues = [];
$profileFieldDisplayValues = [];
// using field infos as the basis for now, uses the correct ordering
foreach($profileFieldInfos as $fieldInfo) {
unset($fieldValue);
foreach($profileFieldValues as $fieldValueTest)
if($fieldValueTest->getFieldId() === $fieldInfo->getId()) {
$fieldValue = $fieldValueTest;
break;
}
$fieldName = $fieldInfo->getName();
if(isset($fieldValue)) {
foreach($profileFieldFormats as $fieldFormatTest)
if($fieldFormatTest->getId() === $fieldValue->getFormatId()) {
$fieldFormat = $fieldFormatTest;
break;
}
$profileFieldRawValues[$fieldName] = $fieldValue->getValue();
$profileFieldDisplayValues[$fieldName] = $fieldFormat->formatDisplay($fieldValue->getValue());
if($fieldFormat->hasLinkFormat())
$profileFieldLinkValues[$fieldName] = $fieldFormat->formatLink($fieldValue->getValue());
}
}
Template::set([
'profile_active_category_stats' => $activeCategoryStats,
'profile_active_category_info' => $activeCategoryInfo,
'profile_active_topic_stats' => $activeTopicStats,
'profile_active_topic_info' => $activeTopicInfo,
'profile_fields_infos' => $profileFieldInfos,
'profile_fields_raw_values' => $profileFieldRawValues,
'profile_fields_display_values' => $profileFieldDisplayValues,
'profile_fields_link_values' => $profileFieldLinkValues,
]);
}
}
Template::render('profile.index', [
'profile_viewer' => $viewerInfo,
'profile_user' => $userInfo,
'profile_colour' => $users->getUserColour($userInfo),
'profile_stats' => $profileStats,
'profile_mode' => $profileMode,
'profile_notices' => $notices,
'profile_can_edit' => $canEdit,
'profile_is_editing' => $isEditing,
'profile_is_banned' => $isBanned,
'profile_is_guest' => $viewingAsGuest,
'profile_is_deleted' => false,
'profile_ban_info' => $activeBanInfo,
'profile_avatar_info' => $avatarInfo,
'profile_background_info' => $backgroundInfo,
]);