fix: never publish a missed schedule, refuse trashed posts, fail on meta read errors

- PostGuard::editable() refuses trashed entries unless the caller opts in
  (content-get, revisions-list, post-trash); publish/schedule/unpublish/update/
  restore/duplicate/replace now refuse them.
- PostGuard::statusStable(): content-update, revision-restore and
  replace-with-copy refuse a future post whose date has passed (any save would
  publish it); the content-update rollback and PostCloner row writes re-check.
- PostCloner::ownMeta() treats a failed SELECT as an error; replace() reads the
  copy before its first write.
- replace-with-copy needs create_posts (backup clone) and reports backup_id in
  write_failed/partial_write.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 01:20:21 +02:00
co-authored by Claude Opus 5.5
parent 6a7193424c
commit d831cf59ca
24 changed files with 501 additions and 43 deletions
+8 -3
View File
@@ -98,15 +98,20 @@ final class Errors
return self::make('unsupported', $message, $hint, 400);
}
/** A write failed and was rolled back completely. */
public static function writeFailed(string $reason): WP_Error
/**
* A write failed and was rolled back completely.
*
* @param array<string, mixed> $details Extra error details (e.g. a backup that stays).
*/
public static function writeFailed(string $reason, array $details = []): WP_Error
{
return self::make(
'write_failed',
trim('Speichern fehlgeschlagen: ' . $reason) . ' Nichts wurde gespeichert.',
'Eingaben prüfen, ggf. korrigieren und erneut versuchen; tritt der Fehler wieder auf, den Administrator '
. 'informieren (Details im PHP-Error-Log der Website).',
500
500,
$details
);
}