feat(booth): view .md (rendered) and .txt/.log in-booth without downloading
Loose .md/.txt/.log files rendered as forced-download links in the gallery
and downloaded (or showed raw) when opened. Now they open in a readable
in-booth page via the existing /b/<name>/view route:
- .md -> rendered HTML (Python-Markdown: fenced code, tables, sane lists),
styled in an Australis .markdown-body with the viewer chrome;
- .txt/.log -> preformatted <pre> text view.
The gallery links docs to the viewer (📄) instead of a download; the view
page keeps a ⬇ (?dl=1) for saving. Files over 2 MB hand back raw. New
markdown dep (optional-import: degrades .md to text view if absent).
booth_image_view -> booth_view_file (now handles image + doc + raw-fallback).
9 new tests, suite 44 passing; deployed + verified live on nh3-dev :8090.
This commit is contained in:
@@ -10,9 +10,11 @@ from booth.app import (
|
||||
build_gallery,
|
||||
classify,
|
||||
create_app,
|
||||
doc_kind,
|
||||
generate_pickup_id,
|
||||
human_dur,
|
||||
is_expired,
|
||||
render_doc,
|
||||
safe_upload_name,
|
||||
sweep_once,
|
||||
wrap_verbatim_html,
|
||||
@@ -353,12 +355,12 @@ def test_image_view_traversal_404(client):
|
||||
assert c.get("/b/run1/view", params={"f": "../../etc/passwd"}).status_code == 404
|
||||
|
||||
|
||||
def test_image_view_nonimage_redirects_to_raw(client):
|
||||
def test_view_nonviewable_redirects_to_raw(client):
|
||||
c, data = client
|
||||
_touch(data / "run1" / "notes.txt")
|
||||
r = c.get("/b/run1/view", params={"f": "notes.txt"}, follow_redirects=False)
|
||||
_touch(data / "run1" / "data.bin") # not image/md/txt -> nothing to render, hand back raw
|
||||
r = c.get("/b/run1/view", params={"f": "data.bin"}, follow_redirects=False)
|
||||
assert r.status_code == 307
|
||||
assert r.headers["location"] == "/b/run1/notes.txt"
|
||||
assert r.headers["location"] == "/b/run1/data.bin"
|
||||
|
||||
|
||||
# ---- verbatim-index.html wrapper --------------------------------------------
|
||||
@@ -435,3 +437,59 @@ def test_verbatim_index_raw_file_route_unwrapped(client):
|
||||
r = c.get("/b/brief/index.html")
|
||||
assert r.status_code == 200
|
||||
assert "booth-nav-home" not in r.text
|
||||
|
||||
|
||||
# ---- .md / .txt in-booth doc viewer -----------------------------------------
|
||||
|
||||
|
||||
def test_doc_kind():
|
||||
assert doc_kind("notes.md") == "markdown"
|
||||
assert doc_kind("a.markdown") == "markdown"
|
||||
assert doc_kind("log.txt") == "text"
|
||||
assert doc_kind("run.log") == "text"
|
||||
assert doc_kind("shot.png") is None
|
||||
assert doc_kind("data.bin") is None
|
||||
|
||||
|
||||
def test_render_doc_markdown():
|
||||
html, is_html = render_doc("# Title\n\n- a\n- b\n", "markdown")
|
||||
assert is_html is True
|
||||
assert "<h1>" in html and "Title" in html
|
||||
assert "<li>" in html
|
||||
|
||||
|
||||
def test_render_doc_text_is_verbatim():
|
||||
body, is_html = render_doc("plain\ntext", "text")
|
||||
assert is_html is False and body == "plain\ntext"
|
||||
|
||||
|
||||
def test_view_markdown_renders(client):
|
||||
c, data = client
|
||||
d = data / "run1"; d.mkdir()
|
||||
(d / "notes.md").write_text("# Heading\n\nsome **bold** text\n")
|
||||
r = c.get("/b/run1/view", params={"f": "notes.md"})
|
||||
assert r.status_code == 200
|
||||
assert "<h1>" in r.text and "Heading" in r.text # rendered, not raw markdown
|
||||
assert "<strong>bold</strong>" in r.text
|
||||
assert "attachment" not in r.headers.get("content-disposition", "") # viewed, not downloaded
|
||||
|
||||
|
||||
def test_view_text_shows_preformatted(client):
|
||||
c, data = client
|
||||
d = data / "run1"; d.mkdir()
|
||||
(d / "out.txt").write_text("line one\nline two")
|
||||
r = c.get("/b/run1/view", params={"f": "out.txt"})
|
||||
assert r.status_code == 200
|
||||
assert "<pre" in r.text and "line one" in r.text
|
||||
assert "attachment" not in r.headers.get("content-disposition", "")
|
||||
|
||||
|
||||
def test_gallery_links_docs_to_view(client):
|
||||
c, data = client
|
||||
d = data / "run1"; d.mkdir()
|
||||
(d / "readme.md").write_text("# hi")
|
||||
(d / "notes.txt").write_text("hello") # loose txt (no media partner) -> own item
|
||||
page = c.get("/b/run1/")
|
||||
assert "view?f=readme.md" in page.text # md -> viewer
|
||||
assert "view?f=notes.txt" in page.text # txt -> viewer
|
||||
assert 'href="readme.md" download' not in page.text # not a forced download
|
||||
|
||||
Reference in New Issue
Block a user