278 lines
13 KiB
PHP
278 lines
13 KiB
PHP
|
<?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';
|