misuzu/public-legacy/forum/posting.php
2023-09-08 20:40:48 +00:00

317 lines
11 KiB
PHP

<?php
namespace Misuzu;
use stdClass;
use RuntimeException;
use Index\DateTime;
use Misuzu\Forum\ForumTopicInfo;
use Misuzu\Parsers\Parser;
$authInfo = $msz->getAuthInfo();
if(!$authInfo->isLoggedIn())
Template::throwError(401);
$forumCtx = $msz->getForumContext();
$forumCategories = $forumCtx->getCategories();
$forumTopics = $forumCtx->getTopics();
$forumPosts = $forumCtx->getPosts();
$usersCtx = $msz->getUsersContext();
$currentUser = $authInfo->getUserInfo();
$currentUserId = $currentUser->getId();
if($usersCtx->hasActiveBan($currentUser))
Template::throwError(403);
$forumPostingModes = [
'create', 'edit', 'quote', 'preview',
];
if(!empty($_POST)) {
$mode = !empty($_POST['post']['mode']) && is_string($_POST['post']['mode']) ? $_POST['post']['mode'] : 'create';
$postId = !empty($_POST['post']['id']) && is_string($_POST['post']['id']) ? (int)$_POST['post']['id'] : 0;
$topicId = !empty($_POST['post']['topic']) && is_string($_POST['post']['topic']) ? (int)$_POST['post']['topic'] : 0;
$forumId = !empty($_POST['post']['forum']) && is_string($_POST['post']['forum']) ? (int)$_POST['post']['forum'] : 0;
} else {
$mode = !empty($_GET['m']) && is_string($_GET['m']) ? $_GET['m'] : 'create';
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
$topicId = !empty($_GET['t']) && is_string($_GET['t']) ? (int)$_GET['t'] : 0;
$forumId = !empty($_GET['f']) && is_string($_GET['f']) ? (int)$_GET['f'] : 0;
}
if(!in_array($mode, $forumPostingModes, true))
Template::throwError(400);
if($mode === 'preview') {
header('Content-Type: text/plain; charset=utf-8');
$postText = (string)($_POST['post']['text']);
$postParser = (int)($_POST['post']['parser']);
if(!Parser::isValid($postParser)) {
http_response_code(400);
return;
}
http_response_code(200);
echo Parser::instance($postParser)->parseText(htmlspecialchars($postText));
return;
}
if(empty($postId) && empty($topicId) && empty($forumId))
Template::throwError(404);
if(empty($postId)) {
$hasPostInfo = false;
} else {
try {
$postInfo = $forumPosts->getPost(postId: $postId);
} catch(RuntimeException $ex) {
Template::throwError(404);
}
if($postInfo->isDeleted())
Template::throwError(404);
// should automatic cross-quoting be a thing? if so, check if $topicId is < 1 first <-- what did i mean by this?
$topicId = $postInfo->getTopicId();
$hasPostInfo = true;
}
if(empty($topicId)) {
$hasTopicInfo = false;
} else {
try {
$topicInfo = $forumTopics->getTopic(topicId: $topicId);
} catch(RuntimeException $ex) {
Template::throwError(404);
}
if($topicInfo->isDeleted())
Template::throwError(404);
$forumId = $topicInfo->getCategoryId();
$originalPostInfo = $forumPosts->getPost(topicInfo: $topicInfo);
$hasTopicInfo = true;
}
if(empty($forumId)) {
$hasCategoryInfo = false;
} else {
try {
$categoryInfo = $forumCategories->getCategory(categoryId: $forumId);
} catch(RuntimeException $ex) {
Template::throwError(404);
}
$hasCategoryInfo = true;
}
$perms = $authInfo->getPerms('forum', $categoryInfo);
if($categoryInfo->isArchived()
|| (isset($topicInfo) && $topicInfo->isLocked() && !$perms->check(Perm::F_TOPIC_LOCK))
|| !$perms->check(Perm::F_CATEGORY_VIEW)
|| !$perms->check(Perm::F_POST_CREATE)
|| (!isset($topicInfo) && !$perms->check(Perm::F_TOPIC_CREATE)))
Template::throwError(403);
if(!$categoryInfo->mayHaveTopics())
Template::throwError(400);
$topicTypes = [];
if($mode === 'create' || $mode === 'edit') {
$topicTypes['discussion'] = 'Normal discussion';
if($perms->check(Perm::F_TOPIC_STICKY))
$topicTypes['sticky'] = 'Sticky topic';
if($perms->check(Perm::F_TOPIC_ANNOUNCE_LOCAL))
$topicTypes['announce'] = 'Announcement';
if($perms->check(Perm::F_TOPIC_ANNOUNCE_GLOBAL))
$topicTypes['global'] = 'Global Announcement';
}
// edit mode stuff
if($mode === 'edit' && !$perms->check($postInfo->getUserId() === $currentUserId ? Perm::F_POST_EDIT_OWN : Perm::F_POST_EDIT_ANY))
Template::throwError(403);
$notices = [];
if(!empty($_POST)) {
$topicTitle = $_POST['post']['title'] ?? '';
$postText = $_POST['post']['text'] ?? '';
$postParser = (int)($_POST['post']['parser'] ?? Parser::BBCODE);
$topicType = isset($_POST['post']['type']) ? $_POST['post']['type'] : null;
$postSignature = isset($_POST['post']['signature']);
if(!CSRF::validateRequest()) {
$notices[] = 'Could not verify request.';
} else {
$isEditingTopic = empty($topicInfo) || ($mode === 'edit' && $originalPostInfo->getId() == $postInfo->getId());
if($mode === 'create') {
$postTimeout = $cfg->getInteger('forum.posting.timeout', 5);
if($postTimeout > 0) {
$postTimeoutThreshold = DateTime::now()->modify(sprintf('-%d seconds', $postTimeout));
$lastPostCreatedAt = $forumPosts->getUserLastPostCreatedAt($currentUser);
if($lastPostCreatedAt->isMoreThan($postTimeoutThreshold)) {
$waitSeconds = $postTimeout + ($lastPostCreatedAt->getUnixTimeSeconds() - time());
$notices[] = sprintf("You're posting too quickly! Please wait %s seconds before posting again.", number_format($waitSeconds));
$notices[] = "It's possible that your post went through successfully and you pressed the submit button twice by accident.";
}
}
}
if($isEditingTopic) {
$originalTopicTitle = $topicInfo?->getTitle() ?? null;
$topicTitleChanged = $topicTitle !== $originalTopicTitle;
$originalTopicType = $topicInfo?->getTypeString() ?? 'discussion';
$topicTypeChanged = $topicType !== null && $topicType !== $originalTopicType;
$topicTitleLengths = $cfg->getValues([
['forum.topic.minLength:i', 3],
['forum.topic.maxLength:i', 100],
]);
$topicTitleLength = mb_strlen(trim($topicTitle));
if($topicTitleLength < $topicTitleLengths['forum.topic.minLength'])
$notices[] = 'Topic title was too short.';
elseif($topicTitleLength > $topicTitleLengths['forum.topic.maxLength'])
$notices[] = 'Topic title was too long.';
if($mode === 'create' && $topicType === null) {
$topicType = array_key_first($topicTypes);
} elseif(!array_key_exists($topicType, $topicTypes) && $topicTypeChanged) {
$notices[] = 'You are not allowed to set this topic type.';
}
}
if(!Parser::isValid($postParser))
$notices[] = 'Invalid parser selected.';
$postTextLengths = $cfg->getValues([
['forum.post.minLength:i', 1],
['forum.post.maxLength:i', 60000],
]);
$postTextLength = mb_strlen(trim($postText));
if($postTextLength < $postTextLengths['forum.post.minLength'])
$notices[] = 'Post content was too short.';
elseif($postTextLength > $postTextLengths['forum.post.maxLength'])
$notices[] = 'Post content was too long.';
if(empty($notices)) {
switch($mode) {
case 'create':
if(empty($topicInfo)) {
$topicInfo = $forumTopics->createTopic(
$categoryInfo,
$currentUser,
$topicTitle,
$topicType
);
$topicId = $topicInfo->getId();
$forumTopics->incrementCategoryTopics($categoryInfo);
} else
$forumTopics->bumpTopic($topicInfo);
$postInfo = $forumPosts->createPost(
$topicId,
$currentUser,
$_SERVER['REMOTE_ADDR'],
$postText,
$postParser,
$postSignature,
$categoryInfo
);
$postId = $postInfo->getId();
$forumCategories->incrementCategoryPosts($categoryInfo);
break;
case 'edit':
$markUpdated = $postInfo->getUserId() === $currentUserId
&& $postInfo->shouldMarkAsEdited()
&& $postText !== $postInfo->getBody();
$forumPosts->updatePost(
$postId,
remoteAddr: $_SERVER['REMOTE_ADDR'],
body: $postText,
bodyParser: $postParser,
displaySignature: $postSignature,
bumpEdited: $markUpdated
);
if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged))
$forumTopics->updateTopic(
$topicId,
title: $topicTitle,
type: $topicType
);
break;
}
if(empty($notices)) {
// does this ternary ever return forum-topic?
$redirect = $msz->getURLs()->format(empty($topicInfo) ? 'forum-topic' : 'forum-post', [
'topic' => $topicId ?? 0,
'post' => $postId ?? 0,
]);
Tools::redirect($redirect);
return;
}
}
}
}
if(!empty($topicInfo))
Template::set('posting_topic', $topicInfo);
if($mode === 'edit') { // $post is pretty much sure to be populated at this point
$post = new stdClass;
$post->info = $postInfo;
if($postInfo->hasUserId()) {
$post->user = $usersCtx->getUserInfo($postInfo->getUserId());
$post->colour = $usersCtx->getUserColour($post->user);
$post->postsCount = $forumCtx->countTotalUserPosts($post->user);
}
$post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId();
$post->isOriginalPoster = $originalPostInfo->hasUserId() && $postInfo->hasUserId()
&& $originalPostInfo->getUserId() === $postInfo->getUserId();
Template::set('posting_post', $post);
}
try {
$lastPostInfo = $forumPosts->getPost(userInfo: $currentUser, getLast: true, deleted: false);
$selectedParser = $lastPostInfo->getParser();
} catch(RuntimeException $ex) {
$selectedParser = Parser::BBCODE;
}
Template::render('forum.posting', [
'posting_breadcrumbs' => $forumCategories->getCategoryAncestry($categoryInfo),
'global_accent_colour' => $forumCategories->getCategoryColour($categoryInfo),
'posting_user' => $currentUser,
'posting_user_colour' => $usersCtx->getUserColour($currentUser),
'posting_user_posts_count' => $forumCtx->countTotalUserPosts($currentUser),
'posting_user_preferred_parser' => $selectedParser,
'posting_forum' => $categoryInfo,
'posting_notices' => $notices,
'posting_mode' => $mode,
'posting_types' => $topicTypes,
'posting_defaults' => [
'title' => $topicTitle ?? null,
'type' => $topicType ?? null,
'text' => $postText ?? null,
'parser' => $postParser ?? null,
'signature' => $postSignature ?? null,
],
]);