[community_calendar]社区日历热
- 社区日历ID community_calendar版本 4.3.0插件制作者 cxk123售价 30 积分4542 行 / 260.4 KBHook / 路由 / 后台页4.3.0:**运行期结构自愈**(把新版文件覆盖进插件目录、没跑 install 时,老库缺表缺列不再让核心把整个插件自动停用 —— 全部碰库入口先走「零查询内存快路径 → settings → 才 DDL」三道门,落后时按安装同一份定义幂等补齐 4 张表 / 10 枚索引 / 9 个增量列,并把该补的那一代数据迁移一起补上)。4.2.0:修「找共同空闲时间」页参与人输入框的反射型 XSS(回显未转义);4.1.0:导入升级与并发加固(重复导入可选「跳过」或「用文件内容原位更新」;源日历重新生成 UID 时用「标题+开始+结束」指纹兜住,不产生第二份;TZID 时区事件按站点时区正确落库;逐期提醒与「取消这一期」用期次级 + 缓存令牌双 CAS,并发计划任务下不重复通知、也不误复活);4.0.0:iCalendar 导入(粘贴 .ics 就把别的日历搬进来,UID 幂等、重复导入不产生第二份)· 重复日程**逐期**提醒(取消的那一期不提醒、改期的那一期按新时间提醒)· 时间冲突检查(未来 N 天「同一时段两件事」一次列全)· 例外日随行缓存(首页语句数 2 → 1,查询预算不再随内容变化);把站里所有「有日子的事」汇成一张日历并一键订阅进手机(3.0.0:重复日程例外日 · 共同空闲时段推荐 · 订阅 ETag 增量同步与失效自愈 · 与活动报名双向联动):月历 / 周历 / 列表三视图,月历格子右上角「+」直接在那天新建日程(日期已预填);每个人都能生成自己的 ICS 订阅地址(webcal 一键订阅 / Android 与 Outlook 用 http、配一张扫码即订的二维码),手机日历每小时自动同步,不用回站刷新;直接聚合「活动报名」的活动与场次、读「问卷与投票」的截止时间,与自定义日程合并成同一张日历(对方插件没装时自动降级、零查询);支持分类筛选、公开/私密日程(私密内容对他人与订阅源完全不可见)、提前 N 分钟站内提醒(计划任务 CAS 去重)、订阅链接可重置可撤销、单条日程一键下载 .ics 进日历;2.0.0 新增一键「我要参加」(人数与名单、我参加的筛选、可只订我参加的)、重复日程(每天/每周/每两周/每月,只存规则、展示与订阅时自动展开)、侧栏「今日日程」卡片。收费插件安装说明购买后点击“授权码”获取一次性授权码;在后台 > 插件广场点击在线安装或更新,并填写该授权码。已购买用户更新免费:每次在线安装或更新前,重新点击“授权码”获取新的一次性授权码即可,无需再次购买;也可直接下载新版后用后台“插件上传”更新。尊重并感谢他人劳动,请勿传播代码。app/plugins/community_calendar/plugin.php插件预览6 张开发日志最新 10 条
此项目接受所有登录用户协助维护和提交更新。
主楼 版本 1.0.0 更新:
1.0.0 首次上架:社区日历(把站里「有日子的事」汇成一张日历,一键订阅进手机)
一、这个插件解决什么
站里的「有日子的事」散在三处:活动报名里的活动与场次、问卷与投票的截止时间、以及各种只存在于帖子里的通知(开会、维护、考试、开播)。
用户想知道「这周有什么」,只能一个个帖子翻;想知道「别忘了报名」,只能靠记忆。社区日历把这三处汇成一张日历,并给每个人一条 ICS 订阅地址:填进手机自带日历之后,站里的日程会自动同步到手机上,不用回站、不用收邮件、也不怕漏。
二、三项核心能力
- 一键订阅(核心卖点):每个人都能生成自己的订阅链接(webcal:// 一键订阅 iOS/macOS;http:// 给 Android 与 Outlook),页面配一张扫码即订的二维码,以及 iPhone / Android / Google 日历 / Outlook 四套「照着点就行」的图文步骤;订阅页还会显示「最近一次被手机拉取的时间」,用来确认到底订上没有。链接可随时重置(旧链接立即失效)或撤销。
- 跨插件聚合:直接读「活动报名」的活动与场次、读「问卷与投票」的截止时间,与自定义日程合并成同一张日历;对方插件没装 / 被停用时自动降级(连一条查询都不发)。来源开关在后台,可按需关掉。
- 三视图 + 极简操作:列表(今天 / 本周 / 本月 / 未来 90 天四个快捷筛选)、月历(一屏看全一个月)、周历(7 列一天);月历格子右上角的「+」直接在那天新建日程(日期已预填);列表每条日程「下载 .ics」双击就能进手机日历。
三、其它细节
- 日程支持分类、地点、全天、公开/私密(私密日程不进公开订阅源)、Markdown 说明。
- 提前 N 分钟站内提醒:发起人收到,订阅了对应来源的用户也收到;计划任务用 CAS 抢占,重复跑不会重复发。
- ICS 输出按 RFC 5545:先转义反斜杠/逗号/分号、最后处理换行,75 字节折行按 UTF-8 字符边界切;全天日程用
VALUE=DATE且 DTEND 为次日;带REFRESH-INTERVAL让手机按小时自动更新。 - 查询预算:列表/月历/周历各 6 条,详情 3 条,订阅页 3 条,新建页 2 条;条目数翻 10 倍查询数不变。
四、兼容与升级
- 纯插件,不改核心;只新增自己的两张表;卸载会一并清理。
- 需要 PHP 8.1+,与站点一致(SQLite / MySQL / PostgreSQL 三驱动通用)。
插件质量报告
插件:
community_calendarHook 功能范围 循环 使用频率 文件读写 修改系统表 读写系统表 读写自己的表 sidebar.feature_links 首页/侧栏快捷功能 否 5 - - - - sidebar.stack 全站侧栏组件栈 回调内 5 - - - 写 plugin_community_calendar_entries, plugin_community_calendar_exceptions, plugin_community_calendar_subs, plugin_community_calendar_joins 修正❗️ top.bar.actions 顶部栏插件入口,同一请求只执行一次(展示位置:版块导航与搜索框前后) 否 5 - - - - user.menu_links 个人卡片/移动端我的菜单(展示位置:个人卡片) 否 4 - - - - diy_layout.components 非系统hook 否 1 - - - 读 plugin_community_calendar_entries 颜色说明:黄色表示有数据读写,红色表示 Hook 在系统循环中执行,浅蓝色表示有文件写入。“列表跳过”表示回调在列表路径会提前返回,“回调内”表示操作位于回调自身或其帮助函数的循环体内。“文件批处理”表示逐个处理用户一次提交的文件,属于必要操作,不标记修正。只有操作实际处于需要优化的循环路径时,才会标记“修正❗️”。
数据字典
plugin_community_calendar_entries字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_community_calendar_subs字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_community_calendar_joins字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 plugin_community_calendar_exceptions字段 类型 可空 默认值 约束 - 动态定义 - - 无法静态解析 沙发,怎么要授权码
版本 2.0.0 更新:
2.0.0 大版本更新:隐私口径统一 + 我要参加 + 重复日程 + 侧栏今日卡片
本次是「先修 BUG,再加功能」:修掉 4 处真实缺陷(其中第一处是隐私泄露),再补 3 项能力。
一、修掉的 4 处缺陷(都可复现、都留了回归断言)
- 私密日程根本「不私密」
现象:勾了「仅自己可见」的日程,别人与游客在列表 / 周历里照样看得到,还会被拉进公开订阅源;给订阅者发的提醒里也会带出私密日程的标题——而详情页与单条 .ics 却是挡住的,同一份数据两套口径。
根因:私密只在详情页与单条导出里判定,列表 / 月历 / 周历 / 订阅源 / 提醒全部直接从取数入口拿数据,那条 SQL 里没有任何可见性条件。
修法:在唯一的取数入口上统一口径——非本人(且非管理团队)永远只能取到公开日程;提醒循环里私密日程只提醒发起人。- 编辑日程会静默重置字段
现象:只想改标题的一次保存,会把私密日程改成公开(下一秒就泄露给所有人)、把全天日程改成普通、把提前提醒关掉、把结束时间清空;非法分类还会被静默替换成第一个分类。
修法:改为「POST 里没有的键一律沿用库里的值」(与站内其它插件同口径);非法分类 / 非法重复规则明确拒绝保存并提示;只提交新开始时间时按同样的位移搬运旧结束时间(改期保持时长),不再因为旧结束时间还在过去而报「结束早于开始」。- ICS 的 DTSTAMP 每次拉取都变
现象:同一场没改过的活动,手机日历每 5 分钟拉一次就换一个「创建时间」,部分客户端会反复刷新;且缺 LAST-MODIFIED。
修法:DTSTAMP / LAST-MODIFIED 改用条目自己的更新时间(外部来源用各自表的 updated_at);重复日程每期用独立 UID。- 详情页成了孤岛
现象:列表 / 月历 / 周历里点日程标题跳到的是「列表页自己」(只是高亮),永远走不到详情页——「我要参加」、参加名单、完整说明没人发现得了。
修法:条目标题统一指向详情页;列表页的 ?id= 深链高亮保留给从别处跳回的场景。二、三项新能力
- 一键「我要参加」:参加 / 取消一键完成(天然幂等),列表上直接显示「N 人参加」,详情页给发起人与管理团队看参加名单;新增「我参加的」快捷筛选;订阅源支持「只订我标记了参加的日程」。
- 重复日程:每天 / 每周 / 每两周 / 每月 + 「重复到哪天」;只存一条规则,展示与订阅源里自动展开(单次最多 26 期、不超出查询窗口);每月重复按自然月走(1-31 → 2-28/29)。
- 侧栏「今日日程」卡片:全站侧栏一眼看到今天 / 明天的日程,带「打开日历 / 订阅 / + 新建」入口;站点确实没有未来日程时零查询短路。
三、验证
- 专项套件扩到 170 项断言 / 0 失败(新增 48 项:2.0.0 结构、隐私回归、字段沿用回归、参加与重复、侧栏卡片、订阅过滤、ICS 稳定性)。
- 六道静态门禁全绿;计划任务仍为 2 个;截图增至 7 张(新增详情页参加 / 侧栏卡片 / 重复日程列表)。
- 查询预算:列表 / 月历 / 周历各 6 条,条目翻 10 倍不变。
版本 2.0.0 更新:
2.0.0 大版本更新:隐私口径统一 + 我要参加 + 重复日程 + 侧栏今日卡片
本次是「先修 BUG,再加功能」:修掉 4 处真实缺陷(其中第一处是隐私泄露),再补 3 项能力。
一、修掉的 4 处缺陷(都可复现、都留了回归断言)
- 私密日程根本「不私密」
现象:勾了「仅自己可见」的日程,别人与游客在列表 / 周历里照样看得到,还会被拉进公开订阅源;给订阅者发的提醒里也会带出私密日程的标题——而详情页与单条 .ics 却是挡住的,同一份数据两套口径。
根因:私密只在详情页与单条导出里判定,列表 / 月历 / 周历 / 订阅源 / 提醒全部直接从取数入口拿数据,那条 SQL 里没有任何可见性条件。
修法:在唯一的取数入口上统一口径——非本人(且非管理团队)永远只能取到公开日程;提醒循环里私密日程只提醒发起人。- 编辑日程会静默重置字段
现象:只想改标题的一次保存,会把私密日程改成公开(下一秒就泄露给所有人)、把全天日程改成普通、把提前提醒关掉、把结束时间清空;非法分类还会被静默替换成第一个分类。
修法:改为「POST 里没有的键一律沿用库里的值」(与站内其它插件同口径);非法分类 / 非法重复规则明确拒绝保存并提示;只提交新开始时间时按同样的位移搬运旧结束时间(改期保持时长),不再因为旧结束时间还在过去而报「结束早于开始」。- ICS 的 DTSTAMP 每次拉取都变
现象:同一场没改过的活动,手机日历每 5 分钟拉一次就换一个「创建时间」,部分客户端会反复刷新;且缺 LAST-MODIFIED。
修法:DTSTAMP / LAST-MODIFIED 改用条目自己的更新时间(外部来源用各自表的 updated_at);重复日程每期用独立 UID。- 详情页成了孤岛
现象:列表 / 月历 / 周历里点日程标题跳到的是「列表页自己」(只是高亮),永远走不到详情页——「我要参加」、参加名单、完整说明没人发现得了。
修法:条目标题统一指向详情页;列表页的 ?id= 深链高亮保留给从别处跳回的场景。二、三项新能力
- 一键「我要参加」:参加 / 取消一键完成(天然幂等),列表上直接显示「N 人参加」,详情页给发起人与管理团队看参加名单;新增「我参加的」快捷筛选;订阅源支持「只订我标记了参加的日程」。
- 重复日程:每天 / 每周 / 每两周 / 每月 + 「重复到哪天」;只存一条规则,展示与订阅源里自动展开(单次最多 26 期、不超出查询窗口);每月重复按自然月走(1-31 → 2-28/29)。
- 侧栏「今日日程」卡片:全站侧栏一眼看到今天 / 明天的日程,带「打开日历 / 订阅 / + 新建」入口;站点确实没有未来日程时零查询短路。
三、验证
- 专项套件扩到 170 项断言 / 0 失败(新增 48 项:2.0.0 结构、隐私回归、字段沿用回归、参加与重复、侧栏卡片、订阅过滤、ICS 稳定性)。
- 六道静态门禁全绿;计划任务仍为 2 个;截图增至 7 张(新增详情页参加 / 侧栏卡片 / 重复日程列表)。
- 查询预算:列表 / 月历 / 周历各 6 条,条目翻 10 倍不变。
版本 3.0.0 更新:
【3.0.0】先修五处真缺陷(每处都在实验室做了双版本实拍取证:2.0.0 跑探针 → 装 3.0.0 复跑),再补四项能力。一、修掉的缺陷(现象 → 根因 → 修法)
- 过期清理把还在重复的日程整条删掉(数据丢失):一条「每天 09:00、一直重复」的例会(开始时间在 400 天前)跑一轮清理任务后,规则与未来全部实例一起消失。根因:清理语句只看这一行的开始/结束时间,不看它是不是还在重复。修法:只清理「重复已结束」的行,一直重复的永不清理。实测条目还在 0 → 1。
- 「今日日程」永久消失(缓存当当前状态):站点标记一旦落 0 就再也不纠正;而落 0 的判定又只看 start_at,开始于 10 天前的每天例会被算成「全站没有未来日程」。修法:存在性判定认重复日程的后续实例,标记为 0 时做一次存在性自愈并写回,结构升级时按新口径重算一次。实测卡片出现 0 → 1、标记 0 → 1。
- 长期重复的日程在窗口里一期都不产出:一条「每天」的日程只要开始时间在 26 天前、一条「每周」的只要在半年前,月历/周历/订阅源里就再也看不到它(用户看到的是「这条日程不见了」)。根因两处叠加:区间谓词把「父条目整条都在窗口之前」的重复日程整行滤掉;26 期上限又被窗口之外的历史期次吃光。修法:区间谓词补一条「规则还没结束」的分支,展开时先跳到窗口起点附近(只推进游标、不产出、不计入上限)。实测今天视图出现 0 → 1。
- 每一次视图渲染都白付一条同款区间查询(取数函数被调了两遍:一次算参加人数、一次遍历)。修法:一次取回两处复用。实测同状态下周历 SQL_COUNT 9 → 8(月/列表页与 3.0.0 新增的例外日查询相抵,净持平)。
- 提醒计划任务里每条日程多查一次分类(一轮 N 条就是 N 条多余查询)。修法:把分类并进候选查询、删掉循环内的点查。实测源码指纹 1 → 0 处;受控一轮(6 条待提醒、其余任务全部推后)核心 SQL 条数 35 → 32。
二、四项新能力
· 重复日程的例外日:调休、临时停一次、某一期改时间都不用改规则本身——新表存「某一期取消/改期」,展示、订阅源、侧栏卡片统一按例外日展开(挪出窗口自然不产出、挪进窗口也能出现);详情页给出接下来几期与三个操作(取消这一期 / 改期 / 恢复),期次合法性按请求时刻前后各两天展开校验,伪造时间戳进不了例外表。
· 共同空闲时段推荐(?a=calendar_find):填参与人、天数、时长,按工作时段×30 分钟步长算每个候选起点的到齐人数,给出推荐时段与「就用这个时间」。忙碌区间只查一条、算法全在内存,查询数与参与人数/天数/条目数无关;隐私口径写在页面上——只看别人公开的日程,私密日程对你看不见也不参与计算。
· 订阅增量同步与失效自愈:订阅源按内容派生修订号并输出 ETag / X-Calendar-Revision,手机带 If-None-Match 且内容没变时直接回 304;订阅页给出健康状态(正常 / 等待首次拉取 / 疑似失效),自愈两条路——重置链接时保留旧令牌、可一键「恢复旧链接」(手机不用重新配置),以及计划任务给长期没被拉取的订阅人发一条系统通知(批量写、一个窗口只发一次)。
· 与「活动报名」的双向联动:自建日程可关联一个活动(编辑页下拉只列未来活动,不存在的 id 落 0 不留悬空引用);详情页只读显示活动时间/地点/报名人数/我是否已报名。「我参加的」与「只订我参加的」订阅过滤同时认三种口径:日历里点的「我要参加」、关联到我已报名活动的自建日程、活动侧我真正报过名的活动(含场次)——活动页报个名,日历这边立刻看到。三、兼容与升级
结构标记 3:幂等补 1 张表 + 3 列 + 2 个索引,并按新口径重算站点标记;原有 2 张表、三视图查询预算、ICS 合规口径、订阅令牌语义一律不变;新增路由 1 个,计划任务仍是 2 个。后台配置写入器改为「以当前配置为基线」逐键覆盖(plugin_save_config 对 config_json 是整体覆盖,漏键=删键)。订阅失效自检也改成批量写,页面路径与 cron 路径的循环内查库审计都干净。验收:tools/lab_verify_calendar.sh 246 项断言 / 0 失败;六道静态门禁全绿;缺陷探针在 2.0.0 与 3.0.0 上各实拍一遍。
版本 3.0.1 更新:
3.0.1 修复:后台设置保存失败时看不到真正的错误原因- 校验失败时直接 fall through 成整页 HTML,核心前端却按 JSON 解析 → 只弹「操作失败」,真正的提示被吞掉。
- 修法:AJAX 请求回
{"ok":0,"message":…}信封;无 JS 浏览器仍是原来的整页渲染(字节不变)。
版本 3.0.1 更新:
3.0.1 修复:后台设置保存失败时看不到真正的错误原因- 校验失败时直接 fall through 成整页 HTML,核心前端却按 JSON 解析 → 只弹「操作失败」,真正的提示被吞掉。
- 修法:AJAX 请求回
{"ok":0,"message":…}信封;无 JS 浏览器仍是原来的整页渲染(字节不变)。
版本 3.0.2 更新:
补丁版本:给「页面组件 DIY」加一个原生组件(未装 layout_diy 时行为逐字节不变)
- 新增只读组件注册(私有 Hook
diy_layout.components):具名渲染回调 + 声明查询成本 + 无数据时零查询降级(靠站点标记短路)+ 拿不到数据返回空串由宿主降级成占位(不抛异常,单块坏天气不会带走整页)。 - 查询形状与行数无关:列表/映射/计数各一条批查,条数参数只改
LIMIT的绑定值。 - 组件自带参数白名单(条数 / 显示哪些字段),后台可视化编辑、越界值拒绝保存。
- 未装「页面组件 DIY」时这个 Hook 无人调用:本插件的行为、查询数、页面字节与上一版逐字节一致(用「旧版 vs 新版、两边都停用 layout_diy」的 A/B 比对守着,含查询数)。
- 新增只读组件注册(私有 Hook
版本 3.0.3 更新:
3.0.3:给「页面组件 DIY」的原生组件补上站点标记自愈路径
修的是一处真缺陷(本仓库「七类缺陷模式」里的「缓存当当前状态」):
「今日日程」组件(
community_calendar:today)此前直接读站点标记plugin_community_calendar_has_upcoming,
而同名的侧栏卡片走的是带自愈的community_calendar_has_upcoming()(3.0.0 给它补过自愈路径)。
于是标记被清成 0、或日程是从别处导入的而标记还是 0 时,库里明明有日程、这一块却永远显示空。- 组件改用
community_calendar_has_upcoming():标记为'1'时零查询;否则一条存在性自愈查询,
确有未来日程就把标记补回
'1'。- 声明成本 2 → 3(= 1 条自愈 + 2 条渲染的最大值)。
其余行为不变:页面白名单(首页 + 日历相关页)照旧、只读、拿不到数据返回空串。
- 组件改用
版本 4.0.0 更新:
4.0.0 大更新:导入 · 逐期提醒 · 冲突检查 · 例外日零查询【新能力】
- iCalendar 导入:把手机 / Google / Outlook 导出的 .ics 用记事本打开、整段粘进来,就变成站内日程。UID 幂等(同一份文件导入两次只会「跳过重复」,不会变成两份);全天(VALUE=DATE)、UTC(…Z)、浮动本地时间、折行与 \n \, \; \\ 转义全部认;RRULE 识别为每天 / 每周 / 每两周 / 每月,识别不了的(COUNT、多 BYDAY)按首期导入并如实报数;导入的日程可编辑、可下载 .ics、也会进你自己的订阅源。
- 重复日程逐期提醒:给「每周例会」设一次提醒,之后每一次开会都会各收到一条(被取消的那一期不提醒、改期的那一期按新时间提醒);幂等守卫从「父条目」升到「期次」,两轮计划任务重叠不会重复发。
- 时间冲突检查:详情页多一块「时间冲突:与 N 条其它日程重叠」;新增总览页把未来 N 天里「同一时段两件事」的日子两两列出(天数后台可调)。
- 例外日随行缓存:首页 / 列表 / 月历 / 周历 / 订阅源 / 空闲推荐 / 详情页都不再为「例外日」多发一条批查;首页本插件贡献从 2 条语句降到 1 条,且与内容无关(有没有重复日程都一样)。
【修复】
F1 删除重复日程后例外日永久残留(cleanup_days=0 时永不回收)→ 删除时一并清掉。
F2 「找共同空闲时间」把别人的私密日程算进你的计算(会泄露「某人某天忙不忙」)→ 只让请求者自己的私密日程参与。
F3 重复日程只有第一期会提醒;起点在过去的系列一条都不提醒 → 改为逐期提醒。
F4 首页每次浏览多付一条「内容相关」的例外日批查 → 随行缓存,查询数变成确定值。
F5 ICS 里的 PRODID 版本号停在 3.0.0(manifest 已是 3.0.3)→ 同轮抬正并加「版本原子性」断言。
F6 卸载不清理站点标记(重装时该跑的结构升级被跳过)→ 卸载一并清掉。
F7 重复日程「下载 .ics」只导出第一期(订阅源里却是全的)→ 按订阅源口径导出全部期次;非重复日程 UID 保持不变。
F8 改过时间之后,之前「取消的那一期」会复活 → 例外日键随改期平移;重复方式换了语义时整条作废并明确提示。
F9 期次时间戳带秒时(导入进来的日程)「取消这一期 / 恢复这一期」永久失效 → 按分钟解析到真正的那一期。【兼容】
结构标记升到 4(幂等补 3 列 + 1 枚索引 + 一次性搬例外日缓存,后台可续跑);表数量与计划任务数量不变;三个核心页面(列表 / 首页 / 月历)的渲染与上一版逐字节一致,只允许查询数下降。版本 4.1.0 更新:
4.1.0:导入升级与并发加固(幂等 · UID 变更 · TZID · 计划任务并发 · 缓存自愈)【新能力 / 升级】
- 导入可选「跳过」或「用文件内容原位更新」:源日历改过标题或时间时,原位更新那一条(分类 / 公开私密 / 提醒设置保留),不会多出一份;时间变了才会重新计时提醒。
- UID 变更兜底:源日历重新生成 UID(换客户端、重建日历很常见)时,靠「标题 + 开始 + 结束」指纹认出是同一件事,不产生第二份;只认导入行,绝不去认领你自己手建的日程 —— 结果面板会如实报出「其中 N 条是 UID 变了但内容一致」。
- 时区(TZID)事件按站点时区正确落库:
DTSTART;TZID=America/New_York:20270115T090000会显示成站点时区的对应时刻(实测 22:00),另支持 VALUE=DATE 全天、…ZUTC 与浮动时间;认不出的时区退化为浮动时间,不会因为一条脏数据让整份文件导不进来。 - 例外日缓存的写路径改成乐观 CAS,后台新增「重建例外日缓存」=校验并修复(逐条与例外表比对,只写回不一致的那些,可重复点)——缓存被写坏 / 写旧时一键修回,杜绝「取消过的期次又回来」。
- 逐期提醒的抢占条件升级为「期次 + 例外日缓存」双令牌:多个计划任务进程同时跑,同一期只会发出一条通知;「取消这一期」正好插在读与写之间时,当轮不发提醒。
【修复】
A. 多行批量更新错位(4.1.0 引入、本轮套件抓到):绑定参数按「行」交错导致两行以上整体错位(第二行的标题被写进第一行的结束时间)→ 改成按 SQL 文本顺序(外层列、内层行),并加两条 typeof 哨兵断言。
B. 冲突总览被外部来源挤满:活动与场次之间天然互相重叠,能把 60 组配额吃光,用户自己的撞车日程一条都列不出来(实测出现 0 次)→ 现在自己的日程优先列出,被截断时明确提示「已列出前 60 处」。
C. 静态审计 E6 待确认项清零:三处解析函数改成接收array $post(不再直接读$_POST),plugin_rules_audit 结论为「无硬规则违规(0 条待人工确认)」。【预算复核】
首页本插件贡献仍为恒定 1 条(3.0.3:有重复日程 2 / 无 1,内容相关;4.0.0 与 4.1.0:两种情形都是 1),逐条 SQL 里不出现例外日批查;整页 9 条(停用 8 条)。【兼容】
结构不变(仍是 3 张表 / 2 个计划任务 / 结构标记 4),无需迁移;三个核心页面(列表 / 首页 / 月历)的渲染与 4.0.0 逐字节一致。版本 4.2.0 更新:
4.2.0 · 安全与导入幂等- 修复(反射型 XSS):「找人找时间」页把
?users=参数未转义回填进输入框(同页其它回显都做了转义)⇒ 可构造链接让已登录用户在站内执行任意脚本。已转义。 - 修复:无 UID 的日历事件重复导入不幂等(会重复建条目);重复日程的 UID 用「产出序号」派生,取消其中一期会让后续期次的 UID 全部前移,导致客户端日历错位/重复。
- 修复(覆盖升级):补上运行期结构自愈(老库覆盖文件后缺列会让提醒任务每轮抛错)。
升级:覆盖文件即自愈。
- 修复(反射型 XSS):「找人找时间」页把
版本 4.3.0 更新:
4.3.0 · 覆盖文件升级自愈- 修复(升级路径):日程事件表与「参与/例外」两张关联表是后续版本新增的,而建表只发生在
install里。核心覆盖文件不重跑 install ⇒ 老站永远没有这两张表,日程页一读即抛异常并被自动停用。现在运行期入口按三道门(内存副本 → 设置标记 → 才 DDL)幂等补齐,CREATE TABLE IF NOT EXISTS与「列不存在才 ALTER」并用。 - 口径:稳态零查询;不挂读钩子,页面预算不变;重复日程、例外与提醒的既有行为逐字节不变。
- 修复(升级路径):日程事件表与「参与/例外」两张关联表是后续版本新增的,而建表只发生在