fix: use request.host for canonical URI (preserves www. prefix)
Provenance helpers were using namespace.name as the host. Foxhop's namespace is named 'foxhop.net' but the public URI lives at 'www.foxhop.net' — exports were emitting 'https://foxhop.net/...' which works (apex 301-redirects to www) but reads wrong on a printed page. Pass request.host through into the provenance bundle so canonical URIs, chapter titles, and per-reply permalinks all preserve whatever host prefix the export was actually fetched from. Falls back to namespace.name when no host is supplied (callers outside the request context).
This commit is contained in:
parent
81344da09a
commit
97a1fffeba
3 changed files with 39 additions and 20 deletions
|
|
@ -134,10 +134,14 @@ def api_export_thread(request):
|
||||||
|
|
||||||
# Build provenance bundle so the exported document points back to its
|
# Build provenance bundle so the exported document points back to its
|
||||||
# living source on Remarkbox (canonical URI + QR + per-reply permalinks).
|
# living source on Remarkbox (canonical URI + QR + per-reply permalinks).
|
||||||
|
# Use request.host (the host the export was actually fetched from) so
|
||||||
|
# canonical URIs preserve user-facing prefixes like `www.` that the
|
||||||
|
# namespace's stored name may omit.
|
||||||
provenance = prov.build(
|
provenance = prov.build(
|
||||||
canonical_uri=prov.canonical_uri_for_node(root),
|
canonical_uri=prov.canonical_uri_for_node(root, host=request.host),
|
||||||
version=request.static_version,
|
version=request.static_version,
|
||||||
kind="thread",
|
kind="thread",
|
||||||
|
host=request.host,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Render tree to markdown
|
# Render tree to markdown
|
||||||
|
|
@ -211,9 +215,10 @@ def api_export_namespace(request):
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
provenance = prov.build(
|
provenance = prov.build(
|
||||||
canonical_uri=prov.canonical_uri_for_namespace(namespace),
|
canonical_uri=prov.canonical_uri_for_namespace(namespace, host=request.host),
|
||||||
version=request.static_version,
|
version=request.static_version,
|
||||||
kind="namespace",
|
kind="namespace",
|
||||||
|
host=request.host,
|
||||||
)
|
)
|
||||||
|
|
||||||
md = namespace_to_markdown(namespace, roots, node_fetcher, provenance=provenance)
|
md = namespace_to_markdown(namespace, roots, node_fetcher, provenance=provenance)
|
||||||
|
|
@ -300,9 +305,10 @@ def api_export_node(request):
|
||||||
subtree_nodes = [n for n in all_nodes if n.id in descendant_ids]
|
subtree_nodes = [n for n in all_nodes if n.id in descendant_ids]
|
||||||
|
|
||||||
provenance = prov.build(
|
provenance = prov.build(
|
||||||
canonical_uri=prov.canonical_uri_for_node(node),
|
canonical_uri=prov.canonical_uri_for_node(node, host=request.host),
|
||||||
version=request.static_version,
|
version=request.static_version,
|
||||||
kind="subthread",
|
kind="subthread",
|
||||||
|
host=request.host,
|
||||||
)
|
)
|
||||||
|
|
||||||
md = node_tree_to_markdown(
|
md = node_tree_to_markdown(
|
||||||
|
|
|
||||||
|
|
@ -353,7 +353,10 @@ def node_tree_to_markdown(root_node, nodes, include_root=True, provenance=None):
|
||||||
heading_level = min(depth + 2, 6)
|
heading_level = min(depth + 2, 6)
|
||||||
author = _get_author_name(child)
|
author = _get_author_name(child)
|
||||||
date = child.created_date or ""
|
date = child.created_date or ""
|
||||||
permalink = prov.permalink_for_node(child) if provenance else None
|
permalink = (
|
||||||
|
prov.permalink_for_node(child, host=provenance.get("host"))
|
||||||
|
if provenance else None
|
||||||
|
)
|
||||||
lines.append(prov.reply_heading_md(
|
lines.append(prov.reply_heading_md(
|
||||||
heading_level, author, date, permalink=permalink,
|
heading_level, author, date, permalink=permalink,
|
||||||
))
|
))
|
||||||
|
|
@ -415,7 +418,8 @@ def namespace_to_markdown(namespace, roots, node_fetcher, provenance=None):
|
||||||
chapter_title = root.title or str(root.id)
|
chapter_title = root.title or str(root.id)
|
||||||
if provenance:
|
if provenance:
|
||||||
lines.append("## [{}]({})".format(
|
lines.append("## [{}]({})".format(
|
||||||
chapter_title, prov.canonical_uri_for_node(root),
|
chapter_title,
|
||||||
|
prov.canonical_uri_for_node(root, host=provenance.get("host")),
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
lines.append("## {}".format(chapter_title))
|
lines.append("## {}".format(chapter_title))
|
||||||
|
|
@ -445,7 +449,10 @@ def namespace_to_markdown(namespace, roots, node_fetcher, provenance=None):
|
||||||
heading_level = min(depth + 3, 6)
|
heading_level = min(depth + 3, 6)
|
||||||
author = _get_author_name(child)
|
author = _get_author_name(child)
|
||||||
date = child.created_date or ""
|
date = child.created_date or ""
|
||||||
permalink = prov.permalink_for_node(child) if provenance else None
|
permalink = (
|
||||||
|
prov.permalink_for_node(child, host=provenance.get("host"))
|
||||||
|
if provenance else None
|
||||||
|
)
|
||||||
lines.append(prov.reply_heading_md(
|
lines.append(prov.reply_heading_md(
|
||||||
heading_level, author, date, permalink=permalink,
|
heading_level, author, date, permalink=permalink,
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -23,33 +23,34 @@ import segno
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def canonical_uri_for_namespace(namespace):
|
def canonical_uri_for_namespace(namespace, host=None):
|
||||||
"""Return the canonical URI of a namespace's living index.
|
"""Return the canonical URI of a namespace's living index.
|
||||||
|
|
||||||
A namespace's name IS its host (e.g. `meta.remarkbox.com`). This holds even
|
A namespace's name IS its host (e.g. `meta.remarkbox.com`). If a `host`
|
||||||
for namespaces fronted only by `my.remarkbox.com/ns/{name}` — the name
|
arg is given (typically `request.host`), prefer it — this preserves
|
||||||
remains the unique identifier.
|
user-facing prefixes like `www.` that the namespace's stored name may
|
||||||
|
omit. Falls back to `namespace.name` when no host is supplied.
|
||||||
"""
|
"""
|
||||||
return "https://{}/".format(namespace.name)
|
return "https://{}/".format(host or namespace.name)
|
||||||
|
|
||||||
|
|
||||||
def canonical_uri_for_node(node):
|
def canonical_uri_for_node(node, host=None):
|
||||||
"""Return the canonical URI of a node's living source.
|
"""Return the canonical URI of a node's living source.
|
||||||
|
|
||||||
Uses the namespace's name as host. Path comes from `node.path`
|
Uses `host` if given (typically `request.host`), otherwise the
|
||||||
(`/{id}/{slug}` or `/{id}`).
|
namespace's name. Path comes from `node.path` (`/{id}/{slug}` or `/{id}`).
|
||||||
"""
|
"""
|
||||||
return "https://{}{}".format(node.root.namespace.name, node.path)
|
return "https://{}{}".format(host or node.root.namespace.name, node.path)
|
||||||
|
|
||||||
|
|
||||||
def permalink_for_node(node):
|
def permalink_for_node(node, host=None):
|
||||||
"""Return a deep link to a single reply: thread URI plus node-id anchor.
|
"""Return a deep link to a single reply: thread URI plus node-id anchor.
|
||||||
|
|
||||||
Replies live inside a thread page, so the deep link is the thread URI with
|
Replies live inside a thread page, so the deep link is the thread URI with
|
||||||
a fragment identifier.
|
a fragment identifier. `host` overrides the namespace name when supplied.
|
||||||
"""
|
"""
|
||||||
root = node.root
|
root = node.root
|
||||||
return "https://{}{}#{}".format(root.namespace.name, root.path, node.id)
|
return "https://{}{}#{}".format(host or root.namespace.name, root.path, node.id)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -159,11 +160,15 @@ def reply_heading_md(level, author, date, permalink=None):
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def build(canonical_uri, version, kind="document", include_qr=True):
|
def build(canonical_uri, version, kind="document", include_qr=True, host=None):
|
||||||
"""Build a complete provenance bundle in one call.
|
"""Build a complete provenance bundle in one call.
|
||||||
|
|
||||||
|
`host` (e.g. `request.host`) is stashed in the bundle so the per-reply
|
||||||
|
permalink helpers and chapter-title links can preserve `www.` (or any
|
||||||
|
user-facing host prefix) instead of falling back to `namespace.name`.
|
||||||
|
|
||||||
Returns a dict suitable for splatting into `node_tree_to_markdown` or
|
Returns a dict suitable for splatting into `node_tree_to_markdown` or
|
||||||
`namespace_to_markdown` as `**provenance`.
|
`namespace_to_markdown`.
|
||||||
"""
|
"""
|
||||||
snapshot_iso = _utc_now_iso()
|
snapshot_iso = _utc_now_iso()
|
||||||
qr_data_uri = qr_png_data_uri(canonical_uri) if include_qr else None
|
qr_data_uri = qr_png_data_uri(canonical_uri) if include_qr else None
|
||||||
|
|
@ -173,4 +178,5 @@ def build(canonical_uri, version, kind="document", include_qr=True):
|
||||||
"version": version,
|
"version": version,
|
||||||
"qr_data_uri": qr_data_uri,
|
"qr_data_uri": qr_data_uri,
|
||||||
"kind": kind,
|
"kind": kind,
|
||||||
|
"host": host,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue