[topic_readonly]主题只读增强热
- 主题只读增强ID topic_readonly版本 1.2.1插件制作者 one免费855 行 / 45.6 KBHook / 路由 / 后台页仅拥有管理权限的用户可将主题设为只读并禁止新增回帖;只读后原作者不可编辑主题、不可编辑/删除自己的回帖,pin/置顶、highlight/高亮、bold/加粗等自管动作同步被收回;可配置管理权限用户是否可绕过只读回复或编辑(两个开关默认关闭,最严格);每次锁定/解锁/编辑主贴/编辑回帖/删除主贴均记录审计日志,保留期由 cron 自动清理。app/plugins/topic_readonly/plugin.php开发日志已有 5 条
个人独立维护项目。
主楼点赞与点评3one2026-08-17
bbs1org2026-08-18
charcoal-fire2026-08-18
插件质量报告
插件:
topic_readonlyHook 功能范围 循环 使用频率 文件读写 修改系统表 读写系统表 读写自己的表 app.boot 全站请求启动 否 5 - - - - page.head 全站页面 head 否 5 - - - 读 plugin_topic_readonly_topics reply.after_render 主题查看页/回帖列表逐条渲染 是 5 - - - - topic.index_data.loaded 首页/版块/用户主题列表查询后 否 5 - - - 读 plugin_topic_readonly_topics topic.title_suffix 首页/版块主题列表逐条标题 是 5 - - - - topic.after_view 主题查看页完成加载 否 4 - - - 读 plugin_topic_readonly_topics reply.after_save 回帖保存后 否 3 - - app_replies 写 plugin_topic_readonly_logs;读 plugin_topic_readonly_topics reply.before_save 回帖发布/编辑表单提交 否 3 - - app_replies 读 plugin_topic_readonly_topics topic.actions 主题查看页操作区(展示位置:主题操作) 否 3 - - - 读 plugin_topic_readonly_topics topic.after_save 主题保存后 否 3 - - - 写 plugin_topic_readonly_logs;读 plugin_topic_readonly_topics topic.before_save 主题发布/编辑表单提交 否 3 - - - 读 plugin_topic_readonly_topics content.before_delete 内容删除操作 否 2 - - - 写 plugin_topic_readonly_topics, plugin_topic_readonly_logs reply.form_extra 回帖编辑表单 否 2 - - - 读 plugin_topic_readonly_topics topic.can_manage 主题管理权限检查 否 2 - - - 读 plugin_topic_readonly_topics 颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。
数据字典
plugin_topic_readonly_topics字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_topic_readonly_logs字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 给主题加一把"管理员专属的只读锁",锁上后所有人不能回帖/编辑,且每一次开锁、关锁、编辑、删除都被记录下来。
主要实现的功能
🔒 主题只读开关(核心)
仅管理员/管理权限用户可在前台主题详情页通过锁形图标一键切换"只读/可回帖"
开启后服务端硬拦截:所有用户(含原作者)都不能新增回帖、不能编辑主贴、不能编辑自己的回帖、不能编辑他人的回帖
前端按钮/表单同步禁用,但真正的防线在服务端,前端绕过也无效
两个管理权限放行开关(后台配置)
允许管理权限用户回复:开启后,管理权限用户在只读主题下仍可回帖
允许管理权限用户编辑:开启后,管理权限用户仍可编辑主贴与回帖
两个开关独立控制,默认关闭(最严格)
📋 操作记录与审计(后台面板)
记录 6 类操作,全部带操作人(头像+用户名)+ 时间 + 楼层号

@one 已下载 1.0.1,并按 bbs1org v8.5.23 的实际删除/保存流程复查。整体的批量预取和服务端回复/编辑拦截思路是对的,但审计功能目前有几处会与说明不一致:
- 删除主贴日志会先 INSERT,紧接着同一回调又执行
DELETE FROM plugin_topic_readonly_logs WHERE topic_id=?,所以delete_topic记录必然立即消失。应保留删除日志,或单独保存主题快照后再清理其他记录。 - 删除回帖是一个新的
/deletePOST 请求,不会先触发topic.after_view;content.before_delete使用topic_readonly_cached(),缓存通常为空,因此delete_reply基本不会写入。这里应调用topic_readonly_record($topic_id)主动读取。 - 核心删除回帖权限使用
can_manage_reply(),没有reply.can_manageHook。当前插件只在content.before_delete记日志,没有阻止操作,因此只读后回复作者仍可删除自己的回复。如果“只读”也要求禁止删除,应在该 Hook 中按管理权限/后台开关服务端err()。 - 后台设置虽然输出了
form_token(),POST 分支没有调用require_post(),而插件后台 Tab 不会由核心自动校验 CSRF。建议改成if (is_post_request()) { require_post(); ... }。 topic.before_save在核心处理 pin/unpin/delete 等 action 之前执行;当前忽略$ctx['action'],只要开启管理员编辑权限,置顶、取消置顶或删除也会先被记录成一次edit_topic。此外日志在真正 UPDATE 前写入,后续校验/写库失败也会留下“成功编辑”的记录。更稳妥的是在 after_save 记录成功编辑,并只接受 editing=true。
建议至少先修复 1–4 再升版;现在质量报告没有标红,是因为这些属于流程/权限正确性问题,不是循环查询问题。
- 删除主贴日志会先 INSERT,紧接着同一回调又执行
版本 1.0.5 更新:
- 服务端兜底权限:修复回帖"编辑/删除"入口仅靠前端 JS 隐藏的缺陷,改用
reply.after_render+reply.form_extra+reply.before_save三重服务端收口,杜绝越权绕过。 - 删除拦截修正:
content.before_delete对软删除的回帖不触发,移除其中无效的回帖删除拦截与delete_reply伪日志,避免后台出现恒为 0 的假指标。 - 权限入口一致性:
topic.can_manage纳入"编辑开关"判断,使主题编辑入口的可见性与实际保存结果一致(开关关闭时不再出现"可点却报错")。 - 审计完整:
edit_topic由topic.after_save统一记录,覆盖管理员自身的编辑操作。 - 零注入:所有 SQL 参数化(
sql_marks()占位符),所有输出经h(),URL 经route_url()/admin_url();切换路由经need_login()+require_post()+form_token()+ 动作白名单校验。 - 最小权限:两个开关默认关闭(最严格);无
exec/外部请求,JS 仅视觉层。 - 后台"操作记录"增加按动作(关闭只读/打开可回帖/编辑主贴/编辑回帖/删除主贴)的可点击筛选。
- 服务端兜底权限:修复回帖"编辑/删除"入口仅靠前端 JS 隐藏的缺陷,改用
版本 1.0.7 更新:
几个小细节的修复。🔍 插件审查报告(对照《AI 开发规则》与开发者文档)
🔒 安全问题
未发现明显问题。切换只读路由
need_login()+require_post()+is_manager且带 CSRF;编辑/回帖拦截在before_save服务端兜底;tx()内无嵌套事务;所有输出经h()转义。权限模型清晰、闭环完整。⚡ 性能问题
未发现明显问题。
topic.after_view与topic.index_data.loaded批量预取只读记录到请求级缓存,topic.title_suffix/reply.after_render红区钩子仅读内存,零 DB 读;楼层号单条 SQL 计算。🐛 功能/规范缺陷
plugin.php:574的border-radius:4px写死数值,建议改用var(--radius)派生变量。
✅ 修复优先级
仅一处 CSS 圆角变量,低优先级。
🔍 复核报告(v1.0.8,回应此前审查)· @one
✅ 已确认修复
上一轮仅一条 border-radius 低优先级问题,其余安全/性能/权限已通过。本次核对维持正确:切换路由 need_login()+require_post()+is_manager 且带 form_token(plugin.php:393-402);before_save 服务端兜底(plugin.php:295-333);reply.after_render 红区仅读请求级缓存(plugin.php:266-279,由 topic.after_view 的 208 预取兜底);列表页批量 IN 预取(plugin.php:86-100)。
❌ 未生效
- 圆角仍写死:plugin.php:570 .topic-readonly-tip code{border-radius:4px;...} 仍是写死数值,建议改 var(--radius-sm)。
💡 顺带建议
reply.after_render 用正则剥除操作入口(plugin.php:274-278)依赖核心 icon-* class 前缀,若核心改样式可能漏删,但已有 before_save/form_extra 双兜底,风险低。
总结:仅剩 1 处 CSS 圆角写死(低优先级),插件干净。
🔍 插件审查报告(AI 自动审查,对照《AI 开发规则》与开发者文档;本插件未被 8/18 楼上 350 的报告覆盖,属独立首次审查)
插件:主题只读增强(topic_readonly)v1.0.8
结论:已完成命名规范、生命周期(install/uninstall 幂等)、数据库跨库兼容、红区 Hook 零 DB 读、Hook 真实性(含核心与跨插件依赖核对)、SQL 注入风险、CSRF/权限校验、CSS 变量规范等项审查,未发现安全、性能或功能性问题。
版本 1.2.1 更新:
Changed
- 管理权限判断改为直接复用核心
can_manage():核心权限模型已从用户级字段迁移到用户组级(app_groups.allow_manage),原插件内手工复制的is_super_user() || me()['allow_manage']判定与新模型存在漂移风险,统一收口到核心函数。 - 前端 JS 定位核心页面元素改用 data-slot 插槽锚定:移除回帖编辑入口改用
[data-slot~="reply.after_render"] a.icon-edit,回复面板状态栏改由插件自带data-topic-readonly-notice标记沿.reply-panel定位,不再依赖核心内部层级选择器(.topic-post-list等),适配核心 2026-08-28 引入的插槽机制。
Fixed
- 修复"编辑回帖"审计日志在保存失败时也落库的问题:日志写入从
reply.before_save(保存前,可能被核心正文校验拦截)迁移到新增的reply.after_save回调(editing=true且保存成功后),与edit_topic(topic.after_save记录)行为保持一致,不再产生假审计记录。
- 管理权限判断改为直接复用核心