misuzu/public-legacy/profile.php
flash 383e2ed0e0 Rewrote the user information class.
This one took multiple days and it pretty invasive into the core of Misuzu so issue might (will) arise, there's also some features that have gone temporarily missing in the mean time and some inefficiencies introduced that will be fixed again at a later time.
The old class isn't gone entirely because I still have to figure out what I'm gonna do about validation, but for the most part this knocks out one of the "layers of backwards compatibility", as I've been referring to it, and is moving us closer to a future where Flashii actually gets real updates.
If you run into anything that's broken and you're inhibited from reporting it through the forum, do it through chat or mail me at flashii-issues@flash.moe.
2023-08-02 22:12:47 +00:00

461 lines
21 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();
$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;
}
$notices = [];
$userRank = $users->getUserRank($userInfo);
$viewerRank = $users->getUserRank($viewerInfo);
$activeBanInfo = $msz->tryGetActiveBan($userInfo);
$isBanned = $activeBanInfo !== null;
$profileFields = $msz->getProfileFields();
$viewingOwnProfile = (string)$viewerId === $userInfo->getId();
$userPerms = perms_get_user($viewerId)[MSZ_PERMS_USER];
$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS);
$canEdit = !$viewingAsGuest && ((!$isBanned && $viewingOwnProfile) || $viewerInfo->isSuperUser() || (
perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS)
&& ($viewingOwnProfile || $viewerRank > $userRank)
));
$avatarInfo = new UserAvatarAsset($userInfo);
$backgroundInfo = new UserBackgroundAsset($userInfo);
if($isEditing) {
if(!$canEdit) {
echo render_error(403);
return;
}
$perms = perms_check_bulk($userPerms, [
'edit_profile' => MSZ_PERM_USER_EDIT_PROFILE,
'edit_avatar' => MSZ_PERM_USER_CHANGE_AVATAR,
'edit_background' => MSZ_PERM_USER_CHANGE_BACKGROUND,
'edit_about' => MSZ_PERM_USER_EDIT_ABOUT,
'edit_birthdate' => MSZ_PERM_USER_EDIT_BIRTHDATE,
'edit_signature' => MSZ_PERM_USER_EDIT_SIGNATURE,
]);
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();
switch($profileMode) {
default:
echo render_error(404);
return;
case 'forum-topics':
$template = 'profile.topics';
$topicsCount = forum_topic_count_user($userInfo->getId(), $viewerId);
$topicsPagination = new Pagination($topicsCount, 20);
if(!$topicsPagination->hasValidOffset()) {
echo render_error(404);
return;
}
$topics = forum_topic_listing_user(
$userInfo->getId(), $viewerId,
$topicsPagination->getOffset(), $topicsPagination->getRange()
);
Template::set([
'title' => $userInfo->getName() . ' / topics',
'canonical_url' => url('user-profile-forum-topics', ['user' => $userInfo->getId(), 'page' => Pagination::param()]),
'profile_topics' => $topics,
'profile_topics_pagination' => $topicsPagination,
]);
break;
case 'forum-posts':
$template = 'profile.posts';
$postsCount = forum_post_count_user($userInfo->getId());
$postsPagination = new Pagination($postsCount, 20);
if(!$postsPagination->hasValidOffset()) {
echo render_error(404);
return;
}
$posts = forum_post_listing(
$userInfo->getId(),
$postsPagination->getOffset(),
$postsPagination->getRange(),
false,
true
);
Template::set([
'title' => $userInfo->getName() . ' / posts',
'canonical_url' => url('user-profile-forum-posts', ['user' => $userInfo->getId(), 'page' => Pagination::param()]),
'profile_posts' => $posts,
'profile_posts_pagination' => $postsPagination,
]);
break;
case '':
$template = 'profile.index';
if(!$viewingAsGuest) {
Template::set('profile_warnings', $msz->getWarnings()->getWarningsWithDefaultBacklog($userInfo));
if((!$isBanned || $canEdit)) {
$activeCategoryStats = forum_get_user_most_active_category_info($userInfo->getId());
$activeCategoryInfo = empty($activeCategoryStats->forum_id) ? null : forum_get($activeCategoryStats->forum_id);
$activeTopicStats = forum_get_user_most_active_topic_info($userInfo->getId());
$activeTopicInfo = empty($activeTopicStats->topic_id) ? null : forum_topic_get($activeTopicStats->topic_id);
$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,
]);
}
}
break;
}
if(!empty($template)) {
Template::render($template, [
'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,
]);
}