首页改介绍页;列表只服务当前软件;新增UUID追踪让提交人看得到未审核反馈
用户提的两个核心问题:
一、首页不该列全部问题
场景:这套系统是给某个软件配反馈入口、把地址贴进那软件的「意见反馈」里。
访客总是从某个软件过来的,看别的软件的问题对他没意义。
⇒ / 不带 sw 时渲染介绍页(特点介绍 + 「点右上角设置按钮选软件」+ 软件卡片)
带 sw 才是该软件的问题列表,检索/筛选/分类全在本软件范围内
二、提交后未审核,用户点不进去(真实缺陷)
邮件里给了「点击查看您刚才提交的反馈」,但 thread.php 只在 visible=1 时
才给看,未审核的一律 404 / 只看到「等待审核」静态页 —— 链接等于白给。
⇒ threads 加 track_token(32位hex = 128位熵),提交时生成:
- 邮件里所有链接一律带 ?t=<token>(不再是裸 id)
- 带对 token:可看完整详情、可回复,哪怕未公开/已关闭
- 没 token 或 token 错:对外一律 404,不泄露存在性
- 公开列表仍用 id 链接(公开内容谁都能看,用于避免重复提交)
实现细节:
- threads.track_token + 唯一索引;幂等迁移,存量数据自动回填
- fb_track_new_token / fb_track_token_ok(hash_equals 防时序探测)
/ fb_thread_viewable / fb_thread_link(thread, personal)
- thread.php 权限从 fb_thread_public 改为 fb_thread_viewable
- 带 token 看自己未公开的反馈时,顶部给「私人跟踪链接」提示条
- 回复表单与回复后跳转都保留 token,否则回复完就失去访问权
- 提交完成后直接带 token 跳详情页,不再只给静态等待页
- close 类邮件补上「查看这条反馈」按钮(原来完全没链接)
- 顶栏「全部问题」改为「问题列表」且只在选中软件后显示
- 详情/提交页「返回问题列表」按所属软件返回,不再回介绍页
- new.php 无 sw 时不再默认落到第一个软件,改为让用户先选
(默默替选会让反馈进错软件、通知发错人)
验证:首页 0 条问题卡片且未泄露任何标题、三个软件入口齐全;
未公开#4 无 token 404 / 错 token 404 / 正确 token 200 且显示完整详情
(不再卡在「等待审核」)、提示条与表单 token 都在;
已公开#2 无 token 可看;5 类邮件(回执/通过/回复/状态/关闭)链接全带 token;
两套回归各 30+ 项断言全过。
This commit is contained in:
+49
-12
@@ -11,16 +11,34 @@ if ($id <= 0) { fb_redirect('index.php'); }
|
||||
$st = $pdo->prepare('SELECT * FROM threads WHERE id = ?');
|
||||
$st->execute([$id]);
|
||||
$thread = $st->fetch();
|
||||
|
||||
/* 这条帖子所属的软件 —— 「返回问题列表」要回到它的列表,
|
||||
* 而不是回到首页(首页现在是介绍页)。 */
|
||||
$backUrl = fb_software_list_url(
|
||||
$thread && !empty($thread['software_id']) ? fb_software((int)$thread['software_id']) : null
|
||||
);
|
||||
if (!$thread) {
|
||||
http_response_code(404);
|
||||
fb_layout_head('问题不存在');
|
||||
echo '<div class="empty"><p>该问题不存在或已被删除。</p><p><a class="btn primary" href="index.php">返回问题列表</a></p></div>';
|
||||
echo '<div class="empty"><p>该问题不存在或已被删除。</p><p><a class="btn primary" href="' . e($backUrl) . '">返回问题列表</a></p></div>';
|
||||
fb_layout_foot();
|
||||
exit;
|
||||
}
|
||||
|
||||
// 审核(先审后显):未通过审核的问题对访客不可见
|
||||
if (!fb_thread_public($thread)) {
|
||||
/* 审核(先审后显)+ 提交人追踪:
|
||||
*
|
||||
* 可见性三档 ——
|
||||
* ① 已公开:所有人可看
|
||||
* ② 未公开 + 带对 token:提交人本人可看(这是他的私人进度页)
|
||||
* ③ 未公开 + 没 token:对外一律装作「不存在」,不泄露标题也不泄露状态
|
||||
*
|
||||
* 🔴 关键:带对 token 时**绝不能**再走「提交成功,等待审核」那个提示页。
|
||||
* 用户之前就卡在这里 —— 邮件里给了链接让他看进度,点进来却只看到
|
||||
* 「等待审核」,等于链接白给。带 token 进来就应该看到完整详情。 */
|
||||
$hasToken = isset($_GET['t']) ? trim((string)$_GET['t']) : '';
|
||||
$isOwnerView = $hasToken !== '' && fb_track_token_ok($thread, $hasToken);
|
||||
|
||||
if (!fb_thread_viewable($thread)) {
|
||||
$justSubmitted = (isset($_GET['submitted']) && (string)$_GET['submitted'] === '1');
|
||||
if ($justSubmitted) {
|
||||
fb_layout_head('提交成功', ['robots' => 'noindex,nofollow']);
|
||||
@@ -29,15 +47,16 @@ if (!fb_thread_public($thread)) {
|
||||
. '<p>您的反馈已收到(编号 #' . (int)$thread['id'] . '),我们会先确认它是否为应用问题:</p>'
|
||||
. '<p>· 确认为问题 —— 审核通过后会公开显示,其他人也能查看与回复;<br>'
|
||||
. '· 确认不是问题 —— 我们会关闭它,并把结论与说明发送到您留下的邮箱。</p>'
|
||||
. '<p class="muted">审核通常在 1~2 个工作日内完成,请留意邮箱。</p>'
|
||||
. '<p><a class="btn primary" href="index.php">返回问题列表</a></p>'
|
||||
. '<p class="muted">审核通常在 1~2 个工作日内完成。'
|
||||
. '我们会发一封邮件给您,里面的链接可以随时查看这条反馈的处理进度。</p>'
|
||||
. '<p><a class="btn primary" href="' . e($backUrl) . '">返回问题列表</a></p>'
|
||||
. '</div>';
|
||||
fb_layout_foot();
|
||||
exit;
|
||||
}
|
||||
http_response_code(404);
|
||||
fb_layout_head('问题不存在');
|
||||
echo '<div class="empty"><p>该问题不存在或已被删除。</p><p><a class="btn primary" href="index.php">返回问题列表</a></p></div>';
|
||||
echo '<div class="empty"><p>该问题不存在或已被删除。</p><p><a class="btn primary" href="' . e($backUrl) . '">返回问题列表</a></p></div>';
|
||||
fb_layout_foot();
|
||||
exit;
|
||||
}
|
||||
@@ -49,11 +68,12 @@ $form = ['name' => '', 'email' => '', 'body' => '', 'captcha' => ''];
|
||||
|
||||
/* ---------------- 处理回复提交 ---------------- */
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// 审核中(未公开)的问题不允许回复
|
||||
if (!fb_thread_public($thread)) {
|
||||
/* 未公开的帖子,只有带对 token 的提交人(或管理员)能回复。
|
||||
* 别人连看都看不到,自然也不该能回。 */
|
||||
if (!fb_thread_public($thread) && !$isOwnerView) {
|
||||
http_response_code(403);
|
||||
fb_layout_head('暂不可回复');
|
||||
echo '<div class="empty"><p>该问题还在审核中,暂时不能回复。</p><p><a class="btn primary" href="index.php">返回问题列表</a></p></div>';
|
||||
echo '<div class="empty"><p>该问题还在审核中,暂时不能回复。</p><p><a class="btn primary" href="' . e($backUrl) . '">返回问题列表</a></p></div>';
|
||||
fb_layout_foot();
|
||||
exit;
|
||||
}
|
||||
@@ -131,7 +151,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
];
|
||||
fb_notify_reply($pdo, $thread, $replyRow, $isAdmin);
|
||||
|
||||
fb_redirect('thread.php?id=' . $id . '#r' . $replyId);
|
||||
/* 回复后跳回去时要带上 token,否则提交人对未公开反馈的访问权会当场丢掉 */
|
||||
fb_redirect('thread.php?id=' . $id
|
||||
. ($isOwnerView ? '&t=' . rawurlencode($hasToken) : '')
|
||||
. '#r' . $replyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,7 +176,18 @@ $thSwForColor = !empty($thread['software_id']) ? fb_software((int)$thread['softw
|
||||
fb_layout_head(fb_excerpt($thread['title'], 50), ['software' => $thSwForColor]);
|
||||
?>
|
||||
|
||||
<div class="crumbs"><a href="index.php">← 返回问题列表</a></div>
|
||||
<div class="crumbs"><a href="<?php echo e($backUrl); ?>">← 返回问题列表</a></div>
|
||||
|
||||
<?php if ($isOwnerView && !fb_thread_public($thread)): ?>
|
||||
<?php /* 提交人凭 token 进来看到自己的未公开反馈时,要明确告诉他:
|
||||
这条还没公开、别人看不到,但他可以一直用这个链接跟踪。
|
||||
否则他会以为「既然能打开,是不是已经公开了」。 */ ?>
|
||||
<div class="owner-note">
|
||||
<b>这是您的私人跟踪链接</b>
|
||||
<span>这条反馈还没通过审核,其他人看不到。请保留本页链接(或邮件里的链接),
|
||||
审核结果与后续进展都能在这里查看。</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<article class="subject">
|
||||
<div class="item-top">
|
||||
@@ -206,8 +240,11 @@ fb_layout_head(fb_excerpt($thread['title'], 50), ['software' => $thSwForColor]);
|
||||
<section class="reply-form">
|
||||
<h2>我要回复</h2>
|
||||
<?php fb_flash_box($err, 'error'); ?>
|
||||
<form method="post" action="thread.php?id=<?php echo (int)$id; ?>">
|
||||
<?php /* 表单要带上 token:否则提交后跳转回不带 t 的地址,
|
||||
提交人会立刻失去对自己未公开反馈的访问权,体验上就是「回复完就看不见了」。 */ ?>
|
||||
<form method="post" action="thread.php?id=<?php echo (int)$id; ?><?php echo $isOwnerView ? '&t=' . e(rawurlencode($hasToken)) : ''; ?>">
|
||||
<input type="hidden" name="id" value="<?php echo (int)$id; ?>">
|
||||
<input type="hidden" name="t" value="<?php echo e($isOwnerView ? $hasToken : ''); ?>">
|
||||
<input type="hidden" name="csrf" value="<?php echo e(fb_csrf_token()); ?>">
|
||||
<input type="hidden" name="t0" value="<?php echo time(); ?>">
|
||||
<div class="hp-field" aria-hidden="true">
|
||||
|
||||
Reference in New Issue
Block a user