2026-10-05 09:36:20 +02:00
|
|
|
# retoor <retoor@molodetz.nl>
|
|
|
|
|
import re
|
|
|
|
|
from functools import lru_cache
|
|
|
|
|
|
|
|
|
|
import mistune
|
|
|
|
|
from mistune.renderers.html import HTMLRenderer
|
|
|
|
|
|
|
|
|
|
from molodetz import config
|
|
|
|
|
from molodetz.rendering import normalize_dashes
|
|
|
|
|
|
|
|
|
|
PROSE_DIR = config.TEMPLATES_DIR / "docs" / "prose"
|
|
|
|
|
|
2026-10-05 15:55:35 +02:00
|
|
|
SECTIONS = ("Start here", "Building with the API", "Admin")
|
2026-10-05 09:36:20 +02:00
|
|
|
|
|
|
|
|
DOCS_PAGES = [
|
2026-10-05 11:25:41 +02:00
|
|
|
{"slug": "welcome", "title": "Welcome", "section": "Start here", "file": "welcome.md"},
|
|
|
|
|
{"slug": "writing", "title": "Writing on the roll", "section": "Start here", "file": "writing.md"},
|
|
|
|
|
{"slug": "join", "title": "Joining", "section": "Start here", "file": "join.md"},
|
|
|
|
|
{"slug": "legal", "title": "Terms and privacy", "section": "Start here", "file": "legal.md"},
|
|
|
|
|
{"slug": "api", "title": "API and authentication", "section": "Building with the API", "file": "api.md"},
|
2026-10-06 02:56:08 +02:00
|
|
|
{"slug": "content", "title": "Galleries and content", "section": "Start here", "file": "content.md"},
|
2026-10-05 11:25:41 +02:00
|
|
|
{"slug": "admin", "title": "Admin and services", "section": "Admin", "file": "admin.md", "admin": True},
|
2026-10-06 02:56:08 +02:00
|
|
|
{"slug": "operator", "title": "Operator runbook", "section": "Admin", "file": "operator.md", "admin": True},
|
2026-10-05 11:25:41 +02:00
|
|
|
{"slug": "backups", "title": "Backups and maintenance", "section": "Admin", "file": "backups.md", "admin": True},
|
2026-10-05 09:36:20 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProseRenderer(HTMLRenderer):
|
|
|
|
|
def heading(self, text, level, **attrs):
|
|
|
|
|
anchor = re.sub(r"[^a-z0-9-]", "-", re.sub(r"<[^>]+>", "", text).lower())
|
|
|
|
|
anchor = re.sub(r"-+", "-", anchor).strip("-")
|
|
|
|
|
return f'<h{level} id="{anchor}"><a class="permalink" href="#{anchor}">{text}</a></h{level}>\n'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_prose_markdown = mistune.create_markdown(escape=False, hard_wrap=True, renderer=ProseRenderer(escape=False), plugins=["table", "strikethrough", "url"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def visible_pages(viewer_is_admin):
|
|
|
|
|
return [page for page in DOCS_PAGES if viewer_is_admin or not page.get("admin")]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def page_by_slug(slug):
|
|
|
|
|
for page in DOCS_PAGES:
|
|
|
|
|
if page["slug"] == slug:
|
|
|
|
|
return page
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache(maxsize=64)
|
|
|
|
|
def page_source(slug):
|
|
|
|
|
page = page_by_slug(slug)
|
|
|
|
|
if page is None:
|
|
|
|
|
return ""
|
|
|
|
|
return normalize_dashes((PROSE_DIR / page["file"]).read_text(encoding="utf-8"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache(maxsize=64)
|
|
|
|
|
def render_page(slug):
|
|
|
|
|
return _prose_markdown(page_source(slug))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sidebar(viewer_is_admin):
|
|
|
|
|
pages = visible_pages(viewer_is_admin)
|
|
|
|
|
return [(section, [page for page in pages if page["section"] == section]) for section in SECTIONS]
|