Land CSOC-WP-0006 PageOps: member space/page CRUD, copy, transfer
Thin CONTENT_ROOT content plane (ADR-0003/0004) with space visual field, product UI for Title/Abstract/Visual, page list sidebar, and tests. Update capability model and smoke checklist; mark WP-0006 finished.
This commit is contained in:
parent
00dbb86c93
commit
affed64839
24 changed files with 1812 additions and 151 deletions
|
|
@ -106,7 +106,7 @@ def _title_from_markdown(text: str, fallback: str) -> str:
|
|||
|
||||
|
||||
def load_space_page(space: Space, page: str | None = None) -> RenderedPage:
|
||||
"""Fetch and render a page for a space. Fail closed with error field set."""
|
||||
"""Fetch and render a page for a space. Prefer CONTENT_ROOT (ADR-0004)."""
|
||||
try:
|
||||
page_slug = normalize_page_slug(page)
|
||||
path = page_path_for(space, page_slug)
|
||||
|
|
@ -120,24 +120,21 @@ def load_space_page(space: Space, page: str | None = None) -> RenderedPage:
|
|||
error=exc.message,
|
||||
)
|
||||
|
||||
if not space.has_content_binding:
|
||||
return RenderedPage(
|
||||
slug=page_slug,
|
||||
path=path,
|
||||
title=space.title,
|
||||
html="",
|
||||
source="",
|
||||
error="This space is not bound to a Forgejo repository yet.",
|
||||
)
|
||||
|
||||
try:
|
||||
fetched = _fetch(space, path)
|
||||
html = render_markdown(fetched.text)
|
||||
title = _title_from_markdown(fetched.text, space.title)
|
||||
# Prefer frontmatter title when present
|
||||
from .pageops import parse_frontmatter
|
||||
|
||||
meta, body = parse_frontmatter(fetched.text)
|
||||
display_title = (
|
||||
str(meta.get("title") or "").strip()
|
||||
or _title_from_markdown(body or fetched.text, space.title)
|
||||
)
|
||||
html = render_markdown(body if meta else fetched.text)
|
||||
return RenderedPage(
|
||||
slug=page_slug,
|
||||
path=path,
|
||||
title=title,
|
||||
title=display_title,
|
||||
html=html,
|
||||
source=fetched.source,
|
||||
error=None,
|
||||
|
|
@ -154,6 +151,17 @@ def load_space_page(space: Space, page: str | None = None) -> RenderedPage:
|
|||
|
||||
|
||||
def _fetch(space: Space, path: str) -> FetchedFile:
|
||||
# ADR-0004: CONTENT_ROOT / spaces/<slug>/pages/…
|
||||
from . import pageops
|
||||
|
||||
try:
|
||||
local_doc = pageops.read_page(space.slug, Path(path).stem)
|
||||
if local_doc.exists and local_doc.path:
|
||||
text = Path(local_doc.path).read_text(encoding="utf-8")
|
||||
return FetchedFile(path=path, text=text, source="content-plane")
|
||||
except pageops.PageOpsError:
|
||||
pass
|
||||
|
||||
# Optional local fixture root for offline tests / air-gapped demos
|
||||
fixture_root = (getattr(settings, "SPACE_CONTENT_FIXTURE_ROOT", None) or "").strip()
|
||||
if fixture_root:
|
||||
|
|
@ -161,6 +169,11 @@ def _fetch(space: Space, path: str) -> FetchedFile:
|
|||
if local.is_file():
|
||||
return FetchedFile(path=path, text=local.read_text(encoding="utf-8"), source="fixture")
|
||||
|
||||
if not space.has_content_binding:
|
||||
raise ContentFetchError(
|
||||
"No local page on the content plane and no Forgejo binding."
|
||||
)
|
||||
|
||||
cache_key = (
|
||||
space.forgejo_owner,
|
||||
space.forgejo_repo,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue