Ошибка TypeError при отправке флага с пользовательским содержимым (флаги require_message)

Описание ошибки

Когда пользователь выбирает тип флага, требующий пользовательского сообщения (например, notify_moderators, notify_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#create (flag.js:24):

create(flagModal, opts) {
  const postAction = this.postActionFor(flagModal); // возвращает undefined
  // ...
  postAction.act(...) // ← TypeError: Cannot read properties of undefined
}

Метод postActionFor в PostFlag ищет выбранный флаг по числовому id внутри actions_summary:

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

Бэкенд-сериализатор PostSerializer включает запись флага в actions_summary только если хотя бы одно из значений can_act, count или acted истинно:

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

Для флагов с require_message: true (которые все имеют notify_type: true) метод post_can_act? устанавливает can_act: false, когда already_did_flagging истинно — то есть, если пользователь ранее отправлял любой флаг типа уведомлений на этот пост. В таком случае запись отсутствует в actions_summary, postActionFor возвращает undefined, и возникает сбой.

Тем временем Post#flagsAvailable (который определяет, какие флаги доступны для выбора в интерфейсе) использует карту actionByName, построенную при загрузке страницы, поэтому флаг может по-прежнему отображаться как доступный для выбора, даже если он больше не присутствует в actions_summary.

Вторичная ошибка

Также в 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), а не искать в actions_summary по id, что соответствует тому, как уже работает TopicFlag#postActionFor:

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

А notifyModeratorsFlag должен сравнивать по name_key, а не по id:

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

Версия Discourse

2026.5.0-latest

Воспроизводится на

  • Последняя ветка main
1 лайк