<?php require_once '../startup.php'; include_once '_category.php'; include_once '_topics.php'; include_once '_posts.php'; include_once '_user.php'; include_once '_track.php'; $topicId = isset($_GET['id']) && is_string($_GET['id']) && ctype_digit($_GET['id']) ? (int)$_GET['id'] : 0; $topicInfo = topic_info($topicId); if(empty($topicInfo['topic_bumped'])) { die_ex('Topic not found.', 404); } $categoryInfo = category_info($topicInfo['cat_id']); $isFeatureReqs = $categoryInfo['cat_variation'] == 1; $userInfo = user_info(current_user_id()); $userActive = !empty($userInfo); $isLocked = !empty($topicInfo['topic_locked']); $isResolved = !empty($topicInfo['topic_resolved']); $isConfirmed = !empty($topicInfo['topic_confirmed']); $canLock = $userActive && $userInfo['user_moderator']; $canResolve = $userActive && ($userInfo['user_moderator'] || (!$isLocked && $isFeatureReqs == 0 && $userInfo['user_id'] == $topicInfo['user_id'])); $canConfirm = $userActive && !$isResolved && $userInfo['user_moderator']; if(isset($_GET['m']) && is_string($_GET['m'])) { if(CSRF::verify()) { switch($_GET['m']) { case 'resolve': if(!$canResolve) die_ex('You aren\'t allowed to mark this topic as resolved.', 403); if($isResolved) die_ex('This topic is already marked as resolved.', 400); $eventType = FMF_POST_TYPE_RESOLVE; mark_topic_resolved($topicInfo['topic_id'], true); $satoriMsg = "[b]forum.flash.moe[/b]: [url=https://forum.flash.moe/user/{$userInfo['user_id']}][b]{$userInfo['user_login']}[/b][/url] marked [url=https://forum.flash.moe/topic/{$topicId}][b]{$topicInfo['topic_title']}[/b][/url] as [b]resolved[/b]."; break; case 'unresolve': if(!$canResolve) die_ex('You aren\'t allowed to mark this topic as unresolved.', 403); if(!$isResolved) die_ex('This topic is already marked as unresolved.', 400); $eventType = FMF_POST_TYPE_UNRESOLVED; $eventBump = true; mark_topic_resolved($topicInfo['topic_id'], false); $satoriMsg = "[b]forum.flash.moe[/b]: [url=https://forum.flash.moe/user/{$userInfo['user_id']}][b]{$userInfo['user_login']}[/b][/url] marked [url=https://forum.flash.moe/topic/{$topicId}][b]{$topicInfo['topic_title']}[/b][/url] as [b]unresolved[/b]."; break; case 'confirm': if(!$canConfirm) die_ex('You aren\'t allowed to mark this topic as confirmed.', 403); if($isResolved) die_ex('This topic has been marked as resolved and doesn\'t need to be confirmed anymore.', 400); if($isConfirmed) die_ex('This topic is already marked as confirmed.', 400); $eventType = FMF_POST_TYPE_CONFIRMED; $eventBump = true; mark_topic_confirmed($topicInfo['topic_id'], true); $satoriMsg = "[b]forum.flash.moe[/b]: [url=https://forum.flash.moe/user/{$userInfo['user_id']}][b]{$userInfo['user_login']}[/b][/url] marked [url=https://forum.flash.moe/topic/{$topicId}][b]{$topicInfo['topic_title']}[/b][/url] as [b]confirmed[/b]."; break; case 'unconfirm': if(!$canConfirm) die_ex('You aren\'t allowed to mark this topic as unconfirmed.', 403); if($isResolved) die_ex('This topic has been marked as resolved, you can\'t revoke confirmations anymore.', 400); if(!$isConfirmed) die_ex('This topic is not confirmed.', 400); $eventType = FMF_POST_TYPE_UNCONFIRMED; $eventBump = true; mark_topic_confirmed($topicInfo['topic_id'], false); $satoriMsg = "[b]forum.flash.moe[/b]: [url=https://forum.flash.moe/user/{$userInfo['user_id']}][b]{$userInfo['user_login']}[/b][/url] marked [url=https://forum.flash.moe/topic/{$topicId}][b]{$topicInfo['topic_title']}[/b][/url] as [b]unconfirmed[/b]."; break; case 'lock': if(!$canLock) die_ex('You aren\'t allowed to lock this topic.', 403); if($isLocked) die_ex('This topic is already locked.', 400); $eventType = FMF_POST_TYPE_LOCKED; lock_topic($topicInfo['topic_id'], true); break; case 'unlock': if(!$canLock) die_ex('You aren\'t allowed to unlock this topic.', 403); if(!$isLocked) die_ex('This topic is already unlocked.', 400); $eventType = FMF_POST_TYPE_UNLOCKED; lock_topic($topicInfo['topic_id'], false); break; default: die_ex('Unknown action.', 404); break; } if(isset($eventType)) create_topic_event($topicInfo['cat_id'], $topicInfo['topic_id'], $userInfo['user_id'], $eventType, $eventData ?? null); if(isset($eventBump)) topic_bump($topicInfo['topic_id']); } if(defined('SATORI_SECRET') && !empty($satoriMsg)) { $sock = @fsockopen(SATORI_HOST, SATORI_PORT, $errno, $errstr, 2); if($sock) { fwrite($sock, chr(0xF) . hash_hmac('sha256', $satoriMsg, SATORI_SECRET) . $satoriMsg . chr(0xF)); fflush($sock); fclose($sock); } } header('Location: /topic/'. $topicInfo['topic_id']); return; } if($userActive) update_track($userInfo['user_id'], $topicInfo['topic_id'], $topicInfo['cat_id']); $title = $topicInfo['topic_title']; $posts = posts_in_topic($topicInfo['topic_id']); include FMF_LAYOUT . '/header.php'; $breadcrumbs = '<a href="/">forum.flash.moe</a> » '; $breadcrumbs_arr = category_breadcrumbs($topicInfo['cat_id'], false); foreach($breadcrumbs_arr as $breadcrumb) { $breadcrumbs .= sprintf('<a href="/category/%d">%s</a> » ', $breadcrumb['cat_id'], $breadcrumb['cat_name']); } echo $breadcrumbs; $topicButtons = '<div class="topic-btns">'; if(!$isLocked || $canLock) { $topicButtons .= '<a href="/topic/'. $topicInfo['topic_id'] .'/reply">Reply</a>'; } if($canResolve) { $topicButtons .= '<a href="/topic/'. $topicInfo['topic_id'] .'/'. ($isResolved ? 'unresolve' : 'resolve') .'?_csrf='. CSRF::token() .'">'. ($isResolved ? 'Reopen' : ($isFeatureReqs ? 'Mark Implemented' : 'Mark Resolved')) .'</a>'; } if($canConfirm) { $topicButtons .= '<a href="/topic/'. $topicInfo['topic_id'] .'/'. ($isConfirmed ? 'unconfirm' : 'confirm') .'?_csrf='. CSRF::token() .'">'. ($isConfirmed ? ($isFeatureReqs ? 'Revoke Accept' : 'Revoke Confirmation') : ($isFeatureReqs ? 'Accept for implementation' : 'Confirm')) .'</a>'; } if($canLock) { $topicButtons .= '<a href="/topic/'. $topicInfo['topic_id'] .'/'. ($isLocked ? 'unlock' : 'lock') .'?_csrf='. CSRF::token() .'">'. ($isLocked ? 'Unlock' : 'Lock') .'</a>'; } $topicButtons .= '</div>'; ?> <h3 class="forum-title"><?=htmlentities($topicInfo['topic_title']);?></h3> <?php echo $topicButtons; if($isLocked) { ?> <div class="notice"> This topic was <b>locked</b> on <b><time datetime="<?=date('c', $topicInfo['topic_locked']);?>"><?=date(FMF_DATE_FORMAT, $topicInfo['topic_locked']);?></time></b>. </div> <?php } if($isResolved) { ?> <div class="notice"> This topic was marked <b>resolved</b> on <b><time datetime="<?=date('c', $topicInfo['topic_resolved']);?>"><?=date(FMF_DATE_FORMAT, $topicInfo['topic_resolved']);?></time></b>. </div> <?php } foreach($posts as $post) { $authorInfo = user_info($post['user_id']); if($post['post_type'] == FMF_POST_TYPE_MESSAGE) { ?> <div class="post<?=(empty($post['post_deleted']) ? '' : ' post-deleted')?>" id="p<?=$post['post_id'];?>"> <?php if(empty($post['post_deleted'])) { ?> <div class="post-details"> <a class="post-username" href="/user/<?=($authorInfo['user_id'] ?? 0);?>"><?=($authorInfo['user_login'] ?? 'Deleted User');?></a> <img class="post-avatar" src="<?=user_gravatar($authorInfo['user_id'] ?? null);?>" alt="<?=($authorInfo['user_login'] ?? 'Deleted User');?>"/> <div class="post-permalink-wrap"> <a class="post-permalink" href="/post/<?=$post['post_id'];?>"><time class="post-time" datetime="<?=date('c', $post['post_created']);?>"><?=date(FMF_DATE_FORMAT, $post['post_created']);?></time></a> </div> </div> <?php } ?> <div class="post-text"> <?php if(empty($post['post_deleted'])) { ?> <div class="post-text-inner"> <?=(new Parsedown)->setSafeMode(true)->text($post['post_text']);?> </div> <div class="post-footer"> <div class="post-edited"> <?php if(!empty($post['post_edited'])) { ?> Last edited: <time datetime="<?=date('c', $post['post_edited']);?>"><?=date(FMF_DATE_FORMAT, $post['post_edited']);?></time> <?php } ?> </div> <div class="post-options"> <?php if($userActive && ($userInfo['user_moderator'] || $userInfo['user_id'] === $post['user_id'])) { ?> <a href="/post/<?=$post['post_id'];?>/edit">Edit</a> <a href="/post/<?=$post['post_id'];?>?m=delete&_csrf=<?=CSRF::token();?>">Delete</a> <?php } ?> <?php if(!empty($post['user_id']) && $userActive && $userInfo['user_moderator']) { ?> <a href="/post/<?=$post['post_id'];?>?m=anonymize&_csrf=<?=CSRF::token();?>">Strip User ID</a> <?php } ?> </div> </div> <?php } else { ?> <div class="post-text-inner"> <a href="/post/<?=$post['post_id'];?>">#<?=$post['post_id'];?></a>: <code>deleted</code> <?php if($userActive && $userInfo['user_moderator']) { ?> (<a href="/post/<?=$post['post_id'];?>?m=restore&_csrf=<?=CSRF::token();?>">Restore</a>) <?php } ?> </div> <?php } ?> </div> </div> <?php } else { unset($eventHtml); switch($post['post_type']) { default: $eventMessages = [ FMF_POST_TYPE_RESOLVE => '<a href="/user/:user_id">:username</a> has marked this ' . ($isFeatureReqs ? 'request as <b>implemented</b>.' : 'topic as <b>resolved</b>.'), FMF_POST_TYPE_LOCKED => '<a href="/user/:user_id">:username</a> has <b>locked</b> this conversation.', FMF_POST_TYPE_UNLOCKED => '<a href="/user/:user_id">:username</a> has <b>unlocked</b> this conversation.', FMF_POST_TYPE_UNRESOLVED => '<a href="/user/:user_id">:username</a> has marked this ' . ($isFeatureReqs ? 'request as <b>unimplemented</b>.' : 'marked this topic as <b>unresolved</b>.'), FMF_POST_TYPE_CONFIRMED => '<a href="/user/:user_id">:username</a> has ' . ($isFeatureReqs ? 'marked this request for <b>implementation</b>.' : '<b>confirmed</b> this topic.'), FMF_POST_TYPE_UNCONFIRMED => '<a href="/user/:user_id">:username</a> has ' . ($isFeatureReqs ? '<b>revoked implementation confirmation</b> for this request.' : '<b>revoked the confirmation</b> from this topic.'), ]; $eventMessage = $eventMessages[$post['post_type']] ?? '<a href="/user/:user_id">:username</a> did something.'; break; } if(isset($eventMessage)) { $eventMessage = strtr($eventMessage, [ ':user_id' => $authorInfo['user_id'] ?? 0, ':username' => $authorInfo['user_login'] ?? 'Deleted User', ]); } if(!isset($eventHtml)) { $eventHtml = sprintf( '<div class="event-msg"><img class="event-avatar" src="%s" alt="%s"/><div class="event-msg-text">%s</div><a class="event-permalink" href="/post/%d"><time class="event-time" datetime="%s">%s</time></a></div>', user_gravatar($authorInfo['user_id'] ?? null, 20), ($authorInfo['user_login'] ?? 'Deleted User'), $eventMessage ?? 'Missing event description.', $post['post_id'], date('c', $post['post_created']), date(FMF_DATE_FORMAT, $post['post_created']) ); } ?> <div class="event" id="p<?=$post['post_id'];?>"> <?=$eventHtml;?> </div> <?php } } echo $topicButtons; echo $breadcrumbs . $topicInfo['topic_title']; include FMF_LAYOUT . '/footer.php';