isLoggedIn()) { echo render_error(401); return; } $currentUser = $msz->getActiveUser(); $currentUserId = $currentUser->getId(); if($msz->hasActiveBan()) { echo render_error(403); return; } $forum = $msz->getForum(); $users = $msz->getUsers(); $userInfos = []; $userColours = []; $userPostsCounts = []; $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)) { echo render_error(400); return; } 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)) { echo render_error(404); return; } if(empty($postId)) { $hasPostInfo = false; } else { try { $postInfo = $forum->getPost(postId: $postId); } catch(RuntimeException $ex) { echo render_error(404); return; } if($postInfo->isDeleted()) { echo render_error(404); return; } // 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 = $forum->getTopic(topicId: $topicId); } catch(RuntimeException $ex) { echo render_error(404); return; } if($topicInfo->isDeleted()) { echo render_error(404); return; } $forumId = $topicInfo->getCategoryId(); $originalPostInfo = $forum->getPost(topicInfo: $topicInfo); $hasTopicInfo = true; } if(empty($forumId)) { $hasCategoryInfo = false; } else { try { $categoryInfo = $forum->getCategory(categoryId: $forumId); } catch(RuntimeException $ex) { echo render_error(404); return; } $hasCategoryInfo = true; } $perms = forum_perms_get_user($categoryInfo->getId(), $currentUserId)[MSZ_FORUM_PERMS_GENERAL]; if($categoryInfo->isArchived() || (isset($topicInfo) && $topicInfo->isLocked() && !perms_check($perms, MSZ_FORUM_PERM_LOCK_TOPIC)) || !perms_check($perms, MSZ_FORUM_PERM_VIEW_FORUM | MSZ_FORUM_PERM_CREATE_POST) || (!isset($topicInfo) && !perms_check($perms, MSZ_FORUM_PERM_CREATE_TOPIC))) { echo render_error(403); return; } if(!$categoryInfo->mayHaveTopics()) { echo render_error(400); return; } $topicTypes = []; if($mode === 'create' || $mode === 'edit') { $topicTypes['discussion'] = 'Normal discussion'; if(perms_check($perms, MSZ_FORUM_PERM_STICKY_TOPIC)) $topicTypes['sticky'] = 'Sticky topic'; if(perms_check($perms, MSZ_FORUM_PERM_ANNOUNCE_TOPIC)) $topicTypes['announce'] = 'Announcement'; if(perms_check($perms, MSZ_FORUM_PERM_GLOBAL_ANNOUNCE_TOPIC)) $topicTypes['global'] = 'Global Announcement'; } // edit mode stuff if($mode === 'edit' && !perms_check($perms, $postInfo->getUserId() === $currentUserId ? MSZ_FORUM_PERM_EDIT_POST : MSZ_FORUM_PERM_EDIT_ANY_POST)) { echo render_error(403); return; } $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(is_string($topicType)) $topicType = ForumTopicInfo::TYPE_ALIASES[$topicType] ?? ForumTopicInfo::TYPE_DISCUSSION; else $topicType = (int)$topicType; if($mode === 'create') { $postTimeout = $cfg->getInteger('forum.posting.timeout', 5); if($postTimeout > 0) { $postTimeoutThreshold = DateTime::now()->modify(sprintf('-%d seconds', $postTimeout)); $lastPostCreatedAt = $forum->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 = (int)($topicInfo?->getType() ?? 0); $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 = $forum->createTopic( $categoryInfo, $currentUser, $topicTitle, $topicType ); $topicId = $topicInfo->getId(); $forum->incrementCategoryTopics($categoryInfo); } else $forum->bumpTopic($topicInfo); $postInfo = $forum->createPost( $topicId, $currentUser, $_SERVER['REMOTE_ADDR'], $postText, $postParser, $postSignature, $categoryInfo ); $postId = $postInfo->getId(); $forum->incrementCategoryPosts($categoryInfo); break; case 'edit': $markUpdated = $postInfo->getUserId() === $currentUserId && $postInfo->shouldMarkAsEdited() && $postText !== $postInfo->getBody(); $forum->updatePost( $postId, remoteAddr: $_SERVER['REMOTE_ADDR'], body: $postText, bodyParser: $postParser, displaySignature: $postSignature, bumpEdited: $markUpdated ); if($isEditingTopic && ($topicTitleChanged || $topicTypeChanged)) $forum->updateTopic( $topicId, title: $topicTitle, type: $topicType ); break; } if(empty($notices)) { // does this ternary ever return forum-topic? $redirect = url(empty($topicInfo) ? 'forum-topic' : 'forum-post', [ 'topic' => $topicId ?? 0, 'post' => $postId ?? 0, 'post_fragment' => 'p' . ($postId ?? 0), ]); 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()) { $postUserId = $postInfo->getUserId(); if(!array_key_exists($postUserId, $userInfos)) { $userInfo = $users->getUser($postUserId, 'id'); $userInfos[$postUserId] = $userInfo; $userColours[$postUserId] = $users->getUserColour($userInfo); $userPostsCounts[$postUserId] = $forum->countPosts(userInfo: $userInfo, deleted: false); } $post->user = $userInfos[$postUserId]; $post->colour = $userColours[$postUserId]; $post->postsCount = $userPostsCounts[$postUserId]; } $post->isOriginalPost = $originalPostInfo->getId() == $postInfo->getId(); $post->isOriginalPoster = $originalPostInfo->hasUserId() && $postInfo->hasUserId() && $originalPostInfo->getUserId() === $postInfo->getUserId(); Template::set('posting_post', $post); } try { $lastPostInfo = $forum->getPost(userInfo: $currentUser, getLast: true, deleted: false); $selectedParser = $lastPostInfo->getParser(); } catch(RuntimeException $ex) { $selectedParser = Parser::BBCODE; } // this sucks, fix it! $topicTypeName = match($topicType ?? $topicInfo?->getType() ?? null) { default => 'discussion', ForumTopicInfo::TYPE_STICKY => 'sticky', ForumTopicInfo::TYPE_ANNOUNCE => 'announce', ForumTopicInfo::TYPE_GLOBAL => 'global', }; Template::render('forum.posting', [ 'posting_breadcrumbs' => $forum->getCategoryAncestry($categoryInfo), 'global_accent_colour' => $forum->getCategoryColour($categoryInfo), 'posting_user' => $currentUser, 'posting_user_colour' => $userColours[$currentUser->getId()] ?? $users->getUserColour($currentUser), 'posting_user_posts_count' => $userPostsCounts[$currentUser->getId()] ?? $forum->countPosts(userInfo: $currentUser, deleted: false), 'posting_user_preferred_parser' => $selectedParser, 'posting_forum' => $categoryInfo, 'posting_notices' => $notices, 'posting_mode' => $mode, 'posting_types' => $topicTypes, 'posting_type_selected' => $topicTypeName, 'posting_defaults' => [ 'title' => $topicTitle ?? null, 'type' => $topicType ?? null, 'text' => $postText ?? null, 'parser' => $postParser ?? null, 'signature' => $postSignature ?? null, ], ]);