{% extends "admin/base.html" %} {% from "admin/_image_picker_field.html" import image_picker_field %} {% set is_edit = page is not none %} {# In working-copy mode `page` is a PageWorkingCopy, so `page.id` is the WC's id, NOT a live page id. `working_copy_page_id` is the live page id; use it for every live-page route (form action, backlinks, etc.). #} {% set is_wc = is_working_copy | default(false) %} {% block title %}{{ 'Edit' if is_edit else 'New' }} page · bragi admin{% endblock %} {% block content %}

{{ 'Edit page' if is_edit else 'New page' }}

{% if is_edit and not is_wc %}

Backlinks »

{% endif %} {% if is_wc %} {% endif %} {% if swap_pending %} {% endif %} {% if demotion_pending %} {% endif %}
{% if swap_pending %}{% endif %} {% if demotion_pending %}{% endif %} {# Hoist current_kind before the resume-fieldset guard below so the comparison doesn't evaluate against Jinja's Undefined and silently return False. #} {% set current_kind = form.get('kind', 'static') %}
{% with slug=form.get('slug', ''), full_path=slug_full_path, error=none, is_edit=is_edit, page_id=(working_copy_page_id if is_wc else (page.id if page else none)), site_slug=request.view_args['site_slug'] %} {% include "admin/_page_slug_field.html" %} {% endwith %}
Leave at "(root)" for a top-level page.
{% include "admin/_tiptap_editor.html" %}
{% if current_kind == 'resume' %} {% include "admin/_resume_fieldset.html" %} {% endif %} {# Blog-index settings: only meaningful once the page IS a post_index, so this renders on the persisted/submitted kind (same server-side gating as the resume fieldset above). #} {% if current_kind == 'post_index' %}
Post permalinks {% set current_permalink = form.get('permalink_style', 'flat') %} Date segments come from each post's publish date (UTC). Changing this does not redirect existing post URLs: old links 404 until relinked.
{% endif %} {# A working copy carries no status: the live row keeps its PUBLISHED status through promote. Hide the control entirely in WC mode so the save can't accidentally carry a status field. #} {% if not is_wc %}
{% endif %}
A site can have at most one post_index page. Promoting another page to post_index will demote the existing one back to static and you'll be asked to confirm before the change is saved.
Navigation {% if is_edit and not is_wc and site is defined and site.home_page_id == page.id %} {% endif %}
Lower numbers come first within this level; ties broken alphabetically by title.
{{ image_picker_field( name="featured_image_id", value=form.get("featured_image_id"), attachment=featured_image, thumb_storage_key=featured_image_thumb_key, label="Featured image", picker_url=url_for("attachment_admin.picker"), site_slug=request.view_args["site_slug"], helper="Optional. Used as og:image / twitter:image and as a card thumbnail. Falls back to the site's default featured image when unset." ) }} {# skip_redirect is meaningless for a working copy: a WC save fires no hooks and inserts no 301 (nothing is live). The recompute-from-title button references #skip_redirect, so render a hidden input in WC mode to keep that script's getElementById from null-dereferencing. #} {% if is_edit and not is_wc %}
{% elif is_wc %} {% endif %} {# On a PUBLISHED page, the live Save is relabelled so the operator sees that it publishes immediately; the prominent staging affordance sits in its own form below (HTML forbids nested forms). Drafts edit live as today, so the label stays the plain "Save". #} {% set is_published_live = is_edit and not is_wc and page.status == 'published' %}
{# On a published page, stage the CURRENT form content (the operator's unsaved edits) into a working copy. A second submit button on the SAME form with its own `formaction` carries the typed body/title/etc. to stage_page; a separate form (illegal to nest) would submit none of it. #} {% if is_published_live %} {% endif %} Cancel
{% if not is_edit %} {% endif %} {# Resume-source plugin widgets (LinkedIn upload, etc.) live OUTSIDE the page-edit form because they post to their own endpoints with their own
elements. HTML5 forbids nested s; the browser strips the inner open tag and the inner
prematurely closes the outer form, orphaning every input below it (status, kind, show_in_nav, ...). Saving in that state silently wipes the orphaned fields back to their defaults (kind -> static, status -> draft). Keep these contributions OUTSIDE the main form. #} {# Resume-source widgets (LinkedIn upload, etc.) post to their own endpoints that mutate the LIVE page's resume_data directly, so they don't belong on the working-copy editor (they'd bypass staging). Restricted to live edit. #} {% if is_edit and not is_wc and current_kind == 'resume' %} {% for partial_path in resume_admin_extras() %} {% include partial_path %} {% endfor %} {% endif %} {% endblock %}