Erreur TypeError lors de la soumission d'un drapeau avec du contenu personnalisé (drapeaux require_message)

Description du bogue

Lorsqu’un utilisateur sélectionne un type de signalement nécessitant un message personnalisé (par exemple notify_moderators, notify_user, ou tout signalement personnalisé créé par un administrateur avec l’option « Message requis » activée), remplit le message et soumet le formulaire, le navigateur lève une erreur TypeError non capturée et le signalement n’est jamais envoyé.

Étapes pour reproduire le problème

  1. Accédez à n’importe quel sujet de discussion.
  2. Cliquez sur le bouton de signalement pour ouvrir la fenêtre modale.
  3. Sélectionnez un type de signalement nécessitant un message (par exemple « Autre chose » / notify_moderators, ou tout signalement personnalisé créé dans Administration → Signalements avec l’option « Message requis » activée).
  4. Saisissez un message dans la zone de texte (assez long pour respecter la longueur minimale).
  5. Cliquez sur le bouton de soumission.

Comportement attendu

Le signalement est soumis avec succès.

Comportement réel

La fenêtre modale de signalement se ferme, mais le signalement n’est pas soumis. La console du navigateur affiche :

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)
    ...

Analyse de la cause racine

Le plantage provient de Flag#create (flag.js:24) :

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

La méthode postActionFor dans PostFlag recherche le signalement sélectionné par son id numérique dans actions_summary :

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

Le sérialiseur backend PostSerializer n’inclut une entrée de signalement dans actions_summary que si au moins l’un des champs can_act, count ou acted est vrai :

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

Pour les signalements avec require_message: true (qui sont tous de type notify_type: true), la méthode post_can_act? définit can_act: false lorsque already_did_flagging est vrai — c’est-à-dire lorsque l’utilisateur a déjà soumis un signalement de type notification sur ce message. Dans ce cas, l’entrée est absente de actions_summary, postActionFor retourne undefined, et le plantage se produit.

Pendant ce temps, Post#flagsAvailable (qui contrôle ce que l’utilisateur peut sélectionner dans l’interface) utilise la carte actionByName construite au chargement de la page, de sorte que le signalement peut toujours apparaître comme sélectionnable même si actions_summary ne le contient plus.

Bogue secondaire

Il existe également une comparaison incorrecte dans flag.gjs :

const NOTIFY_MODERATORS_KEY = "notify_moderators"; // chaîne de caractères

get notifyModeratorsFlag() {
  return this.flagsAvailable.find((f) => f.id === NOTIFY_MODERATORS_KEY);
  //                                      ^ nombre   ^ chaîne — toujours faux
}

f.id est un nombre, mais NOTIFY_MODERATORS_KEY est une chaîne, donc === retourne toujours false et notifyModeratorsFlag est toujours undefined. Cela rompt la logique du bouton « Signaler pour examen » dans flagForReview().

Solution proposée

PostFlag#postActionFor devrait utiliser la carte actionByName (indexée par name_key) au lieu de rechercher dans actions_summary par id, conformément au fonctionnement déjà en place dans TopicFlag#postActionFor :

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

Et notifyModeratorsFlag devrait comparer par name_key au lieu de id :

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

Version de Discourse

2026.5.0-latest

Reproductible sur

  • La dernière branche main
1 « J'aime »