Export Web/SCORM : corrige le chemin de l'icône doublé "static/static/"
Le premier correctif ("static/icons/x.svg") ne suffisait pas : cette URL
est posée en style inline (--icon-url) mais CONSOMMÉE par `mask-image:
var(--icon-url)` dans static/style.css (.icon-svg) — une URL relative
dans une propriété personnalisée CSS se résout par rapport à la feuille
de style où le var() est UTILISÉ, pas où elle est définie (piège CSS
connu). Résultat : "static/icons/x.svg" redevenait
"static/static/icons/x.svg" une fois résolu depuis static/style.css
(CORS bloqué — deuxième rapport du même utilisateur). Seul "icons/x.svg"
(sans le préfixe "static/") est correct une fois résolu depuis
static/style.css. Corrigé côté rendu initial
(_relativize_absolute_urls) et côté port JS (forgeRenderIcone, qui
régénère ce widget après une action).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e08c53e042
commit
b60fe63a54
@@ -67,9 +67,21 @@ def _relativize_absolute_urls(html, slug):
|
|||||||
ailleurs que dans une URL réellement générée par le moteur. Sans ce
|
ailleurs que dans une URL réellement générée par le moteur. Sans ce
|
||||||
correctif, un index.html ouvert en local (file://) échouait à charger
|
correctif, un index.html ouvert en local (file://) échouait à charger
|
||||||
icônes/sprites/fichiers envoyés (CORS bloque une requête absolue vers
|
icônes/sprites/fichiers envoyés (CORS bloque une requête absolue vers
|
||||||
file:///static/... — "la requête CORS n'utilise pas HTTP")."""
|
file:///static/... — "la requête CORS n'utilise pas HTTP").
|
||||||
|
|
||||||
|
Cas particulier des icônes (render_icone.py) : leur URL est posée dans
|
||||||
|
un style INLINE (--icon-url), mais CONSUMÉE par `mask-image:
|
||||||
|
var(--icon-url)` dans static/style.css (voir .icon-svg) — une URL
|
||||||
|
relative dans une PROPRIÉTÉ PERSONNALISÉE CSS se résout par rapport à
|
||||||
|
la feuille de style où le var() est UTILISÉ, pas où elle est définie
|
||||||
|
(piège CSS connu). "icons/x.svg" (juste "/static/" retiré, sans le
|
||||||
|
garder) reste donc correct une fois résolu depuis static/style.css,
|
||||||
|
alors que "static/icons/x.svg" y redevient "static/static/icons/x.svg"
|
||||||
|
(bug signalé — CORS bloqué sur ce chemin doublé). Traité EN PREMIER,
|
||||||
|
plus spécifique que le remplacement générique juste après."""
|
||||||
return (
|
return (
|
||||||
html.replace(f"/game/{slug}/uploads/", "uploads/")
|
html.replace(f"/game/{slug}/uploads/", "uploads/")
|
||||||
|
.replace("/static/icons/", "icons/")
|
||||||
.replace("/static/", "static/")
|
.replace("/static/", "static/")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ test('icône — repli sur "star" si _icon_slug absent', () => {
|
|||||||
const html = forgeRenderElementHtml(el, baseGameData(), {}, {});
|
const html = forgeRenderElementHtml(el, baseGameData(), {}, {});
|
||||||
// le style entier passe par forgeHtmlEscape (comme html.escape() côté
|
// le style entier passe par forgeHtmlEscape (comme html.escape() côté
|
||||||
// serveur) : l'apostrophe de url('...') devient '.
|
// serveur) : l'apostrophe de url('...') devient '.
|
||||||
assert.ok(html.includes("--icon-url:url('static/icons/star.svg')"));
|
assert.ok(html.includes("--icon-url:url('icons/star.svg')"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('onglets — un panneau actif, les autres masqués', () => {
|
test('onglets — un panneau actif, les autres masqués', () => {
|
||||||
|
|||||||
@@ -73,13 +73,17 @@ function forgeRenderIcone(el, meta, gameData, childrenMap, ctx, parentFlexDirect
|
|||||||
var existingClass = attrs.class || "";
|
var existingClass = attrs.class || "";
|
||||||
delete attrs.class;
|
delete attrs.class;
|
||||||
attrs.class = (existingClass + " icon-svg").trim();
|
attrs.class = (existingClass + " icon-svg").trim();
|
||||||
// Chemin RELATIF (jamais "/static/...") : ce fichier ne s'exécute que
|
// Chemin RELATIF, et surtout PAS préfixé "static/" : cette URL est
|
||||||
// dans le paquet exporté (window.FORGE_OFFLINE, voir render-element.js),
|
// posée dans --icon-url (style inline) mais CONSOMMÉE par `mask-image:
|
||||||
// ouvert en local (file://) sans racine de serveur — un chemin absolu y
|
// var(--icon-url)` dans static/style.css (voir .icon-svg) — une URL
|
||||||
// déclenche un blocage CORS ("la requête CORS n'utilise pas HTTP"),
|
// relative dans une propriété personnalisée CSS se résout par rapport à
|
||||||
// exactement le bug qui affectait aussi le rendu initial côté serveur
|
// LA FEUILLE DE STYLE où le var() est utilisé (static/style.css), pas
|
||||||
// (voir _relativize_absolute_urls dans publish/build_scorm_package.py).
|
// où elle est définie (piège CSS connu) : "static/icons/x.svg" y
|
||||||
var iconUrl = "static/icons/" + iconSlug + ".svg";
|
// redeviendrait "static/static/icons/x.svg" (404/CORS bloqué — bug
|
||||||
|
// signalé). "icons/x.svg" est correct une fois résolu depuis
|
||||||
|
// static/style.css (voir aussi _relativize_absolute_urls dans
|
||||||
|
// publish/build_scorm_package.py, même correctif côté rendu initial).
|
||||||
|
var iconUrl = "icons/" + iconSlug + ".svg";
|
||||||
var style = forgeStyleString(el, parentFlexDirection, el.widget) + " --icon-url:url('" + iconUrl + "');";
|
var style = forgeStyleString(el, parentFlexDirection, el.widget) + " --icon-url:url('" + iconUrl + "');";
|
||||||
return "<i " + forgeAttrString(attrs, style) + "></i>";
|
return "<i " + forgeAttrString(attrs, style) + "></i>";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,16 @@ def test_export_scorm_icon_widget_uses_a_relative_url(client, game):
|
|||||||
part de la racine du serveur Flask) — jamais réécrit par le refactor
|
part de la racine du serveur Flask) — jamais réécrit par le refactor
|
||||||
asset_url de templates/play.html (celui-ci ne touche que les <script>/
|
asset_url de templates/play.html (celui-ci ne touche que les <script>/
|
||||||
<link> du template, pas le rendered_html déjà bake dans le payload).
|
<link> du template, pas le rendered_html déjà bake dans le payload).
|
||||||
Sans _relativize_absolute_urls (build_scorm_package.py), un index.html
|
|
||||||
ouvert en local (file://) déclenchait un blocage CORS sur cette icône
|
Cette URL, posée en style INLINE (--icon-url), est CONSOMMÉE par
|
||||||
("la requête CORS n'utilise pas HTTP") — voir aussi le miroir JS
|
`mask-image: var(--icon-url)` dans static/style.css (voir .icon-svg) —
|
||||||
|
une URL relative dans une propriété personnalisée CSS se résout par
|
||||||
|
rapport à LA FEUILLE DE STYLE où le var() est utilisé, pas où elle est
|
||||||
|
définie (piège CSS connu) : un premier correctif ("static/icons/x.svg")
|
||||||
|
redevenait donc "static/static/icons/x.svg" une fois résolu depuis
|
||||||
|
static/style.css (CORS bloqué sur ce chemin doublé — deuxième rapport
|
||||||
|
du même utilisateur) — seul "icons/x.svg" (sans "static/") est correct
|
||||||
|
une fois résolu depuis static/style.css. Voir aussi le miroir JS
|
||||||
(forgeRenderIcone, static/js/play/offline/render-special-widgets.js),
|
(forgeRenderIcone, static/js/play/offline/render-special-widgets.js),
|
||||||
qui régénère ce même widget après une action, et devait être corrigé
|
qui régénère ce même widget après une action, et devait être corrigé
|
||||||
séparément."""
|
séparément."""
|
||||||
@@ -72,7 +79,8 @@ def test_export_scorm_icon_widget_uses_a_relative_url(client, game):
|
|||||||
import io
|
import io
|
||||||
with zipfile.ZipFile(io.BytesIO(resp.data)) as zf:
|
with zipfile.ZipFile(io.BytesIO(resp.data)) as zf:
|
||||||
html = zf.read("index.html").decode("utf-8")
|
html = zf.read("index.html").decode("utf-8")
|
||||||
assert "static/icons/star.svg" in html
|
assert "icons/star.svg" in html
|
||||||
|
assert "static/icons/star.svg" not in html
|
||||||
assert "/static/icons/star.svg" not in html
|
assert "/static/icons/star.svg" not in html
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user