diff --git a/CHANGELOG.md b/CHANGELOG.md index c787d03..c9b0834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v7.85.4 (2026-11-06) — Barre commune v0.6.0 re-vendangée : entrée « Administration » (fusion #142) + +- **Re-vendange verbatim** de la barre commons v0.6.0 (PR dracodev-commons #14) : `static/js/vendor/draco-barre.js` + `static/css/draco-barre.css`. Le menu compte gagne l'entrée **Administration** → `/admin` (le Hub redirige vers `/admin/utilisateurs`), à **visibilité conditionnelle** (affirmée par l'hôte via `cfg.admin` / `DRACO_BAR.setAdmin`, jamais déduite par la barre). +- **Pas de drapeau chez Flowdeck** : Flowdeck ne connaît que le rôle du registre du Hub pour sa propre application (consulté au pont draco avant le JIT) — pas le rôle global de suite (rôle du compte chez ObsiGate, la définition de la garde du Hub). À défaut de certitude, le contrat v0.6.0 impose de ne pas affirmer : l'entrée reste masquée dans Flowdeck (elle apparaît chez ObsiGate et dans la console, qui peuvent l'établir). Commentaire posé dans le gabarit `_draco_bar.html`. +- **Test** : `tests/test_draco_bar_texte.py::test_barre_v060_entree_administration_vendangee` (marqueurs de la vendange dans les assets servis). + ## v7.85.3 (2026-11-06) — Déconnexion du menu compte branchée (fusion #141) - **Défaut corrigé** : le bouton « Déconnexion » du menu compte de la barre commune ne faisait rien — la barre relaie l'action `deconnexion` à l'hôte et le `onAction` du gabarit (`app/templates/_draco_bar.html`) n'avait aucune branche pour elle (même défaut que la console Spark, l'hôte du signalement de Bruno). diff --git a/VERSION b/VERSION index 020a223..b2c144f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.85.3 +7.85.4 diff --git a/app/templates/_draco_bar.html b/app/templates/_draco_bar.html index 176842b..aba7e68 100644 --- a/app/templates/_draco_bar.html +++ b/app/templates/_draco_bar.html @@ -113,6 +113,14 @@ dérivation par l'agent, défaut officiel) alimente les liens du menu compte (Télécharger, À Propos, Aides, Profil) via urls.hub. */ + /* Barre v0.6.0 (fusion #142) : JS/CSS re-vendangés verbatim + — le menu compte gagne l'entrée « Administration » + (lien Hub /admin) à visibilité conditionnelle. + Flowdeck ne passe PAS le drapeau admin : il ne connaît + que le rôle du registre du Hub pour sa propre app + (consulté au pont draco), pas le rôle global de suite + (rôle du compte chez ObsiGate) — à défaut de certitude, + l'entrée reste masquée ici, comme le contrat l'exige. */ urls: { hub: "{{ draco_urls().hub }}/" }, domain: (function () { try { return localStorage.getItem("draco_domain") || "pro"; } catch (e) { return "pro"; } })(), counts: {}, diff --git a/static/css/draco-barre.css b/static/css/draco-barre.css index 55ab916..431f81a 100644 --- a/static/css/draco-barre.css +++ b/static/css/draco-barre.css @@ -101,6 +101,11 @@ text-decoration: none; cursor: pointer; } .draco-menu-item:hover, .draco-menu-item:focus-visible { background: var(--surface-2); } +/* Item conditionnel masqué (v0.6.0 : « Administration » tant que + l'hôte n'a pas affirmé le statut d'administrateur) — le + display:flex ci-dessus écraserait l'attribut hidden sans cette + règle, même patron que .draco-compte-menu[hidden]. */ +.draco-menu-item[hidden] { display: none; } /* Icônes monoline du menu compte (v0.4.0) : appendues après le libellé dans le DOM, replacées à gauche par order: -1. */ .draco-menu-icone { diff --git a/static/js/vendor/draco-barre.js b/static/js/vendor/draco-barre.js index 7b66b81..ff7e3d1 100644 --- a/static/js/vendor/draco-barre.js +++ b/static/js/vendor/draco-barre.js @@ -42,6 +42,22 @@ * l'ouverture par le mécanisme data-theme des hôtes, avec cache * local court et repli intégral sur le comportement actuel de * l'hôte si le Hub ne répond pas. Voir « Thème commun » plus bas. + * v0.6.0 (demande de Bruno, 2026-11-06) : le menu compte gagne un + * cinquième lien Hub, « Administration » → /admin (le Hub y + * redirige vers /admin/utilisateurs), placé après le séparateur, + * avant « Déconnexion », avec son icône monoline (curseurs de + * réglage). AFFICHAGE CONDITIONNEL : l'entrée n'apparaît que si + * l'hôte AFFIRME que le compte courant est administrateur de + * suite — cfg.admin === true à la configuration, ou + * DRACO_BAR.setAdmin(true) quand l'hôte l'apprend après coup + * (patron setUrls). L'hôte ne passe le drapeau que s'il l'a établi + * depuis SA source d'autorité (rôle global du compte chez + * ObsiGate, niveau du registre des rôles du Hub pour son app…) ; + * à défaut de certitude, pas de drapeau et l'entrée reste masquée. + * La barre ne devine jamais : le jeton draco ne porte aucun rôle + * (claims minimaux, par design). La vraie protection reste la + * garde serveur du Hub (/admin/*), pas ce masquage d'affichage. + * Voir le README, « Administration » — contrat de visibilité. * * Aucun gestionnaire inline : un seul écouteur délégué sur le document * (patron validé par le prototype — les onclick du HTML généré sont @@ -54,6 +70,11 @@ * counts: { tiktak: 3, kanbante: 3, alertes: 3, spark: 0 }, * user: "Bruno", // nom affiché (v0.1.6) ; * // ou { display_name: "Bruno", username: "bruno" } + * admin: true, // (v0.6.0, optionnel) + * // administrateur de suite ÉTABLI par l'hôte : démasque + * // l'entrée « Administration » du menu compte. Absent ou + * // non-strictement-true → entrée masquée. Peut aussi être + * // posé après coup via DRACO_BAR.setAdmin(true). * urls: { // bases des applications * obsigate: "https://og.dracodev.net/", * flowdeck: "https://flowdeck.dracodev.net/", @@ -653,6 +674,18 @@ ["path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }], ["circle", { cx: "12", cy: "7", r: "4" }] ], + /* Curseurs de réglage (trois axes, trois boutons) — v0.6.0. */ + "admin": [ + ["line", { x1: "4", y1: "21", x2: "4", y2: "14" }], + ["line", { x1: "4", y1: "10", x2: "4", y2: "3" }], + ["line", { x1: "12", y1: "21", x2: "12", y2: "12" }], + ["line", { x1: "12", y1: "8", x2: "12", y2: "3" }], + ["line", { x1: "20", y1: "21", x2: "20", y2: "16" }], + ["line", { x1: "20", y1: "12", x2: "20", y2: "3" }], + ["line", { x1: "1", y1: "14", x2: "7", y2: "14" }], + ["line", { x1: "9", y1: "8", x2: "15", y2: "8" }], + ["line", { x1: "17", y1: "16", x2: "23", y2: "16" }] + ], /* Flèche sortant d'un cadre (porte). */ "deconnexion": [ ["path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }], @@ -687,6 +720,15 @@ return span; } + /* Visibilité de l'entrée « Administration » (v0.6.0) : vraie + * seulement si l'hôte l'a AFFIRMÉE — cfg.admin === true à la + * configuration, ou DRACO_BAR.setAdmin(true) après coup. La + * barre ne déduit jamais ce statut (le jeton draco ne porte + * aucun rôle) ; à défaut, l'entrée reste masquée et la garde + * serveur du Hub reste la vraie protection. */ + var compteAdmin = cfg.admin === true; + var adminItem = null; + /* Menu compte (v0.1.6 ; v0.3.0 pour les liens) — en-tête * d'identité, puis les liens du Hub (Télécharger, À Propos, * Aides, Profil — vrais , même patron que le menu des @@ -719,6 +761,22 @@ sep.className = "draco-menu-sep"; sep.setAttribute("aria-hidden", "true"); compteMenu.appendChild(sep); + /* « Administration » (v0.6.0) : cinquième lien Hub + * (/admin, où le Hub redirige vers /admin/utilisateurs). + * data-hub-link="admin" : son href est resynchronisé par + * refreshCompteHrefs / setUrls comme les quatre autres liens. + * Toujours construit ; masqué tant que l'hôte n'a pas affirmé + * le statut d'administrateur (compteAdmin). */ + var adm = document.createElement("a"); + adm.className = "draco-menu-item"; + adm.href = hubHref("admin"); + adm.setAttribute("data-hub-link", "admin"); + adm.setAttribute("role", "menuitem"); + adm.textContent = "Administration"; + adm.appendChild(menuIcone("admin")); + adm.hidden = !compteAdmin; + compteMenu.appendChild(adm); + adminItem = adm; var b = document.createElement("button"); b.type = "button"; b.className = "draco-menu-item"; @@ -734,6 +792,7 @@ var head = compteMenu.querySelector(".draco-compte-nom"); var name = displayName(); if (head) head.textContent = name || "Compte"; + if (adminItem) adminItem.hidden = !compteAdmin; refreshCompteHrefs(); } function toggleCompteMenu(force) { @@ -835,6 +894,14 @@ window.DRACO_BAR.urls = resolvedUrls; applyUrls(resolvedUrls); refreshCompteHrefs(); + }, + /* Administration (v0.6.0) : l'hôte pose le drapeau quand il a + * ÉTABLI que le compte courant est administrateur de suite + * (sa source d'autorité — voir l'en-tête et le README). Toute + * valeur autre que le booléen true masque l'entrée. */ + setAdmin: function (v) { + compteAdmin = v === true; + if (adminItem) adminItem.hidden = !compteAdmin; } }; diff --git a/tests/test_draco_bar_texte.py b/tests/test_draco_bar_texte.py index 70a65a9..05ab235 100644 --- a/tests/test_draco_bar_texte.py +++ b/tests/test_draco_bar_texte.py @@ -45,6 +45,23 @@ def test_barre_deconnexion_branchee_globale(): assert '{{ draco_urls().obsigate }}/sso/logout?return_to=' in gabarit +def test_barre_v060_entree_administration_vendangee(): + """Barre v0.6.0 (fusion #142) : JS/CSS vendangés portent l'entrée + « Administration » du menu compte (lien Hub data-hub-link + « admin », setAdmin, règle CSS [hidden]) ; le gabarit documente + pourquoi Flowdeck ne passe pas le drapeau (pas de rôle global + connu) — aucun « admin: true » dans la configuration servie.""" + js = (ROOT / "static/js/vendor/draco-barre.js").read_text() + assert 'setAttribute("data-hub-link", "admin")' in js + assert '"Administration"' in js + assert "setAdmin" in js + css = (ROOT / "static/css/draco-barre.css").read_text() + assert ".draco-menu-item[hidden]" in css + gabarit = (ROOT / "app/templates/_draco_bar.html").read_text() + assert "v0.6.0" in gabarit + assert "admin: true" not in gabarit + + def test_barre_v020_onglet_shaarit_et_garde_data_theme(): """Barre v0.2.0 (commons @70c837a) : onglet ShaarIt entre Kanbante et l'onglet agent, logo fourni tel quel, CSS vendangé verbatim