Ce bug existe depuis longtemps. Je l’ai recherché l’année dernière, et voici les étapes pour le reproduire :
- En tant qu’administrateur, définissez
max favorite badgessur 2 et créez au moins un badge qui peut être gagné plusieurs fois. - En tant qu’utilisateur normal, définissez deux badges comme favoris, dont l’un est le badge qui peut être gagné plusieurs fois (désigné ici par Badge A).
- En tant qu’administrateur, accordez à nouveau le Badge A à l’utilisateur.
- En tant qu’utilisateur normal, actualisez la page et essayez de désépingler le Badge A. Vous rencontrerez une erreur.
La raison de l’erreur est que chaque fois qu’un badge pouvant être gagné plusieurs fois est accordé, un nouvel enregistrement user_badge est créé dans la base de données. Cependant, lorsque l’utilisateur a mis le badge en favori et le reçoit à nouveau, le nouvel enregistrement user_badge n’est pas automatiquement marqué comme is_favorite. Lorsque l’utilisateur essaie de désépingler le Badge A, le frontend envoie par défaut l’ID user_badge le plus récent. Comme cet enregistrement n’est pas marqué comme is_favorite, le backend suppose que l’utilisateur essaie de définir un nouveau badge comme favori (au lieu de le désépingler), ce qui dépasse la limite max favorite badges, entraînant ainsi l’erreur.
Le code pertinent se trouve ici :
Une solution possible consiste à modifier la ligne 131 comme suit :
if UserBadge.where(badge: user_badge.badge, user: user_badge.user).pluck(:is_favorite).any? &&
Cependant, cela ne résout pas entièrement l’incohérence des enregistrements de la base de données.
En tant que solution de contournement temporaire, un utilisateur normal peut désépingler tous les badges en exécutant le code JavaScript suivant dans la console :
const user_name = require("discourse/models/user").default.current().username;
const badges = await require("discourse/models/user-badge").default.findByUsername(user_name);
const favorites = new Map();
badges.filter((b)=>b.is_favorite).forEach((b)=>favorites.set(b.badge_id,b));
favorites.forEach((b)=>b.favorite());