feat(legal): B-2.9 6 pages légales (CGU, Loi 25, cookies, remboursement, accessibilité, mentions)
- src/legal/__init__.py: define canonical LEGAL_VERSION='2026-04-27' constant (single source of truth — auth.py now imports it as SIGNUP_LEGAL_VERSION). - src/legal/routes.py: add /legal/<page> + /legal/ index routes; markdown rendered from src/legal/content/*.md with toc, tables, fenced_code, attr_list extensions. - src/legal/content/: 6 French (Québec) markdown documents — DictIA Inc. / InnovA AI S.E.N.C. branding, Loi 25-compliant 12-section privacy policy, WCAG 2.2 AA accessibility statement, AGPL-3.0 attribution. All marked DRAFT v1.0 pending legal review by Allison Rioux. - templates/legal/_layout.html + index.html: extends marketing/base.html; inline .legal-content typographic styles (no CSS rebuild required). - .gitignore: allow-rule for src/legal/content/*.md so markdown is tracked despite the global *.md ignore. - tests/test_legal_pages.py: 9 tests covering 200 responses, DictIA branding, rprp@dictia.ca presence, 12 mandatory Loi 25 sections, public indexability (no X-Robots-Tag noindex), shared layout, marketing/base.html extension, DRAFT callout, and LEGAL_VERSION/SIGNUP_LEGAL_VERSION equivalence. - tests/_run_legal_pages_windows.py: manual driver (Windows fcntl stub). - static/css/marketing.css: regenerated by `npm run build:css` to include new utility classes referenced from templates/legal/*.html. Tests: 9/9 pass. No off-limits files modified beyond the 2-line auth.py constant move spec'd in B-2.9. No schema changes; markdown==3.5.1 already pinned in requirements.txt (B-1.1). Pages publicly indexable by design (Loi 25 transparency). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
84
src/legal/routes.py
Normal file
84
src/legal/routes.py
Normal file
@@ -0,0 +1,84 @@
|
||||
"""Legal pages — 6 markdown-rendered pages (B-2.9).
|
||||
|
||||
Each page extends templates/legal/_layout.html and is publicly indexable
|
||||
(see src/app.py:_PUBLIC_INDEXABLE_PREFIXES = ('marketing.', 'legal.')).
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
import markdown
|
||||
from flask import abort, render_template
|
||||
|
||||
from src.legal import LEGAL_VERSION, legal_bp
|
||||
|
||||
CONTENT_DIR = Path(__file__).parent / 'content'
|
||||
|
||||
VALID_PAGES = (
|
||||
'conditions',
|
||||
'confidentialite',
|
||||
'cookies',
|
||||
'remboursement',
|
||||
'accessibilite',
|
||||
'mentions',
|
||||
)
|
||||
|
||||
PAGE_TITLES = {
|
||||
'conditions': "Conditions d'utilisation",
|
||||
'confidentialite': "Politique de confidentialité (Loi 25)",
|
||||
'cookies': "Politique de cookies",
|
||||
'remboursement': "Politique de remboursement",
|
||||
'accessibilite': "Déclaration d'accessibilité (WCAG 2.2 AA)",
|
||||
'mentions': "Mentions légales",
|
||||
}
|
||||
|
||||
PAGE_DESCRIPTIONS = {
|
||||
'conditions': "Conditions d'utilisation du service DictIA — droits, obligations, responsabilités.",
|
||||
'confidentialite': "Politique de confidentialité conforme à la Loi 25 du Québec — collecte, conservation, droits des utilisateurs.",
|
||||
'cookies': "Utilisation des cookies et traceurs sur les sites DictIA.",
|
||||
'remboursement': "Politique de remboursement des abonnements DictIA.",
|
||||
'accessibilite': "Engagement DictIA en matière d'accessibilité numérique (WCAG 2.2 AA).",
|
||||
'mentions': "Mentions légales — DictIA Inc. (filiale d'InnovA AI S.E.N.C.).",
|
||||
}
|
||||
|
||||
|
||||
def _render_markdown(page: str) -> str:
|
||||
"""Read the markdown file for `page` and return rendered HTML."""
|
||||
md_path = CONTENT_DIR / f'{page}.md'
|
||||
if not md_path.exists():
|
||||
abort(404)
|
||||
raw = md_path.read_text(encoding='utf-8')
|
||||
return markdown.markdown(
|
||||
raw,
|
||||
extensions=['toc', 'tables', 'fenced_code', 'attr_list'],
|
||||
output_format='html5',
|
||||
)
|
||||
|
||||
|
||||
@legal_bp.route('/<page>')
|
||||
def legal_page(page):
|
||||
"""Render one of the 6 legal pages by slug."""
|
||||
if page not in VALID_PAGES:
|
||||
abort(404)
|
||||
return render_template(
|
||||
'legal/_layout.html',
|
||||
title=PAGE_TITLES[page],
|
||||
description=PAGE_DESCRIPTIONS[page],
|
||||
content=_render_markdown(page),
|
||||
page=page,
|
||||
legal_version=LEGAL_VERSION,
|
||||
)
|
||||
|
||||
|
||||
@legal_bp.route('/')
|
||||
def legal_index():
|
||||
"""Index page listing all 6 legal pages."""
|
||||
pages = [
|
||||
{'slug': slug, 'title': PAGE_TITLES[slug], 'description': PAGE_DESCRIPTIONS[slug]}
|
||||
for slug in VALID_PAGES
|
||||
]
|
||||
return render_template(
|
||||
'legal/index.html',
|
||||
title="Documents légaux DictIA",
|
||||
description="Index des documents légaux DictIA — conditions, confidentialité, cookies, remboursement, accessibilité, mentions.",
|
||||
pages=pages,
|
||||
legal_version=LEGAL_VERSION,
|
||||
)
|
||||
Reference in New Issue
Block a user