日程表单的日历本下拉只列可写 custom 本(#4)
用户原话(2026-10-26):「网页端,添加日程,如果日历本是只读的,例如别人
共享给我只读,或者系统自带日历、观影、纪念日等,就不要出现在日历本选框中,
只出现那些可写的。」
三条判据缺一不可(只做一条都修不好):
① canWrite() —— 挡「别人共享给我且只读」。
实测:只有这一类能被 canWrite 拦住。
② !isReadonlyKind() —— 挡系统只读本(农历/节假日/传统节日/二十四节气)。
它们的 owner_id 就是我 ⇒ canWrite() 恒真,光靠 ① 一本也拦不住。
③ kind === 'custom' —— 挡观影/旅行的派生本。
这两本**确实可写**(往里加一条 = 建一条观影/旅程记录),
但不是"放日程的地方",放在新建日程下拉里只会让人建出别的东西。
- app/Calendar.php:新增 writableForEvent() / filterWritableForEvent(),
作为**唯一口径**(日历页、首页卡片、API 都走它),
避免 v82 那种"两个入口的下拉内容可以不一样"的老毛病。
判据用 READONLY_KINDS 常量而不是 kind 白名单:新增系统本只要登记就自动生效。
- templates/calendar/index.php、index.php:改用上面的口径。
- templates/calendar/_add_modal.php:清掉恒假的 disabled 与「(自动生成)」
死代码(前端已过滤,留着只会误导);保留 data-kind 给探针当锚点;
按钮色点与文案改成**服务端就带出来**(无 JS 时也看得见颜色和本名)。
验证:
- 浏览器探针 28/0:含自建夹具(只读共享 / 可写共享 / 自己的)三类,
与 PHP 侧独立重算的期望集逐项比对,真保存一次,
以及**绕过前端直接 POST** —— 只读共享回「这本日历你没有写权限。」且库无新增;
观影/旅行回 ok:true 且真的落 events 表(不进 calendar_events)。
- 反向验证 6/6 全抓住:退回 canWrite / 只看 kind / 不管 custom / 全滤空 /
只读共享漏进下拉 / 拆掉 event_add 的 canWrite 闸门。
最后一条注入时**只有 ⑪ 变红**,证明它测的是服务端拦截而非前端下拉。
- 全量流水线(--all)退出码 0,已部署并确认线上 4 个文件 php -l 全通过。
工单:#4
This commit is contained in:
@@ -976,12 +976,13 @@ switch ($r) {
|
||||
不补的话首页这张弹框的默认本可能落到**观影本**上,
|
||||
用户直接点"加上"就建出一条观影记录(v24 第 21 批真事故)。 */
|
||||
Calendar::ensurePersonalBook($familyId, $me);
|
||||
$agCals = Calendar::mine($familyId, $me);
|
||||
/* 🔴 v95(用户 2026-10-26):口径统一走 `writableForEvent()`。
|
||||
⚠️ 原来这里是首页自己写的 `array_filter(canWrite)`,与日程页
|
||||
"看起来一样、实则不是同一份代码" —— 改一处忘另一处就漂移。
|
||||
canWrite 拦不住观影/旅行派生本,也拦不住系统只读本
|
||||
(它们的 perm 是 owner),详见 `Calendar::writableForEvent()`。 */
|
||||
$homeAddModals['agenda'] = [
|
||||
'writable' => array_values(array_filter(
|
||||
$agCals,
|
||||
static fn($c) => Calendar::canWrite($c, $me)
|
||||
)),
|
||||
'writable' => Calendar::writableForEvent($familyId, $me),
|
||||
];
|
||||
} elseif ($hcKey === 'contacts' && !isset($homeAddModals['contacts'])) {
|
||||
/* 🔴 v82(用户 2026-10-23):「名册,也没有新增成员这个按钮」——
|
||||
|
||||
Reference in New Issue
Block a user