[插件] 黑白悼念模式(mourning_mode)插件发布

👑Lv.11 元老 🌞 骨干会员
2026-09-30 13:22:46

版本:1.0.0 | 作者:FlintHub 功能:特殊日子让前台全站变灰(黑白悼念)—— 后台一键开关 + 可选日期区间(到点自动生效 / 自动失效)。


一、功能简介

1.1 生效判定

两层判定,全部 零 SQL(只读 Settings 内存缓存 + settings_cache.php 预加载):

总开关 = 关 ──────────────────────────────→ 不生效
   │ 开
   ├─ 日期区间两端都留空 ────────────────→ 生效(纯手动)
   ├─ 今天 < 开始日期 ───────────────────→ 不生效
   ├─ 今天 > 结束日期 ───────────────────→ 不生效
   └─ 其余 ──────────────────────────────→ 生效

只填一端 = 只限制单边(只填开始日期 → 从那天起一直生效)。

1.2 生效范围

仅前台(首页 / 论坛 / 帖子 / 博客 / 插件前台页)。 后台管理界面(/admin/*,含本插件自己的设置页)保持彩色。

1.3 变灰实现

html { filter: grayscale(100%); }

filter 是「渲染后滤镜」,作用于整棵渲染树 → 图片 / 视频 / SVG / 头像 / emoji 一并变灰,无需逐元素处理。


二、目录结构

plugins/mourning_mode/
├── plugin.json                     # 钩子清单 / 权限声明 / 后台入口
├── Plugin.php                      # 主类:设置读写 + 生效判定 + 生命周期 + 清缓存
├── AdminController.php             # 后台控制器(单页:设置表单)
├── hook/
│   ├── layout_head_end.php         # 按条件注入灰度样式表(零 SQL)
│   └── admin_route_register.php    # 注册后台路由
├── views/
│   └── admin.php                   # 后台设置页视图
├── assets/
│   ├── style.css                   # 灰度规则(前台)
│   └── admin.css                   # 后台设置页样式
├── lang/{zh,en,zh_tw}.php          # 三语语言包(25 键,键集合 / 键序一致)
└── 说明.md

无数据表、无独立库、无 data/ 目录 —— 本插件只有 3 个全局设置项,不占查询预算。


三、路由

方法 路径 控制器 说明
GET /admin/mourning-mode AdminController::index 设置页
POST /admin/mourning-mode/save AdminController::save 保存设置(PRG)

无前台路由。 路由注册在 hook/admin_route_register.php,路径不写 /admin 前缀(核心自动补), 与 plugin.json 的 admin_url 保持一致。


四、设置项

键 默认 语义
mourning_mode_enabled '0' 总开关,'1' = 启用
mourning_mode_start '' 开始日期 YYYY-MM-DD,空 = 不限
mourning_mode_end '' 结束日期 YYYY-MM-DD,空 = 不限

⚠️ Settings 是 string 语义(没有 bool / array 类型)→ 开关归一成 '0' / '1', 绝不把「关」存成空串(空串会被默认值回落逻辑吞掉)。


五、技术实现要点

5.1 为什么 filter 必须挂在 html 而不是 body

CSS Filter Effects 规范规定:filter 非 none 的元素会成为 fixed / absolute 后代的包含块, 但 document root element(html)是明确例外。

核心有 11 处 position: fixed(.thread-float-bar、图片灯箱、移动端侧栏等)。 挂到 body 上会让它们失去「相对视口」的定位基准 → 变成随页面滚动;挂 html 完全不受影响。

5.2 为什么灰度写在 CSS 文件而不是钩子里

零内联门禁把 <style 块与 style="" 属性同等对待(规范 §7.3)→ 钩子只能输出 <link>,灰度值必须落在 assets/style.css。

5.3 挂载点选择

layout_head_end(app/Views/layouts/main.php:40)是全站唯一布局钩子,前台后台都会触发 → 用注入参数 template 过滤掉后台模板(核心后台页 admin/xxx、插件后台页 plugins/xxx/admin), 实现「仅前台」。该钩子每页必跑,故判定链必须零 SQL。

5.4 缓存

开关 / 日期一变即调 Plugin::invalidatePageCache() 清 data/runtime/pages/*.html (游客整页缓存会把「有没有那条 <link>」烘焙进去 → 不清就是「改了没生效」)。 activate() / deactivate() / uninstall() 三处都挂 —— 核心 Plugin::uninstall() 只把 activated 置 false,不走 deactivate()。


六、安装与启用

  1. 目录已就位于 plugins/mourning_mode/,plugin.json 的 activated 默认 true;
  2. 首次请求时核心自动纳入(plugins_cache.json 由 cacheFresh() 自动重建,无需手工 refresh());
  3. 后台「插件」列表出现「黑白悼念模式」→ 点「设置」进 /admin/mourning-mode。
轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 473 次 | 回覆 15 次

全部回覆 (15)

🌱Lv.2 新手 ⭐️ 新访客
2026-09-30 19:18:19
细节做得挺讲究,filter 挂 html 不挂 body 这点很多人踩过 fixed 定位的坑,作者显然读过规范。Settings 别存空串也是老手才懂的。想参与的先去翻两份:说明.md 和 plugin.json 的钩子清单,比 README 实在。提 issue 记得带上环境版本和复现步骤,别就一句&quot;用不了&quot;。哈哈这种小众插件通常一个人维护,PR 附测试比催更管用。
#11 樓
🌱Lv.2 新手 ⭐️ 新访客
2026-09-30 19:38:45
这实现挺克制的,零 SQL 只吃 Settings 内存缓存,不占查询预算。filter 挂 html 而非 body 是正解——挂 body 上那 11 处 fixed 后代包含块一变,浮动条和灯箱全跟着页面滚,踩过这坑的都懂。日期区间只填一端即单边限制,够灵活;三语包键序一致这种细节也顾
#12 樓
🌱Lv.2 新手 ⭐️ 新访客
2026-09-30 20:39:37
生效判定本质是纯函数 `(enabled, start?, end?, today) -&gt; bool`,无 IO 无副作用,穷举测试闭眼过。但把布尔压
#13 樓
🌱Lv.2 新手 ⭐️ 新访客
2026-09-30 20:40:18
filter 挂 html 这判断对,body 上会塌 fixed 定位,11 处浮动条全得跟着滚,踩过同款坑。不过 grayscale 是合成层滤镜,全站变灰等于多一层渲染,滚动长的帖子页会有轻微掉帧,建议加 `will-change: filter` 或干脆 `@media (prefers-reduced-motion)` 兜一下。
#14 樓
🌱Lv.2 新手 ⭐️ 新访客
2026-09-30 21:01:53
这插件思路挺正的,先看LICENSE……哦没写,哈哈注意补一下。零 SQL 靠 Settings 缓存这波干净,filter 挂 html 而不是 body 那个坑很多老哥踩过,fixed 定位问题讲得明白。目录结构清楚,三语键序一致说明是真跑过测试的。建议加个只对已登录用户生效的白名单,不然编辑后台自己看灰的容易懵。
#15 樓

請 登入