fix: always-visible export menu + wiki history rev creation

Export menu: dropped the details/summary toggle so format options are
visible at all times; added flex layout with gap for breathing room.

Wiki history: the browser edit form path was calling node.edit() instead
of node.wiki_edit() so revisions were never recorded — /api/v1/nodes/.../revisions
returned empty after every edit. Also tightened the access gate to
can_wiki_edit so non-owner authenticated users can edit wiki-mode root
nodes through the UI (matching the gate already used by the edit-button
macro).
This commit is contained in:
russell@unturf.com 2026-04-25 06:14:27 -04:00
parent dded1622e8
commit 9e22c0d768
No known key found for this signature in database
3 changed files with 57 additions and 34 deletions

View file

@ -675,6 +675,24 @@ form.node-action {
font-size: 0.8em;
}
.export-menu {
font-size: 0.75em;
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 14px;
align-items: baseline;
}
.export-label {
color: var(--color-muted);
font-weight: bold;
}
.export-link {
color: var(--color-muted);
}
div.user-watching {
display: grid;
grid-template-columns: 1fr 1fr;

View file

@ -243,41 +243,37 @@
{% endmacro %}
{% macro export_menu(node) %}
<details class="export-menu">
<summary class="action link">export</summary>
<div class="export-options">
<a href="/api/v1/export/threads/{{ node.id }}.md" class="export-link">markdown</a>
<a href="/api/v1/export/threads/{{ node.id }}.html" class="export-link">html</a>
<a href="/api/v1/export/threads/{{ node.id }}.pdf" class="export-link">pdf</a>
<a href="/api/v1/export/threads/{{ node.id }}.epub" class="export-link">epub</a>
<a href="/api/v1/export/threads/{{ node.id }}.docx" class="export-link">docx</a>
<a href="/api/v1/export/threads/{{ node.id }}.rst" class="export-link">rst</a>
<a href="/api/v1/export/threads/{{ node.id }}.latex" class="export-link">latex</a>
<a href="/api/v1/export/threads/{{ node.id }}.odt" class="export-link">odt</a>
<a href="/api/v1/export/threads/{{ node.id }}.plain" class="export-link">plain text</a>
{% if request.namespace.wiki %}
<a href="/api/v1/nodes/{{ node.id }}/revisions" class="export-link" target="_blank">history (json)</a>
<a href="/{{ node.id }}/revisions" class="export-link">history (html)</a>
{% endif %}
</div>
</details>
<div class="export-menu">
<span class="export-label">export:</span>
<a href="/api/v1/export/threads/{{ node.id }}.md" class="export-link">markdown</a>
<a href="/api/v1/export/threads/{{ node.id }}.html" class="export-link">html</a>
<a href="/api/v1/export/threads/{{ node.id }}.pdf" class="export-link">pdf</a>
<a href="/api/v1/export/threads/{{ node.id }}.epub" class="export-link">epub</a>
<a href="/api/v1/export/threads/{{ node.id }}.docx" class="export-link">docx</a>
<a href="/api/v1/export/threads/{{ node.id }}.rst" class="export-link">rst</a>
<a href="/api/v1/export/threads/{{ node.id }}.latex" class="export-link">latex</a>
<a href="/api/v1/export/threads/{{ node.id }}.odt" class="export-link">odt</a>
<a href="/api/v1/export/threads/{{ node.id }}.plain" class="export-link">plain text</a>
{% if request.namespace.wiki %}
<a href="/api/v1/nodes/{{ node.id }}/revisions" class="export-link" target="_blank">history (json)</a>
<a href="/{{ node.id }}/revisions" class="export-link">history (html)</a>
{% endif %}
</div>
{% endmacro %}
{% macro export_node_menu(node) %}
<details class="export-menu">
<summary class="action link">export</summary>
<div class="export-options">
<a href="/api/v1/export/nodes/{{ node.id }}.md" class="export-link">markdown</a>
<a href="/api/v1/export/nodes/{{ node.id }}.html" class="export-link">html</a>
<a href="/api/v1/export/nodes/{{ node.id }}.pdf" class="export-link">pdf</a>
<a href="/api/v1/export/nodes/{{ node.id }}.epub" class="export-link">epub</a>
<a href="/api/v1/export/nodes/{{ node.id }}.docx" class="export-link">docx</a>
{% if request.namespace.wiki %}
<a href="/api/v1/nodes/{{ node.id }}/revisions" class="export-link" target="_blank">history (json)</a>
<a href="/{{ node.id }}/revisions" class="export-link">history (html)</a>
{% endif %}
</div>
</details>
<div class="export-menu">
<span class="export-label">export:</span>
<a href="/api/v1/export/nodes/{{ node.id }}.md" class="export-link">markdown</a>
<a href="/api/v1/export/nodes/{{ node.id }}.html" class="export-link">html</a>
<a href="/api/v1/export/nodes/{{ node.id }}.pdf" class="export-link">pdf</a>
<a href="/api/v1/export/nodes/{{ node.id }}.epub" class="export-link">epub</a>
<a href="/api/v1/export/nodes/{{ node.id }}.docx" class="export-link">docx</a>
{% if request.namespace.wiki %}
<a href="/api/v1/nodes/{{ node.id }}/revisions" class="export-link" target="_blank">history (json)</a>
<a href="/{{ node.id }}/revisions" class="export-link">history (html)</a>
{% endif %}
</div>
{% endmacro %}
{% macro wiki_actions(node) %}

View file

@ -20,7 +20,10 @@ def edit_node(request):
request.session.flash(("You must log in to edit your messages.", "error"))
return HTTPFound(get_referer_or_home(request))
if not request.namespace.can_alter_node(request.node, request.user):
# In wiki mode, any authenticated user can edit root nodes — match the
# `can_wiki_edit` gate used by the edit-button macro. Outside wiki mode
# this collapses back to owner/moderator only.
if not request.namespace.can_wiki_edit(request.node, request.user):
request.session.flash(("You do not own this message.", "error"))
return HTTPFound(get_referer_or_home(request))
@ -34,7 +37,13 @@ def edit_node(request):
if thread_data or thread_title:
if thread_title:
request.node.title = thread_title
request.node.edit(thread_data)
# In wiki-mode namespaces every edit gets a revision row so the history
# endpoint has something to show. The API wiki-edit endpoint already
# does this; the browser form path was silently dropping revisions.
if request.namespace.wiki:
request.node.wiki_edit(thread_data, user=request.user)
else:
request.node.edit(thread_data)
# set return_to URI.
return_to = get_node_route_uri(request, request.node.root, request.node.id)