feat(post-form): U1 — shared create/edit form + published field + owner_username

One /post form now drives both create and edit (KTD1, Alt B): cache-on-save
toggles add-page-by-form's overwrite_mode per submit — 'edit' when the new hidden
edit_path field is filled (write back in place, stable URL), 'false' when empty
(fresh dated folder via slug_field). add-page-by-form stays stock (no fork);
the toggle lives in cache-on-save, which reads edit_path and mutates the /post
page header's pageconfig before add-page-by-form's onFormProcessed runs.

- post-form.md: overwrite_mode:false (create-safe fallback), hidden edit_path,
  authoritative published toggle (default on), removed the static
  pagefrontmatter.published so the field wins on every submit (KTD3).
- cache-on-save.php: editPathFromForm + setOverwriteMode helpers.
- site.yaml: owner_username (single source of truth for the KTD8 owner gate).

Verified against the 2.0.4 container: create writes a fresh dated folder with
published:true/false as a real boolean (V1, V2); edit writes in place with the
slug/URL unchanged; save-as-draft-on-create works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H1FrCYNq6RXdGYbn5PFrhM
This commit is contained in:
2026-07-04 23:31:25 +02:00
co-authored by Claude Opus 4.8
parent fff5358ce2
commit aa5d34717e
3 changed files with 79 additions and 1 deletions
+5
View File
@@ -7,3 +7,8 @@ metadata:
description: 'A travel blog by Mischa'
active_trip: /trips/us-canada-mex-2024
travelling: false
# Single source of truth for the site owner's account username. Backs both the
# front-end edit/delete UI gate and the server-side scope guards (KTD8): only
# this user (not merely any authenticated/super-admin account) may edit, delete,
# or see drafts on the active trip's feed.
owner_username: mischa
+34 -1
View File
@@ -6,13 +6,21 @@ access:
# Parent (write target) is injected server-side from site.active_trip by the
# cache-on-save plugin (onFormValidationProcessed) — no manual sync needed.
#
# overwrite_mode is toggled per submit by cache-on-save (KTD1, Alt B): edit when
# the hidden edit_path field is filled (write back in place), false when empty
# (create a fresh dated folder via slug_field). The static value here is the
# create-safe fallback for if cache-on-save doesn't run — add-page-by-form stays
# stock (no fork), so a static `edit` would break create (empty edit_path -> '.').
pageconfig:
slug_field: 'date,title'
overwrite_mode: false
# published is NOT a static pagefrontmatter default anymore — it is an
# authoritative form field (below) so every submit (create AND edit) writes the
# owner's chosen publish state. Only `template` stays static here.
pagefrontmatter:
template: entry
published: true
form:
name: new-entry
@@ -129,11 +137,36 @@ form:
'car': '🚗 Car'
'plane': '✈️ Plane'
# Hidden edit target. Empty on create (add-page-by-form falls through to
# slug_field and writes a fresh dated folder); on edit, post-form.js sets
# it to the entry's path so overwrite_mode:edit writes back in place.
-
name: edit_path
type: hidden
default: ''
# Advanced fields — collapsed behind "More options" (see U5).
# No hero_image field: journal entries render their hero from the first
# uploaded photo (entry-journal.html.twig uses entry.media.images|first),
# so an explicit hero filename was redundant. Stories still use hero_image
# but they aren't posted through this form.
-
name: published
label: Published
# Authoritative publish state (replaces the removed static
# pagefrontmatter.published). Default ON so new entries publish; the
# owner flips it OFF to save/keep a draft, or to unpublish on edit.
# validate.type:bool keeps it a real boolean in frontmatter (not '0').
type: toggle
classes: advanced-field
highlight: 1
default: 1
options:
1: 'Yes'
0: 'No'
validate:
type: bool
-
name: force_connect
label: Force connector line
+40
View File
@@ -48,6 +48,46 @@ class CacheOnSavePlugin extends Plugin
}
$form->setData('parent', $this->resolveDailiesParent($activeTrip));
// One shared /post form drives both create and edit (KTD1). add-page-by-form
// reads overwrite_mode from the /post page header's pageconfig (not form
// data), so we toggle it here per submit:
// - edit (edit_path present) -> overwrite_mode:edit, writes back in place
// - create (edit_path empty) -> overwrite_mode:false, so stock falls
// through to slug_field (date,title) and writes a fresh dated folder.
// This keeps add-page-by-form stock (no fork), since a static
// overwrite_mode:edit would break create (empty edit_path slugs to '.').
$editPath = $this->editPathFromForm($form);
$this->setOverwriteMode($editPath === '' ? 'false' : 'edit');
}
/**
* The hidden edit_path form field, trimmed. Empty string when creating a new
* entry; the entry's `<route>/entry.md` path when editing (set by post-form.js).
*/
private function editPathFromForm($form): string
{
$value = $form->value('edit_path');
return is_string($value) ? trim($value) : '';
}
/**
* Override add-page-by-form's overwrite_mode by mutating the current (/post)
* page header's pageconfig. add-page-by-form reads
* `$grav['page']->header()->pageconfig['overwrite_mode']` (add-page-by-form.php
* :385) from the same page singleton, and Page::header() returns a cached
* instance, so this write is visible when its onFormProcessed runs afterwards.
*/
private function setOverwriteMode(string $mode): void
{
$page = $this->grav['page'] ?? null;
if (!$page) {
return;
}
$header = $page->header();
$pageconfig = (isset($header->pageconfig) && is_array($header->pageconfig)) ? $header->pageconfig : [];
$pageconfig['overwrite_mode'] = $mode;
$header->pageconfig = $pageconfig;
}
/**