提交自定义内容标志(require_message 标志)时出现 TypeError

Bug 描述

当用户选择需要自定义消息的旗帜类型(例如 notify_moderatorsnotify_user,或任何在后台启用“需要消息”选项的管理员自定义旗帜),填写消息并提交时,浏览器会抛出未捕获的 TypeError,且旗帜从未被提交。

复现步骤

  1. 访问任意主题帖子
  2. 点击旗帜按钮以打开旗帜弹窗
  3. 选择一个需要消息的旗帜类型(例如“其他事项”/ notify_moderators,或在 管理 → 旗帜 中创建的任何启用“需要消息”的自定义旗帜)
  4. 在文本区域中输入消息(长度需满足最低要求)
  5. 点击提交按钮

预期行为

旗帜成功提交。

实际行为

旗帜弹窗关闭,但旗帜未被提交。浏览器控制台显示:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'act')
    at n.create (flag.js:24:8)
    at S.createFlag (flag.gjs:205:32)
    at S.takeAction (flag.gjs:196:12)
    at m.perform (reviewable-bundled-action.gjs:47:17)
    at ej._boundaryActionHandler (select-kit.js:685:34)
    ...

根本原因分析

崩溃起源于 Flag#createflag.js:24):

create(flagModal, opts) {
  const postAction = this.postActionFor(flagModal); // 返回 undefined
  // ...
  postAction.act(...) // ← TypeError: Cannot read properties of undefined
}

PostFlag 中的 postActionFor 方法在 actions_summary 中通过数字 id 查找所选旗帜:

// post-flag.js
postActionFor(flagModal) {
  return flagModal.args.model.flagModel.actions_summary.find(
    (item) => item.id === flagModal.selected.id
  );
}

后端 PostSerializer 仅在 can_actcountacted 中至少有一个为真值时,才会在 actions_summary 中包含旗帜条目:

result << summary if summary[:can_act] || summary[:count] || summary[:acted]

对于 require_message: true 的旗帜(均为 notify_type: true),当 already_did_flagging 为真(即用户之前已在该帖子提交过任何通知类旗帜)时,post_can_act? 会将 can_act 设置为 false。此时该条目不会出现在 actions_summary 中,导致 postActionFor 返回 undefined,进而引发崩溃。

与此同时,控制用户在 UI 中可选择内容的 Post#flagsAvailable 使用的是页面加载时构建的 actionByName 映射,因此即使 actions_summary 中不再包含该条目,旗帜仍可能显示为可选。

次要 Bug

flag.gjs 中还存在一个错误的比较:

const NOTIFY_MODERATORS_KEY = "notify_moderators"; // 字符串

get notifyModeratorsFlag() {
  return this.flagsAvailable.find((f) => f.id === NOTIFY_MODERATORS_KEY);
  //                                      ^ 数字   ^ 字符串 — 始终为 false
}

f.id 是数字,而 NOTIFY_MODERATORS_KEY 是字符串,因此 === 始终返回 false,导致 notifyModeratorsFlag 始终为 undefined。这破坏了 flagForReview() 中“标记以供审查”按钮的逻辑。

建议修复方案

PostFlag#postActionFor 应使用 actionByName 映射(以 name_key 为键),而不是通过 id 搜索 actions_summary,这与 TopicFlag#postActionFor 的现有实现方式保持一致:

// post-flag.js
postActionFor(flagModal) {
  return flagModal.args.model.flagModel.actionByName[
    flagModal.selected.name_key
  ];
}

同时,notifyModeratorsFlag 应改为通过 name_key 进行比较:

get notifyModeratorsFlag() {
  return this.flagsAvailable.find((f) => f.name_key === NOTIFY_MODERATORS_KEY);
}

Discourse 版本

2026.5.0-latest

可复现于

  • 最新的 main 分支
1 个赞