`. `wp_kses()` répond à sa question ET apporte une * vraie défense en profondeur : si un rendu se met un jour à produire une balise inattendue, * elle est retirée au lieu d'être servie. * * ⛔ POURQUOI PAS `wp_kses_post()`, QUI SERAIT PLUS COURT : il ne connaît AUCUNE balise SVG. * Mesuré sur le parc .org : 419 ``, 616 ``, 135 ``, 99 ``, * 95 ``, 83 ``. Un `wp_kses_post()` effacerait toutes les jauges et tous * les graphiques — un « correctif » qui casse l'écran en satisfaisant le linter. C'est * exactement ce qu'il ne faut pas faire, et la liste ci-dessous est là pour l'éviter. * * ⚠ SI UNE BALISE MANQUE ICI, ELLE DISPARAÎT DE L'ÉCRAN SANS ERREUR. C'est le mode d'échec de * ce fichier, et il est SILENCIEUX : la page s'affiche, en moins bien. Toute balise ou tout * attribut ajouté à un rendu doit donc être ajouté ici — et la vérification se fait à l'écran, * jamais au linter. */ defined( 'ABSPATH' ) || exit; if ( ! class_exists( 'Aiforya_Schema_Kses' ) ) { class Aiforya_Schema_Kses { /** * Balises et attributs autorisés dans les rendus de l'interface AIFORYA : * le HTML de contenu que WordPress accepte déjà, plus le SVG que nos jauges dessinent. * * @return array> */ public static function ui(): array { static $liste = null; if ( null !== $liste ) { return $liste; } // Attributs communs à toutes les balises SVG que nous produisons. $svg_commun = array( 'class' => true, 'style' => true, 'id' => true, 'fill' => true, 'fill-opacity' => true, 'fill-rule' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-dasharray' => true, 'stroke-dashoffset' => true, 'stroke-opacity' => true, 'opacity' => true, 'transform' => true, // Accessibilité : `aria-hidden` est porté 406 fois dans le parc. Le placer ici // plutôt que sur la seule balise `svg` évite de le perdre s'il migre un jour sur // un `path` ou un `g` — une perte qui ne produirait aucune erreur, juste un // lecteur d'écran qui annonce une décoration. 'aria-hidden' => true, 'aria-label' => true, 'role' => true, 'focusable' => true, ); $svg = array( 'svg' => $svg_commun + array( 'xmlns' => true, // ⚠ `wp_kses` compare les noms d'attributs en minuscules, mais les deux casses // sont listées volontairement : le coût est nul, et l'oubli d'une seule // ferait disparaître le cadrage du SVG SANS message d'erreur. Le parc // emploie `viewBox` (419 fois) et `preserveAspectRatio` (3 fois). 'viewbox' => true, 'viewBox' => true, 'width' => true, 'height' => true, 'preserveaspectratio' => true, 'preserveAspectRatio' => true, ), 'g' => $svg_commun, 'defs' => $svg_commun, 'title' => array( 'id' => true ), 'desc' => array( 'id' => true ), 'path' => $svg_commun + array( 'd' => true ), 'circle' => $svg_commun + array( 'cx' => true, 'cy' => true, 'r' => true ), 'ellipse' => $svg_commun + array( 'cx' => true, 'cy' => true, 'rx' => true, 'ry' => true ), 'rect' => $svg_commun + array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'rx' => true, 'ry' => true ), 'line' => $svg_commun + array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true ), 'polyline' => $svg_commun + array( 'points' => true ), 'polygon' => $svg_commun + array( 'points' => true ), 'text' => $svg_commun + array( 'x' => true, 'y' => true, 'dx' => true, 'dy' => true, 'text-anchor' => true, 'dominant-baseline' => true, 'font-size' => true, 'font-family' => true, 'font-weight' => true, 'letter-spacing' => true, ), 'tspan' => $svg_commun + array( 'x' => true, 'y' => true, 'dx' => true, 'dy' => true, 'font-size' => true, 'font-weight' => true ), 'lineargradient' => $svg_commun + array( 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, 'gradientunits' => true ), 'radialgradient' => $svg_commun + array( 'cx' => true, 'cy' => true, 'r' => true, 'gradientunits' => true ), 'stop' => array( 'offset' => true, 'stop-color' => true, 'stop-opacity' => true, 'class' => true, 'style' => true ), 'use' => $svg_commun + array( 'x' => true, 'y' => true, 'width' => true, 'height' => true, 'href' => true ), ); // `post` couvre div/span/table/p/a/strong/ul/li/img… avec class, style, id, data-*. $html = wp_kses_allowed_html( 'post' ); // Attributs d'interface que le contexte `post` n'accorde pas : nos onglets et nos // boutons en dépendent (accessibilité + état actif). Sans eux, l'écran reste correct // mais perd son accessibilité — c'est une perte silencieuse, donc explicite ici. foreach ( array( 'div', 'span', 'a', 'button', 'table', 'th', 'td', 'tr', 'ul', 'li', 'p', 'h2', 'h3', 'h4' ) as $balise ) { if ( ! isset( $html[ $balise ] ) ) { $html[ $balise ] = array(); } $html[ $balise ] += array( 'class' => true, 'style' => true, 'id' => true, 'title' => true, 'role' => true, 'tabindex' => true, 'hidden' => true, 'aria-hidden' => true, 'aria-label' => true, 'aria-current' => true, 'aria-expanded' => true, 'aria-controls' => true, 'aria-selected' => true, 'aria-live' => true, 'data-tab' => true, 'data-target' => true, 'data-value' => true, 'data-id' => true, 'colspan' => true, 'rowspan' => true, ); } $html['button'] = ( $html['button'] ?? array() ) + array( 'type' => true, 'disabled' => true, 'value' => true, 'name' => true ); $liste = array_merge( $html, $svg ); return $liste; } } }