本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/docs/右侧栏改造方案_2026-09-27.md。
右侧栏改造方案(2026-09-27)
状态:方案已定稿(2026-09-27),代码未动 —— 等开工指令。
需求来源:站长口述 6 条,6 项决策已全部拍板(见 §四)
涉及面:核心视图
app/Views/_components/right_sidebar.php、核心样式assets/css/modern.css、插件
plugins/daily_checkin/*、规范docs/插件开发规范.md⚠️ 本次必须改核心视图,与之前"只改 plugins"的口径不同 —— 见 §五。
〇、一句话结论
把「签到」从核心视图里彻底摘出来,改成签到插件自己挂右栏钩子输出;核心只留「个人信息 + 发帖」两个等宽按钮,
并在它们上方开一个 2 列网格的通用钩子(钩子A = right_sidebar_user_actions_above)给"日常常用操作"类插件用。
用户栏下方、同级的面板挂载点复用已有的 right_sidebar_after(决策 6 选①,零核心改动)。
改完,核心 right_sidebar.php 里不再出现 daily_checkin 任何字样。
一、现状:耦合到底长什么样(实测证据)
1.1 核心里对签到插件的 3 处硬编码
| # | 位置 | 内容 |
|---|---|---|
| 1 | app/Views/_components/right_sidebar.php:182 | $this->include('plugins/daily_checkin/_sidebar'); |
| 2 | app/Views/_components/right_sidebar.php:187-193 | #checkinHint 提示行(含 I18n::get('plugin.daily_checkin.msg_success_persist')) |
| 3 | app/Views/_components/right_sidebar.php:191 | I18n::get('plugin.daily_checkin.title') / .subtitle |
app/Views/home/index.php:121也出现daily_checkin字样,但那是首页「插件介绍」的静态营销文案(<code>daily_checkin</code>+ 中文名),不是代码耦合,本次不动。
1.2 未装 / 未启用签到插件时的两个可见症状
站点上没有 daily_checkin 目录(或 plugin.json 里 activated: false)时:
症状 A —— 按钮位空一格
right_sidebar.php:182 的 include() 走 TemplateCompiler::include()(app/Core/TemplateCompiler.php:284):
public function include($template, $extra = [])
{
$data = array_merge($this->data, $extra);
$r = $this->resolveTemplate($template);
if (!$r) return; // ← 模板不存在 → 静默 return,不报错、不写日志
...
}→ 插件目录不存在时这里什么都不输出,.mn-user-actions 里只剩「个人信息」一个按钮(它带 mn-flex-1,会被拉满整行)。
症状 B —— 吐出一行键名(这才是真正的"显示问题")
right_sidebar.php:185-194 的 #checkinHint 区块完全没有 Plugin::isActivated() 门控,无条件渲染,
而它要的 i18n 键只存在于插件语言包里:
app/Helpers/I18n.php:58-71mergePluginLangs()→if (empty($cfg['activated'])) continue;→ 未启用插件的语言包根本不合并- 核心
lang/.php里没有plugin.daily_checkin.任何键(已 grep 确认) I18n::get()(app/Helpers/I18n.php:143-153)缺键时return $key;("永不白屏"设计)
结果:右栏用户卡下方多出一条分隔线 + 一行灰色小字,内容是原文键名:
plugin.daily_checkin.title · plugin.daily_checkin.subtitle1.3 两个独立机理,别混为一谈
| 机理 | 触发条件 | 表现 |
|---|---|---|
| 模板解析失败静默 | 插件目录不存在 | 按钮位空白(少一个按钮) |
| i18n 键缺失吐键名 | 插件存在但 activated:false,或目录不存在 | 提示行显示 plugin.daily_checkin.title · plugin.daily_checkin.subtitle |
两者可单独出现、也可同时出现。解耦后两者一起消失 —— 因为未启用插件的钩子压根不执行(见 §3.1)。
二、改造总览(改前 / 改后)
改前
[用户卡] 头像 + 名字 / 加入日期
积分 │ 主题
[ 个人信息 ] [ 签到 ] ← 第 2 格 = 核心硬编码 include 签到插件
─────────────────────────
获得 2 积分,已连续签到 3 天 ← 无门控;未装插件时吐键名
──────────────────────────────────────
[right_sidebar_after 钩子] ← 现被 dice / qa / quiz_duel 占用
[站点统计]
[热门标签]
[sidebar_above_friend_links 钩子] ← 排行榜 + 最新会员
[友情链接]改后
[用户栏 .mn-panel] 头像 + 名字 / 加入日期
积分 │ 主题
┌─ 钩子A 常用操作(新,每行 2 列)─┐
│ [我的收藏] [我的帖子] │
│ [消息] [草稿箱] │
└─────────────────────────────────┘
[ 个人信息 ] [ 发帖 ] ← 等宽;核心写死,零插件依赖
← 用户栏 结束(卡片内部到此为止)
────────────────────────────────────────────────────
[钩子B 用户栏下方 · 同级] ← 本次新增;插件挂独立 mn-panel,与用户栏同级
[站点统计]
[热门标签]
[排行榜 + 最新会员]
[友情链接]钩子A 与钩子B 分居用户栏内外,职责不同
- 钩子A(用户栏内部、按钮上方)→ 给"日常常用操作",核心强制 2 列网格,保证整齐。
- 钩子B(用户栏外部、与用户栏同级)→ 给"独立面板",插件自己输出完整
<div class="mn-panel">,形态与排行榜 / 最新会员 / 站点统计一致。✅ 决策 6 已定:钩子B 复用已有的
right_sidebar_after(right_sidebar.php:210,规范 §9 #14,注释原文"右栏通用钩子:用户信息面板之后,供插件注入侧栏面板"),不新增钩子位。
现有占用者
dice(停用)/qa(启用,只认/qa*)/quiz_duel(停用)→ 实际零冲突;先后顺序由各插件
plugin.json的sort_order控制。
三、逐条方案
需求① 把右侧栏的签到解耦
核心侧(删)
- 删
right_sidebar.php:182——$this->include('plugins/daily_checkin/_sidebar'); - 删
right_sidebar.php:185-194—— 整个#checkinHint区块(含外层if (!$isBlogPage))
插件侧(加)
- 新增
plugins/daily_checkin/hook/right_sidebar_after.php plugin.json的hooks增"right_sidebar_after": "hook/right_sidebar_after.php"(按需加sort_order)
为什么选 right_sidebar_after
- 钩子 #14(
docs/插件开发规范.md:674),触发点right_sidebar.php:210,无参数、单触发、PC 右栏;
位置正好在用户面板之后、站点统计之前 → 就是"用户信息栏的下方"。
Plugin::hook()(app/Helpers/Plugin.php:604-658)第 609 行if (empty($config['activated'])) continue;
→ 未启用插件的钩子根本不执行 → 未装/未启用时零输出,§1.2 两个症状一起消失。
- 模板解析失败静默(
if (!$r) return;)这条也顺带被绕开了 —— 因为核心不再 include 插件模板。
副作用(要认)
:210 在 <?php endif; ?>(:206)之后 → 帖子/博客详情页也会触发。
现状是详情页没有签到入口,解耦后会多一个 → 属功能新增,不是回归。→ 见 §四 决策 2。
钩子占用情况(无实际冲突)
| 插件 | 状态 | 行为 |
|---|---|---|
dice | activated:false | 不执行 |
qa | activated:true | 只认 /qa 前缀,其他页 return |
quiz_duel | activated:false | 不执行 |
✅ 与需求⑥ 的联动已定:需求⑥ 要的"用户栏下方、同级"挂载点就是
right_sidebar_after。决策 6 选①(复用)→ 本需求的周视图面板维持挂
right_sidebar_after,位置在:210,不改。
如需固定顺序,给 plugin.json 加 sort_order(Plugin::sortPlugins() 按 sort_order → seq 排;
实测 new_members 落 index 37、leaderboard 15 → sort_order 强于字母序)。
需求② 用户信息栏下方新增「本周签到」面板
数据源(复用现有查询,不增加查询数)
hook/controller_view_before.php 现在查的是"今天"这一行。改成查"本周"这一区间,一次查询喂两个用途:
$weekStart = date('Y-m-d', strtotime('monday this week'));
$weekEnd = date('Y-m-d', strtotime('sunday this week'));
// SELECT checkin_date, points, consecutive_days FROM daily_checkin
// WHERE user_id = :uid AND checkin_date BETWEEN :s AND :echeckin_today= 结果里是否含今天(原来那次"今日"查询就省了)checkin_consecutive= 今日行的consecutive_days(今日未签到则为 0)- 新增
week_days= 7 项['label'=>'一','date'=>'2026-09-21','checked'=>bool,'is_today'=>bool,'is_future'=>bool]
面板结构(对齐右栏其他面板:.mn-panel + .mn-panel-title)
视觉映射(严格只用既有 --mn-* 变量,已脚本核对 9/9 主题全覆盖)
| 参考图元素 | 本站实现 | 变量 |
|---|---|---|
| 卡片 | .mn-panel | --mn-bg-card / --mn-border / --mn-shadow-sm |
| 标题 + 右箭头 | .mn-panel-title + mn-ml-auto 的 <a> | — |
| 7 列字母 | 新增 .dc-week-label | --mn-text-muted |
| 空格圆圈 | 新增 .dc-week-dot | border: 1px solid var(--mn-border) |
| 已签到填充 | .dc-week-dot.is-checked | background: var(--mn-primary) + 勾用 --mn-text-inverse |
| 绿色胶囊 | 复用核心 .mn-alert.mn-alert-success | --mn-alert-success-bg/-text/-border |
| 底部灰字 | mn-fs-12 mn-text-muted | — |
| 未签到状态 | 复用现有 htmx 表单 → .mn-btn.mn-btn-primary.mn-btn-sm | --mn-primary |
已核对的变量覆盖(9/9 主题):
--mn-primary--mn-primary-light--mn-primary-hover--mn-primary-subtle
--mn-text--mn-text-secondary--mn-text-inverse--mn-text-muted--mn-border--mn-border-light
--mn-bg--mn-bg-body--mn-bg-card--mn-success--mn-alert-success-bg/-text/-border--mn-shadow-sm。⚠️ 已知不可用:
--mn-bg-hover(0/9)、--mn-text-success(0/9)、--mn-radius(0/9)。
⚠️ 两处不能踩
- 核心没有
.mn-alert-warning(只有success/error,9 套主题也只定义了这两组变量)
→ "还没签到"不要用 warning 语义,用中性灰字 + 主色按钮。
- 新增 CSS 一律写在插件自己的
assets/style.css(前缀dc-week-*),核心modern.css一个字都不加。
交互(htmx,沿用现有紧凑回包机制)
- 面板 id 由
#checkinSidebar改为#checkinWeek - 未签到 → 表单
hx-post="/checkin"+hx-target="#checkinWeek"+hx-swap="outerHTML",
隐藏字段 widget=sidebar(CheckinController::doCheckin() 靠它走紧凑回包)
- 回包模板即面板本身 → 原地变成"已签到"态
周起始日(决策 3 已定 = 周一起)
- 周视图按 周一 ~ 周日 排列,表头沿用既有 i18n 键
week_mon…week_sun(已存在于三语包) - 区间算法:
date('monday this week')~date('sunday this week') - ⚠️ 与站内既有日历不一致:
views/_area.php:96的月份日历表头是 日一二三四五六(周日起)。
_area.php 本次不动,两处起始日不同属已知差异 → 会在插件 说明.md 里注明,避免以后当成 bug。
模板入口的关键坑(实现时必踩,先记下)
同一份模板会被两条路径加载,取数方式不同:
| 入口 | 路径 | `$this->getData()` 可用? | 普通变量可用? |
|---|---|---|---|
| htmx 回包 | doCheckin() → viewRaw('plugins/daily_checkin/_sidebar', $data) → TemplateCompiler::display()(:197-200 会 extract()) | ✅ | ✅ |
| 右栏钩子 | Plugin::hook() → 闭包作用域(Plugin.php:644)$this 不可用 | ❌ | ✅(经 $extra) |
- 钩子里不能写
$this->include()→ 要$view = $GLOBALS['__view'](app/init.php:97已设),
再 $view->include('plugins/daily_checkin/_sidebar', $extra);此时 $this 在 require 作用域内可用。
- ⚠️
include($tpl, $extra)(TemplateCompiler.php:284-296)是array_merge($this->data, $extra)后extract,
不会把 $extra 写回 $this->data → 走钩子路径时 $this->getData('week_days') 取不到。
结论:模板里取数据一律用普通变量(
$week_days/$checkin_today),禁止$this->getData()。
$this->e()/$this->t()/$this->url()/$this->icon()照常可用。
需求③ 用户栏的签到位 → 改发帖按钮
right_sidebar.php:179-183:
<?php if ($isBlogPage): ?>
…blog.new…
<?php else: ?>
<?php $this->include('plugins/daily_checkin/_sidebar'); ?> ← 换成发帖按钮
<?php endif; ?>改为:
<?php if ($isBlogPage): ?>
<?php echo $this->icon('write', 12); ?> <?php echo $this->t('blog.new'); ?>
<?php else: ?>
<?php echo $this->icon('new-post', 12); ?> <?php echo $this->t('forum.new_thread'); ?>
<?php endif; ?>- 落点
/post/new、文案forum.new_thread、图标new-post—— 与详情页发帖人卡(right_sidebar.php:96)完全一致,零新文案 - 博客页保持
/blog/new(blog.new)不变
需求④ 个人信息 / 发帖按钮 平均分布宽度
现状 .mn-user-actions(assets/css/modern.css:1206)= display:flex; gap:6px;,
两个按钮各带 mn-flex-1(flex:1; min-width:0)→ 天然 50 / 50。
- 「个人信息」按钮
:178已有mn-flex-1 - 新的发帖按钮按 §3.3 写法也带
mn-flex-1
→ 无需新增 CSS。这条本质是"确认现状 + 保证新按钮不掉类"。文字长短不影响等宽。
需求⑤ 用户栏「个人信息 / 发帖按钮」上方新增常用操作钩子(钩子A,每行 2 列)
核心改动:在 .mn-user-stats(:167-176)之后、.mn-user-actions(:177)之前插入
<?php
ob_start();
\app\Helpers\Plugin::hook('right_sidebar_user_actions_above');
$quickActions = ob_get_clean();
?>
<?php if (trim($quickActions) !== ''): ?>
<?php echo $quickActions; ?>
<?php endif; ?>- 只在已登录分支内(
$isLoggedIn && $currentUser)→ 游客不触发 - ⚠️ 用
ob_start+trim() !== ''判空,不要用 CSS:empty—— PHP 会输出换行/缩进空白,
:empty 会失效,空 div 白留 12px 高度
- ⚠️ 渲染钩子异常被
Plugin::hook()内部 try/catch 吞掉(Plugin.php:652)
→ 出问题时表现是"这块不显示",只在 protected/error.log 留一行
核心 CSS(modern.css 新增,约 4 行)
.mn-user-quick { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 12px; }(2 列等宽,间距与 .mn-user-actions 的 gap:6px 对齐)
钩子契约:插件在这个钩子里只输出裸条目(如 <a class="mn-btn mn-btn-sm">…</a>),
不要自己包容器/网格 —— 网格由核心提供,才能保证"每行两列"。
命名候选(→ §四 决策 4)
right_sidebar_user_actions_above—— 与sidebar_above_friend_links同构,语义直白(推荐)right_sidebar_user_quick—— 更短,但语义模糊
风格三选(→ §四 决策 1,就是站长问的那个)
| 方案 | 长什么样 | 代价 |
|---|---|---|
| ① 与个人信息/发帖按钮一致 | 每格一个 mn-btn mn-btn-sm(带边框圆角) | 视觉最统一、零新增 CSS;但 4 个操作 = 2 行实心按钮,整卡偏高 |
| ② 单纯图标 + 文字 | 每格「图标 + 文字」一行,无边框,hover 才出浅底 | 最紧凑(高度约按钮的 6 成)、最"极简";但与下方按钮风格断层,紧挨着会显得两块不像一家人 |
| ③ 混合(无边框小按钮) | 结构同 mn-btn-sm,但透明底 + 无边框,hover 出底 | 兼顾统一与紧凑;需插件自带类,或核心加一个 mn-btn-ghost 变体(多一处核心改动) |
容量与视觉边界(实测尺寸,别拍脑袋)
右栏 --mn-right-width: 260px(modern.css:2655);.mn-panel padding 16px + border 1px
→ 卡片内容宽 226px;钩子A 2 列 + gap 6px → 每格 110px;
.mn-btn-sm padding 5px 12px → 文字可用宽 84px → 12px 字号下每格 ≈ 图标 + 5 个中文字。
| 项 | 数值 |
|---|---|
| 每格宽度 | 110px(文字可用 84px) |
| 每行高度 | ≈ 29px(12px×1.4 + padding 5×2 + border 1×2) |
| 行间距 | 6px(与 .mn-user-actions 对齐) |
| 4 项 | 2 行 ≈ 64px |
| 6 项 | 3 行 ≈ 99px |
| 8 项 | 4 行 ≈ 134px |
数量没有硬上限 —— grid-template-columns: 1fr 1fr 会自动换行,N 项 = ⌈N/2⌉ 行,高度自适应。
但有 4 个真实边界:
- ⚠️ 奇数项会留半格空:CSS grid 不会拉伸最后一项 → 5 项 = 3 行,最后一行右边空着。
→ 要么接受(推荐,最简单),要么让某一项 grid-column: span 2 独占整行(需插件自带类)。
- ⚠️ 栏目名必须短:每格只有约 5 个中文字的余量。"我的收藏""我的帖子""草稿箱""消息"都放得下;
"个人资料设置"(6 字)会挤。建议 ≤5 字。
- ⚠️ 高度会顶穿:8 项 ≈ 134px;用户卡本身还有 44px 头像 + 2 个统计格 + 按钮行 ≈ 190px
→ 整卡约 330px,右栏首屏基本被它占满。建议 ≤6 项。
- ⚠️⚠️ 只在桌面端可见:
.mn-right { display: none }在@media (max-width: 1024px)(modern.css:2121-2124)
→ ≤1024px 右栏整体消失(是 1024px,不是 768px;768px 那档是 .mn-mobile-sidebar 才出来)。
→ 钩子A 里的栏目在平板/手机上完全看不到,不要往里放"只有这里能进"的功能入口。
不渲染的三种情况
- 未登录 → 钩子在已登录分支内,不触发
- 一个插件都没挂 →
ob_start判空 → 容器整块不输出 - ≤1024px → 随右栏一起消失
跨插件顺序:由各自 plugin.json 的 sort_order 决定(Plugin::sortPlugins() 按 sort_order → seq);
插件内部的项序自己控。要固定顺序,就得给每个插件配 sort_order。
容量建议
| 预计项数 | 建议 |
|---|---|
| ≤4 项 | 方案①(带边框按钮),2 行以内,零新增 CSS |
| 5~6 项 | 方案③(无边框小按钮)压高度;或方案① 但接受 3 行 |
| >6 项 | 别都堆这儿 → 另开一个带标题的独立面板走钩子B(可分类、可折叠,且不受 110px 窄格限制) |
规格同步:docs/插件开发规范.md §9 钩子表新增第 42 行(当前 41 个),
并在 §11 补一条"右栏常用操作钩子的容器契约(只输出裸条目,不包容器)"
+ "每格约 110px / 栏目名 ≤5 字 / 建议 ≤6 项"。
需求⑥ 用户栏下方(同级)新增钩子(钩子B)
要什么:在用户栏面板外面、与用户栏同级的位置,加一个供插件挂独立面板的挂载点 ——
形态与排行榜 / 最新会员 / 站点统计一致(插件自己输出 <div class="mn-panel">…</div>)。
⚠️ 关键事实:这个位置已经有钩子了。
| 项 | 值 |
|---|---|
| 钩子名 | right_sidebar_after |
| 编号 | 规范 §9 #14 |
| 触发点 | app/Views/_components/right_sidebar.php:210 |
| 位置 | 用户面板 <?php endif; ?>(:206)之后、站点统计(:213)之前 —— 正是"用户栏下方、同级" |
| 参数 | 无 |
| 触发次数 | 单触发(PC 右栏;mobile_sidebar.php 里没有它) |
| 核心注释原文 | "右栏通用钩子:用户信息面板之后,供插件注入侧栏面板" |
| 现有占用 | dice(停用)/ qa(启用,只认 /qa*)/ quiz_duel(停用) |
✅ 决策 6 已定:选路径 1(复用 right_sidebar_after),零核心改动。
✅ 采用 · 路径 1:复用 right_sidebar_after,零核心改动
- 什么都不用加,位置天然正确。
- 先后顺序由
plugin.json的sort_order控制(Plugin::sortPlugins()按sort_order → seq排;
实测 new_members 落 index 37、leaderboard 15 → sort_order 强于字母序)。
- 代价:与
dice/qa/quiz_duel共享同一个钩子位。实际不冲突(两个停用、qa 只认/qa*),
且按站长判断语义足够干净,接受共享。
❌ 未采纳 · 路径 2:新增专用钩子 right_sidebar_user_below(留档备查)
- 原方案:插在
right_sidebar.php:206(<?php endif; ?>)与:210之间。 - 好处:语义明确、顺序稳定、不受其他插件干扰。
- 未采纳原因:与
right_sidebar_after位置/语义高度重叠,属重复建设;且会让规范 §9 从 41 → 43 个钩子,
后人容易挂错。若将来真的需要,再单独开一项。
钩子契约(两条路径通用)
- 插件输出完整面板:
<div class="mn-panel"><h3 class="mn-panel-title">…</h3>…</div> - 无数据一律
return,不要输出空面板占位(站内通例:① 满站刷屏;② 会造出死按钮) - 渲染钩子在闭包作用域 →
$this不可用,一律用htmlspecialchars($s, ENT_QUOTES, 'UTF-8')+Csrf::token() - ⚠️ 若将来该钩子要进移动端,需同步加进
mobile_sidebar.php→ 钩子从"单触发"变"双触发",
必须同步改规范 §9,并回归 dice / qa / quiz_duel
对本方案的影响(已定):需求② 的「本周签到面板」维持挂 right_sidebar_after + sort_order,不改动。
四、决策(已定稿 · 2026-09-27 站长拍板)
| # | 决策 | 选项 | ✅ 决定 |
|---|---|---|---|
| 1 | 钩子A 的视觉风格 | ①与按钮一致 / ②纯图标+文字 / ③混合 | ① 与按钮一致,先按 ≤4 项设计 |
| 2 | 详情页是否也显示「本周签到」面板 | ①全站显示 / ②仅非详情页 | ① 全站显示 |
| 3 | 周视图起始日 | ①周一起 / ②周日起 | ① 周一起(M T W T F S S) |
| 4 | 钩子A 命名 | ①right_sidebar_user_actions_above / ②right_sidebar_user_quick | ① right_sidebar_user_actions_above |
| 5 | 移动端是否同步 | ①本次不动 / ②给 mobile_sidebar.php 也加 | ① 本次不动 |
| 6 | 用户栏下方钩子(钩子B) | ①复用 right_sidebar_after / ②新开 right_sidebar_user_below | ① 复用 right_sidebar_after |
决策带来的三个简化(都朝"更少改动"方向)
- 决策 6 选① → 钩子B 零核心改动:
right_sidebar_after已存在(:210),不新增钩子位。
→ 规范 §9 钩子表只 +1 行(钩子A),不是 +2;需求② 的周视图面板维持挂 right_sidebar_after。
- 决策 1 选① + ≤4 项 → 钩子A 零新增组件样式:直接复用
.mn-btn.mn-btn-sm;
modern.css 只需加 .mn-user-quick(2 列网格容器)那 4 行,不需要 mn-btn-ghost 之类的变体。
- 决策 5 选① → 钩子触发次数不变:
right_sidebar_after保持单触发,
不用改规范 §9 的"触发次数"列,也不用回归 dice / qa / quiz_duel 的双触发行为。
实施时需要一并落实的(来自决策的推论)
- 决策 1「先按 ≤4 项设计」 →
.mn-user-quick就用grid-template-columns: 1fr 1fr+gap: 6px,
不做 span 2、不做奇数项补齐。将来项数涨了再议(§3.5 容量建议:>6 项改走钩子B)。
- 决策 2「全站显示」 → 周视图面板的钩子
right_sidebar_after不加任何 URI 白名单,
与 leaderboard / new_members 的处理一致(它们也不做模板白名单)。
- 决策 3「周一起」 →
date('monday this week')~date('sunday this week');
_area.php 的月份日历表头(日一二三四五六)不动,两处起始日不同属已知差异,会在插件 说明.md 注明。
容量依据(§3.5 实测):每格仅 110px(约 5 个中文字)、每行 ≈ 29px。
站长已确认先按 ≤4 项设计;若将来涨到 5~6 项,建议改用无边框小按钮压高度;
>6 项则改走钩子B 的独立面板。
五、改动文件清单(定稿)
核心(本次无法避开)
| 文件 | 改动 |
|---|---|
app/Views/_components/right_sidebar.php | 删 2 处(:182、:185-194);:179-183 换发帖按钮;:176/:177 之间插入钩子A 容器(right_sidebar_user_actions_above)。钩子B 复用已有 :210,本文件该处不动 |
assets/css/modern.css | 新增 .mn-user-quick(约 4 行) |
docs/插件开发规范.md | §9 钩子表 +1 行(钩子A right_sidebar_user_actions_above,总 41 → 42);§11 补容器契约 + 容量约束(每格 110px / 栏目名 ≤5 字 / 建议 ≤6 项) |
插件 daily_checkin
| 文件 | 改动 |
|---|---|
plugin.json | hooks 增 right_sidebar_after;加 sort_order 固定位置 |
hook/right_sidebar_after.php | 新增 —— 输出周视图面板($GLOBALS['__view']->include() 复用模板) |
hook/controller_view_before.php | 查询由"今日"改"本周",注入 week_days / checkin_today / checkin_consecutive |
views/_sidebar.php | 重写为周视图面板(钩子 + htmx 回包共用) |
CheckinController.php | compact 回包模板名不变(仍 _sidebar);若换名需同步 |
assets/style.css | 新增 dc-week-* 样式 |
lang/{zh,zh_tw,en}.php | 新增键(msg_tomorrow 等);三语必须同键集合(基线:三语各 1343 键、零差异) |
说明.md | 更新挂载点与视图说明 |
⚠️
daily_checkin目录名含下划线 →Plugin.php末尾必须有
class_alias(Plugin::class, 'Plugin\daily_checkin\Plugin', false),否则生命周期静默跳过(建表/卸载都会出问题)。实施前先核一遍现状。
六、验证方式(我跑逻辑,界面你看)
静态
php -l全部改动文件- i18n 引用键 ↔ 三语字典全量比对(判据 = 键集合零差异,不是"值非空")
- 核心
right_sidebar.phpgrepdaily_checkin→ 必须 0 命中 - 新增 CSS 变量脚本核对 9 套主题覆盖(本次已预核,见 §3.2)
逻辑(HTTP 冒烟,php -S,启动 + curl + kill 同一个 Bash 调用)
| 场景 | 期望 |
|---|---|
| 停用 daily_checkin,登录态 | 右栏无签到痕迹;用户卡 = 个人信息 + 发帖两按钮等宽;整页无 plugin.daily_checkin 字样 |
| 未登录 | 钩子A 不触发;显示登录 / 注册 |
| 启用 daily_checkin,登录且今日未签到 | 周视图 7 格、今日格高亮;按钮可点;htmx POST 后原地变"已签到" |
| 已签到 | 绿胶囊 + "明天见" |
| 无常用操作插件 | 钩子A 的 .mn-user-quick 容器不输出(不是空 div) |
| 无挂钩子B 的插件 | 用户栏下方不出现空面板 / 占位块,站点统计紧接用户栏 |
详情页(/thread/{id}、/blog/{id}) | 周视图面板照常显示(决策 2 = 全站显示,钩子无 URI 白名单) |
| 右栏宽 ≤1024px | 右栏整体 display:none → 钩子A / 钩子B 内容都不出现(预期行为,非缺陷) |
回归:排行榜 / 最新会员 / qa 侧栏(/qa*)三个占用侧栏钩子的插件不回归;
另外确认 dice / qa / quiz_duel 的 right_sidebar_after 输出未受影响(钩子B 复用它,不新增钩子位);
改完清一次前台页面缓存(/、/forum、/forum/category/N 有前置缓存,仅游客)后复测。
七、风险与回滚
| 风险 | 说明 | 对策 |
|---|---|---|
| 核心视图改动影响全站右栏 | right_sidebar.php 被 main.php:341 无条件 include | 改动点少且局部;改前先存快照,改后比对 |
钩子在闭包作用域($this 不可用) | 误用 $this->e() → fatal 被 try/catch 吞成"面板整块不显示"、HTTP 仍 200 | 钩子内一律 htmlspecialchars() + Csrf::token();发真实 HTTP 并看 protected/error.log |
| 模板双入口取数方式不同 | $this->getData() 在钩子路径取不到 | 模板统一用普通变量(§3.2) |
| 未启用插件语言包不合并 | 新键若只在插件包 → 未启用时吐键名 | 面板整块由钩子输出(未启用 = 不执行),不会吐键名 |
| 页面缓存 | /、/forum、/forum/category/N 有前置缓存 | 改完走后台「维护 → 清理缓存」 |
回滚:全部改动可逆向(删新增文件 + 还原 3 个被改文件)。改前先存
right_sidebar.php / modern.css 快照 + sha256 基线。
八、实施计划(分 3 阶段,每阶段末暂停确认)
原则:先动核心、后动插件;每阶段独立可验证;阶段末停下来汇报,等确认再进下一阶段。
阶段 1 —— 核心视图解耦 + 发帖按钮 + 钩子A(改动最小、可独立验证)
- 存
right_sidebar.php/modern.css快照 + sha256 基线 - 删
right_sidebar.php:182、:185-194(解耦) :179-183换成发帖按钮(/post/new+forum.new_thread):176/:177之间插入钩子A 容器(ob_start判空)modern.css新增.mn-user-quick(4 行)- 验证:
php -l;grepdaily_checkin= 0 命中;HTTP 冒烟(登录 / 未登录两态,确认无键名泄漏、两按钮等宽、无钩子插件时容器不输出) - 暂停汇报 —— 此时签到功能会短暂消失(插件还没挂钩子),属预期
阶段 2 —— 签到插件周视图面板
hook/controller_view_before.php:查询由"今日"改"本周",注入week_days/checkin_today/checkin_consecutiveviews/_sidebar.php重写为周视图面板(周一~周日 7 格,模板内只用普通变量)- 新增
hook/right_sidebar_after.php($GLOBALS['__view']->include()复用模板) plugin.json加right_sidebar_after钩子 +sort_orderassets/style.css新增dc-week-*lang/{zh,zh_tw,en}.php同步加键(msg_tomorrow等)—— 三语键集合必须零差异说明.md更新(含"周一起 vs_area.php周日起"的已知差异)- 验证:三语键集合比对;HTTP 冒烟(未签到 / 已签到 / htmx POST 原地刷新 / 详情页也显示);看
protected/error.log无新增 - 暂停汇报
阶段 3 —— 规范同步 + 回归 + 打包
docs/插件开发规范.md:§9 钩子表 +1 行(钩子A,总 41 → 42);§11 补容器契约 + 容量约束- 回归:排行榜 / 最新会员 / qa 侧栏 / dice / quiz_duel
- 清前台页面缓存(后台「维护 → 清理缓存」)
- 打包
update/39(含version.php+改动说明.md六节体例) - 暂停汇报
九、遗留(与本次无关,等你一句话)
update/38已交付,但站点根version.php仍是V1.0.31(包内写的是V1.0.38)→ 是否顺手升?update/38只有目录、没有 zip → 要不要补一个?