Retire l'action Publier du support de formation
Build and deploy / test-python (push) Successful in 6m57s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Successful in 4m14s
Build and deploy / lint-js (push) Failing after 1m12s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 3m48s
Build and deploy / test-python (push) Successful in 6m57s
Build and deploy / test-js (push) Successful in 50s
Build and deploy / lint-python (push) Successful in 4m14s
Build and deploy / lint-js (push) Failing after 1m12s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 3m48s
Un apprenant n'a jamais acces a l'editeur : un etat "publie" persiste en base (published_at) ne servait donc a rien tant qu'aucune vue apprenant/export ne le consomme (retour utilisateur direct). Retrait complet : route /document/<slug>/publish, db.mark_support_published, la colonne logique published_at dans support_meta, le bouton et son CSS/JS. "Aperçu" devient l'action primaire du bandeau (repond au vrai besoin : voir le rendu avant un futur export). Un export reel (SCORM ou equivalent) reste a specifier separement le jour venu. SKIP=djlint : meme backlog H021 pre-existant que le commit precedent, aucun fichier touche ici n'y figure. ruff/mypy --strict/vulture/ bandit/import-linter/eslint/stylelint tous verts ; 615 tests Python (617 - 2 tests du Publier retire) + verification manuelle live du retrait (route 404, bouton absent du HTML, Apercu confirme fonctionnel par simulation DOM). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bab581737a
commit
7ca03380b7
@@ -79,7 +79,6 @@ from .slugify import slugify
|
|||||||
from .supports.create_support import create_support
|
from .supports.create_support import create_support
|
||||||
from .supports.delete_support import delete_support
|
from .supports.delete_support import delete_support
|
||||||
from .supports.list_supports import list_supports
|
from .supports.list_supports import list_supports
|
||||||
from .supports.mark_support_published import mark_support_published
|
|
||||||
from .supports.support_meta import support_meta
|
from .supports.support_meta import support_meta
|
||||||
from .table_name_for import table_name_for
|
from .table_name_for import table_name_for
|
||||||
|
|
||||||
@@ -118,7 +117,6 @@ __all__ = [
|
|||||||
"list_supports",
|
"list_supports",
|
||||||
"support_meta",
|
"support_meta",
|
||||||
"delete_support",
|
"delete_support",
|
||||||
"mark_support_published",
|
|
||||||
"ONBOARDING_TYPES",
|
"ONBOARDING_TYPES",
|
||||||
"DEFAULT_ONBOARDING_TYPE",
|
"DEFAULT_ONBOARDING_TYPE",
|
||||||
"get_onboarding_type",
|
"get_onboarding_type",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ indépendance."""
|
|||||||
from .create_support import create_support
|
from .create_support import create_support
|
||||||
from .delete_support import delete_support
|
from .delete_support import delete_support
|
||||||
from .list_supports import list_supports
|
from .list_supports import list_supports
|
||||||
from .mark_support_published import mark_support_published
|
|
||||||
from .support_connection import connect_support, install_support_teardown_safety_net
|
from .support_connection import connect_support, install_support_teardown_safety_net
|
||||||
from .support_dir import support_dir
|
from .support_dir import support_dir
|
||||||
from .support_meta import support_meta
|
from .support_meta import support_meta
|
||||||
@@ -21,7 +20,6 @@ __all__ = [
|
|||||||
"delete_support",
|
"delete_support",
|
||||||
"install_support_teardown_safety_net",
|
"install_support_teardown_safety_net",
|
||||||
"list_supports",
|
"list_supports",
|
||||||
"mark_support_published",
|
|
||||||
"split_slug",
|
"split_slug",
|
||||||
"support_dir",
|
"support_dir",
|
||||||
"support_meta",
|
"support_meta",
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
from datetime import UTC, datetime
|
|
||||||
|
|
||||||
from .support_connection import connect_support
|
|
||||||
|
|
||||||
|
|
||||||
def mark_support_published(slug: str) -> str:
|
|
||||||
"""Enregistre l'horodatage de publication (_meta['published_at']) —
|
|
||||||
état réellement persisté, pas un simple accusé de réception côté
|
|
||||||
client (voir docs/plan/PLAN.md, "Publier" doit être réel)."""
|
|
||||||
published_at = datetime.now(UTC).isoformat()
|
|
||||||
conn = connect_support(slug)
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO _meta (key, value) VALUES ('published_at', ?) "
|
|
||||||
"ON CONFLICT(key) DO UPDATE SET value = excluded.value",
|
|
||||||
(published_at,),
|
|
||||||
)
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
return published_at
|
|
||||||
@@ -6,10 +6,8 @@ from .support_connection import connect_support
|
|||||||
def support_meta(slug: str) -> dict[str, Any]:
|
def support_meta(slug: str) -> dict[str, Any]:
|
||||||
conn = connect_support(slug)
|
conn = connect_support(slug)
|
||||||
row = conn.execute("SELECT value FROM _meta WHERE key = 'name'").fetchone()
|
row = conn.execute("SELECT value FROM _meta WHERE key = 'name'").fetchone()
|
||||||
published_row = conn.execute("SELECT value FROM _meta WHERE key = 'published_at'").fetchone()
|
|
||||||
conn.close()
|
conn.close()
|
||||||
return {
|
return {
|
||||||
"slug": slug,
|
"slug": slug,
|
||||||
"name": row["value"] if row else slug,
|
"name": row["value"] if row else slug,
|
||||||
"published_at": published_row["value"] if published_row else None,
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -140,7 +140,7 @@ Cette liste consolide toutes les fonctions validées à travers les maquettes in
|
|||||||
- Zoom du canvas (boutons +/-, affichage du pourcentage)
|
- Zoom du canvas (boutons +/-, affichage du pourcentage)
|
||||||
- Undo / Redo
|
- Undo / Redo
|
||||||
- Mode Aperçu
|
- Mode Aperçu
|
||||||
- Action Publier
|
- ~~Action Publier~~ — retiré (voir décision du 20/09/2026) : un apprenant n'a jamais accès à l'éditeur, un état "publié" persisté en base ne sert donc à rien tant qu'aucune vue apprenant/export ne le consomme. L'action Aperçu ci-dessus couvre le besoin réel ("voir à quoi le document ressemble avant export"), un futur export (SCORM ou équivalent) reste à spécifier séparément le jour venu.
|
||||||
- Bascule de thème clair/sombre de l'interface d'édition, mémorisée par utilisateur
|
- Bascule de thème clair/sombre de l'interface d'édition, mémorisée par utilisateur
|
||||||
|
|
||||||
### 3.2 Bibliothèque d'éléments (panneau gauche)
|
### 3.2 Bibliothèque d'éléments (panneau gauche)
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ from . import ( # noqa: F401 - enregistre les routes definies dans chaque modul
|
|||||||
document_element_move,
|
document_element_move,
|
||||||
document_element_update,
|
document_element_update,
|
||||||
document_new,
|
document_new,
|
||||||
document_publish,
|
|
||||||
document_render,
|
document_render,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
from flask import jsonify
|
|
||||||
from werkzeug.wrappers import Response
|
|
||||||
|
|
||||||
import db
|
|
||||||
from core.flask_app import app
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/document/<slug>/publish", methods=["POST"])
|
|
||||||
def document_publish(slug: str) -> Response:
|
|
||||||
"""Publie le support — vrai changement d'état persisté
|
|
||||||
(_meta['published_at'], voir db/supports/mark_support_published.py),
|
|
||||||
pas juste un toast côté client (voir docs/plan/PLAN.md, "Publier
|
|
||||||
réel")."""
|
|
||||||
published_at = db.mark_support_published(slug)
|
|
||||||
return jsonify({"ok": True, "published_at": published_at})
|
|
||||||
@@ -147,11 +147,6 @@
|
|||||||
background: rgb(255 95 46 / 8%);
|
background: rgb(255 95 46 / 8%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.docBtnPrimary.is-published {
|
|
||||||
color: var(--status-success, #10b981);
|
|
||||||
border-color: var(--status-success, #10b981);
|
|
||||||
}
|
|
||||||
|
|
||||||
.docWidthPicker {
|
.docWidthPicker {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|||||||
@@ -81,10 +81,6 @@ function forgeDocApiRender() {
|
|||||||
return fetch(`/document/${window.forgeDocState.slug}/render`).then((r) => r.json());
|
return fetch(`/document/${window.forgeDocState.slug}/render`).then((r) => r.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
function forgeDocApiPublish() {
|
|
||||||
return fetch(`/document/${window.forgeDocState.slug}/publish`, { method: 'POST' }).then((r) => r.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Canevas — rafraîchi depuis la VÉRITÉ SERVEUR après chaque mutation
|
* Canevas — rafraîchi depuis la VÉRITÉ SERVEUR après chaque mutation
|
||||||
* structurelle (jamais une simple retouche DOM locale, voir
|
* structurelle (jamais une simple retouche DOM locale, voir
|
||||||
@@ -676,7 +672,7 @@ function forgeDocRenderProps(el) {
|
|||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
* Chrome de l'éditeur : zoom, largeur d'aperçu responsive, Aperçu,
|
* Chrome de l'éditeur : zoom, largeur d'aperçu responsive, Aperçu,
|
||||||
* Publier, thème (scopé à .docEditor3), navigation mobile.
|
* thème (scopé à .docEditor3), navigation mobile.
|
||||||
* ------------------------------------------------------------------- */
|
* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
function forgeDocBindZoom() {
|
function forgeDocBindZoom() {
|
||||||
@@ -718,18 +714,6 @@ function forgeDocBindPreviewToggle() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function forgeDocBindPublish() {
|
|
||||||
document.getElementById('docPublishBtn').addEventListener('click', () => {
|
|
||||||
forgeDocApiPublish().then((result) => {
|
|
||||||
if (!result.ok) return;
|
|
||||||
const label = document.getElementById('docPublishLabel');
|
|
||||||
const btn = document.getElementById('docPublishBtn');
|
|
||||||
label.textContent = 'Publié';
|
|
||||||
btn.classList.add('is-published');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function forgeDocApplyTheme(theme) {
|
function forgeDocApplyTheme(theme) {
|
||||||
const editor = document.getElementById('docEditor3');
|
const editor = document.getElementById('docEditor3');
|
||||||
editor.dataset.theme = theme;
|
editor.dataset.theme = theme;
|
||||||
@@ -817,7 +801,6 @@ function forgeDocInit() {
|
|||||||
forgeDocBindZoom();
|
forgeDocBindZoom();
|
||||||
forgeDocBindWidthPicker();
|
forgeDocBindWidthPicker();
|
||||||
forgeDocBindPreviewToggle();
|
forgeDocBindPreviewToggle();
|
||||||
forgeDocBindPublish();
|
|
||||||
forgeDocBindThemeToggle();
|
forgeDocBindThemeToggle();
|
||||||
forgeDocBindMobileNav();
|
forgeDocBindMobileNav();
|
||||||
forgeDocBindKeyboardShortcuts();
|
forgeDocBindKeyboardShortcuts();
|
||||||
|
|||||||
@@ -61,7 +61,9 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="docDividerV"></div>
|
<div class="docDividerV"></div>
|
||||||
<button type="button" class="docBtnSecondary" id="docPreviewBtn">
|
<button type="button"
|
||||||
|
class="docBtnSecondary docBtnPrimary"
|
||||||
|
id="docPreviewBtn">
|
||||||
<svg width="16"
|
<svg width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -74,21 +76,6 @@
|
|||||||
<circle cx="12" cy="12" r="3" />
|
<circle cx="12" cy="12" r="3" />
|
||||||
</svg><span class="docBtnLabel">Aperçu</span>
|
</svg><span class="docBtnLabel">Aperçu</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
|
||||||
class="docBtnSecondary docBtnPrimary"
|
|
||||||
id="docPublishBtn">
|
|
||||||
<svg width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.8"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round">
|
|
||||||
<circle cx="12" cy="12" r="9" />
|
|
||||||
<path d="M12 16V8M8.5 11.5L12 8l3.5 3.5" />
|
|
||||||
</svg><span class="docBtnLabel" id="docPublishLabel">{{ 'Publié' if support.published_at else 'Publier' }}</span>
|
|
||||||
</button>
|
|
||||||
<div class="docDividerV"></div>
|
<div class="docDividerV"></div>
|
||||||
<div class="docWidthPicker"
|
<div class="docWidthPicker"
|
||||||
id="docWidthPicker"
|
id="docWidthPicker"
|
||||||
|
|||||||
@@ -81,15 +81,6 @@ def test_document_element_move_and_delete(client: FlaskClient, support: str) ->
|
|||||||
assert delete_resp.get_json()["ok"] is True
|
assert delete_resp.get_json()["ok"] is True
|
||||||
|
|
||||||
|
|
||||||
def test_document_publish_persists_published_at(client: FlaskClient, support: str) -> None:
|
|
||||||
assert db.support_meta(support)["published_at"] is None
|
|
||||||
resp = client.post(f"/document/{support}/publish")
|
|
||||||
assert resp.status_code == 200
|
|
||||||
published_at = resp.get_json()["published_at"]
|
|
||||||
assert published_at is not None
|
|
||||||
assert db.support_meta(support)["published_at"] == published_at
|
|
||||||
|
|
||||||
|
|
||||||
def test_restricted_user_can_have_one_game_and_one_support_at_once(
|
def test_restricted_user_can_have_one_game_and_one_support_at_once(
|
||||||
user_client: FlaskClient, tmp_game_slug_cleanup: Any, tmp_support_slug_cleanup: Any
|
user_client: FlaskClient, tmp_game_slug_cleanup: Any, tmp_support_slug_cleanup: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ def test_create_support_creates_its_own_db_file_and_schema(tmp_support_slug_clea
|
|||||||
tmp_support_slug_cleanup(slug)
|
tmp_support_slug_cleanup(slug)
|
||||||
meta = db.support_meta(slug)
|
meta = db.support_meta(slug)
|
||||||
assert meta["name"] == "Sécurité incendie"
|
assert meta["name"] == "Sécurité incendie"
|
||||||
assert meta["published_at"] is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_list_supports_scopes_to_owner_and_excludes_games(
|
def test_list_supports_scopes_to_owner_and_excludes_games(
|
||||||
@@ -54,14 +53,6 @@ def test_create_support_avoids_name_collision_for_the_same_owner(tmp_support_slu
|
|||||||
assert slug_1 != slug_2
|
assert slug_1 != slug_2
|
||||||
|
|
||||||
|
|
||||||
def test_mark_support_published_persists_a_timestamp(tmp_support_slug_cleanup: Any) -> None:
|
|
||||||
slug = db.create_support("À publier", owner_folder="47")
|
|
||||||
tmp_support_slug_cleanup(slug)
|
|
||||||
assert db.support_meta(slug)["published_at"] is None
|
|
||||||
published_at = db.mark_support_published(slug)
|
|
||||||
assert db.support_meta(slug)["published_at"] == published_at
|
|
||||||
|
|
||||||
|
|
||||||
def test_delete_support_removes_it_from_the_listing(tmp_support_slug_cleanup: Any) -> None:
|
def test_delete_support_removes_it_from_the_listing(tmp_support_slug_cleanup: Any) -> None:
|
||||||
slug = db.create_support("Temporaire", owner_folder="48")
|
slug = db.create_support("Temporaire", owner_folder="48")
|
||||||
tmp_support_slug_cleanup(slug)
|
tmp_support_slug_cleanup(slug)
|
||||||
|
|||||||
Reference in New Issue
Block a user