Deux PR ouvertes introduisent des approches différentes pour l’édition des attributs BBCode dans le contenu brut :
-
#43187 — Ajout du mode de sujet successeur récurrent pour les Événements introduit
BbcodeRewriter, qui identifie un Événement rendu et réécrit en toute sécurité la balise d’ouverture BBCode correspondante lors du roulement automatique. -
#42932 — Ajout des actions Événement aux workflows introduit
PrettyText.update_bbcode_attributes, qui localise un bloc BBCode analysé et met à jour ses attributs lorsqu’un workflow ouvre ou ferme un Événement.
Lors de la revue de #43187, Martin a suggéré d’extraire la fonctionnalité générique de manipulation BBCode du service spécifique aux Événements.
J’ai initialement inclus certaines fonctionnalités basées sur des jetons (tokens) issues de l’autre PR, mais cela a introduit deux mécanismes de réécriture au sein de #43187. Comme l’API basée sur les jetons n’avait aucun appelant en production dans cette branche, je l’ai supprimée dans le commit 6d13371.
L’API basée sur les jetons reste utile indépendamment pour #42932.
Je me suis demandé si ces deux approches devraient éventuellement converger vers une API centrale réutilisable unique, ou si leurs exigences différentes justifient de conserver les deux.
Comparaison des implémentations actuelles
Le tableau décrit ce que le code implémente actuellement, sans suggérer qu’une des approches est intrinsèquement incorrecte.
| Capacité | BbcodeRewriter (#43187) |
API basée sur les jetons (#42932) | Signification et importance |
|---|---|---|---|
| 1. Utilise l’analyseur BBCode de Discourse | Balaye les balises d’ouverture candidates à l’aide de parseBBCodeTag, la même grammaire de balise d’ouverture utilisée par le moteur Markdown. |
Utilise DiscourseMarkdownIt.parse pour obtenir des jetons Markdown et trouver un bloc BBCode correspondant. |
Les deux approches comprennent la syntaxe BBCode, plutôt que de s’appuyer uniquement sur une expression régulière indépendante. Elles diffèrent dans la manière dont elles identifient la balise à éditer. |
| 2. Sélectionne le bloc BBCode cible | Marque temporairement les balises d’ouverture candidates, rend le post et identifie le candidat correspondant à un élément HTML sélectionné, tel que div.discourse-post-event. |
Sélectionne le premier jeton BBCode analysé dont meta.bbcode correspond au nom de balise requis. |
L’identité de la cible est importante. #43187 doit associer le BBCode brut à l’Événement effectivement rendu par Discourse. Le workflow de #42932 utilise le premier bloc d’Événement analysé correspondant. |
| 3. Gère les cibles manquantes ou ambiguës | Lève TargetNotFound, AmbiguousTarget ou UnsupportedRenderer lorsque la cible ne peut pas être identifiée de manière fiable. |
Retourne null si aucun bloc analysé correspondant n’existe ; sinon, sélectionne la première correspondance. |
Le comportement en cas d’échec diffère. #43187 refuse délibérément les éditions automatiques ambiguës. La sélection du premier bloc analysé peut être suffisante lorsque l’appelant n’attend qu’un seul bloc pertinent. |
| 4. Vérifie le mapping brut-vers-rendu | Rend le contenu marqué, vérifie le marqueur sur l’élément HTML prévu et confirme que l’insertion et la suppression du marqueur laissent le HTML rendu inchangé. | Utilise les métadonnées des jetons de l’analyseur Markdown pour localiser la balise d’ouverture sans contrôle supplémentaire d’identité du HTML rendu. | Cela protège contre l’édition de la cible rendue incorrecte. C’est particulièrement pertinent lorsqu’un tâche en arrière-plan archive automatiquement un Événement existant, mais implique un rendu supplémentaire et une complexité accrue. |
| 5. Vérifie la balise d’ouverture sélectionnée | Avant la réécriture, rebalayage du brut fourni et vérification que le candidat sélectionné correspond toujours au texte de balise d’ouverture attendu. | Localise la balise d’ouverture correspondante pendant l’appel de mise à jour, sans comparaison séparée avec une balise d’ouverture attendue. | Ceci est un contrôle de cohérence interne. Il aide à éviter de réécrire un candidat qui ne correspond plus à la sélection antérieure. Ce n’est pas, en soi, une protection contre les modifications concurrentes de la base de données. |
| 6. Gère les noms d’attributs et les valeurs existantes | Réécrit les attributs sélectionnés en utilisant les limites d’attribut de l’analyseur. Il normalise également les noms d’attributs rendus équivalents, permettant de supprimer de manière cohérente des variantes telles que Recurrence et recurrence. |
Utilise les correspondances de jetons d’attribut pour remplacer ou supprimer les noms demandés et le sérialiseur d’attributs BBCode partagé pour insérer les valeurs. | Les deux visent à préserver les attributs non liés. La normalisation des noms équivalents est particulièrement importante lors de l’archivage d’un Événement, car laisser un alias de récurrence pourrait faire rester l’Événement terminé en mode récurrent. |
| 7. Valide la balise réécrite | Analyse à nouveau la balise d’ouverture réécrite et vérifie que ses attributs correspondent aux valeurs prévues. Un aller-retour invalide lève une erreur. | Produit directement la balise d’ouverture réécrite, sans étape finale équivalente d’analyse et de comparaison. | La validation aller-retour détecte les problèmes de sérialisation, tels que des valeurs d’attributs modifiées ou des limites malformées. L’approche basée sur les jetons est plus simple mais repose sur sa logique de réécriture et ses tests plutôt que sur ce contrôle d’exécution supplémentaire. |
| 8. Préserve le contenu du post non lié | Modifie la balise d’ouverture sélectionnée tout en conservant le contenu brut environnant. Il vérifie également que les valeurs d’attributs non liés survivent à la réécriture. | Remplace la sous-chaîne de la balise d’ouverture sélectionnée tout en conservant le contenu brut environnant et les attributs non demandés pour modification. | Les deux doivent éviter les modifications non intentionnelles du post. Une édition liée à un Événement ne doit pas modifier silencieusement la discussion environnante ou les attributs BBCode non liés. |
| 9. Coût d’implémentation | Nécessite un balayage de candidats, des marqueurs temporaires, un rendu du post, une inspection HTML, une réécriture des attributs et une validation. | Analyse les jetons Markdown, sélectionne la première balise d’ouverture correspondante et la réécrit directement. | Des contrôles d’exécution plus robustes ont un coût. La question est de savoir si les mesures de sécurité supplémentaires doivent être disponibles pour tous les appelants, optionnelles, ou implémentées uniquement là où l’application les exige. |
Pourquoi les exigences diffèrent
Pour #43187, le roulement du successeur s’exécute automatiquement lorsqu’une occurrence se termine. L’Événement original est archivé comme une occurrence non récurrente, et un nouveau Sujet est créé pour la prochaine occurrence.
Si le mauvais bloc [event] est modifié, ou si les attributs réécrits ne représentent plus l’Événement prévu, l’erreur pourrait persister à travers les tentatives de roulement automatique.
Pour #42932, l’action du workflow demande explicitement qu’un Événement soit ouvert ou fermé. Son implémentation actuelle basée sur les jetons modifie le premier bloc d’Événement analysé correspondant.
L’approche basée sur les jetons n’est donc pas nécessairement inadaptée à ce cas d’utilisation. Elle fournit simplement des garanties de sélection et de validation différentes.
Les deux approches partagent déjà certaines infrastructures sous-jacentes, notamment parseBBCodeTag et serializeBBCodeAttr.
Direction possible pour une abstraction partagée
Une possibilité serait de séparer trois responsabilités :
-
Trouver une balise d’ouverture BBCode — en utilisant des jetons d’analyseur, une sélection explicite de candidats ou un mapping d’éléments rendus.
-
Réécrire ses attributs — en utilisant une implémentation partagée unique pour les limites d’attributs, la sérialisation et la préservation des valeurs non liées.
-
Valider le résultat — en appliquant optionnellement l’identité de la cible, la détection d’ambiguïté, l’équivalence du contenu rendu ou des contrôles aller-retour des attributs, selon l’appelant.
Cela permettrait aux workflows plus simples d’utiliser une sélection de jetons directe, tandis que les opérations d’édition automatique plus sensibles pourraient demander des mesures de sécurité plus robustes.
Cependant, cela pourrait aussi introduire une complexité inutile par rapport au maintien de deux API plus petites.
Question pour les mainteneurs
Sera-t-il préférable que les deux PR conservent leurs API de réécriture distinctes, étant donné leurs exigences différentes de sélection de cible ?
Sinon, un réécrivain d’attributs de bas niveau partagé avec une sélection de cible et une validation configurables serait-il une meilleure direction à long terme ?
Je suis particulièrement intéressé par le fait de savoir si les contrôles de sécurité supplémentaires actuellement implémentés par BbcodeRewriter sont considérés comme une fonctionnalité centrale généralement utile, ou s’ils devraient rester spécifiques aux cas d’utilisation nécessitant des garanties plus fortes.
Je ne propose pas qu’une des PR doive bloquer l’autre ; l’objectif est d’éviter des duplications inutiles dans l’infrastructure BBCode centrale de Discourse alors que les deux fonctionnalités évoluent.