fix: Texte zu post_ids und Bearbeitungsrecht nennen forbidden für genannte Live-Seiten; Test für genannte blockierte Live-Seite; Smoke prüft Seiten-IDs numerisch

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-18 17:28:19 +02:00
co-authored by Claude Sonnet 5.5
parent 10bed73b24
commit 6b5de199f9
6 changed files with 40 additions and 9 deletions
+7 -3
View File
@@ -84,9 +84,13 @@ Einzelne Module bekommen Presets mit `lit-divi-insert`, `lit-divi-update` und `l
umgestellte Module ändern sich nicht noch einmal. Jede Seite bekommt ihre eigene WordPress-Revision; einen umgestellte Module ändern sich nicht noch einmal. Jede Seite bekommt ihre eigene WordPress-Revision; einen
Snapshot gibt es für Seiteninhalte nicht. Snapshot gibt es für Seiteninhalte nicht.
- Blockaden: Ohne `post_ids` verweigert der Aufruf alles und schreibt nichts: mit `forbidden` (und `blocked_ids`), - Blockaden: Ohne `post_ids` verweigert der Aufruf alles und schreibt nichts: mit `forbidden` (und `blocked_ids`),
wenn das Konto eine veröffentlichte Fundstelle nicht bearbeiten darf, mit `unsupported`, wenn es mehr als 2000 wenn das Konto eine veröffentlichte, private oder geplante Fundstelle nicht bearbeiten darf, mit `unsupported`,
veröffentlichte Fundstellen gibt. Mit `post_ids` können nur die genannten Seiten blockieren, die Obergrenze gilt nicht, und eine genannte wenn es mehr als 2000 solcher Fundstellen gibt.
Seite ohne Bearbeitungsrecht steht unter `skipped` (nicht `blocked_ids`). Scheitert das Speichern mitten im Lauf, - Mit `post_ids` blockieren nur die genannten Seiten, die Obergrenze von 2000 gilt nicht. Eine genannte
veröffentlichte, private oder geplante Seite ohne Bearbeitungsrecht verweigert den ganzen Aufruf (`forbidden`
mit `blocked_ids`, nichts wird geschrieben; der Probelauf listet sie unter `blocked_ids`). Eine genannte
Seite, die nicht live ist (Entwurf usw.) und nicht bearbeitet werden darf, steht unter `skipped`.
- Scheitert das Speichern mitten im Lauf,
meldet `partial_write` die bis dahin gespeicherten Seiten (`saved`); die übrigen mit einem neuen Probelauf meldet `partial_write` die bis dahin gespeicherten Seiten (`saved`); die übrigen mit einem neuen Probelauf
weiterführen. weiterführen.
- Globale Module und die Bibliothek: Eine globale Instanz (`[global <ID>]` in `lit-divi-outline`) wird auf der Seite - Globale Module und die Bibliothek: Eine globale Instanz (`[global <ID>]` in `lit-divi-outline`) wird auf der Seite
@@ -47,7 +47,7 @@ Ansätze vorschlagen → Entwurf abschnittsweise freigeben → Spec schreiben
- R6 Outline: group_presets je Zeile und in node-get; Fingerprint unverändert. - R6 Outline: group_presets je Zeile und in node-get; Fingerprint unverändert.
- R7 clear_overrides beim Einfügen wirkt nach set/attrs desselben Knotens; nicht-boolesch → Fehler. - R7 clear_overrides beim Einfügen wirkt nach set/attrs desselben Knotens; nicht-boolesch → Fehler.
- R8 Seiten-Umschreiber: globale Instanzen nie umgeschrieben (skipped; Bibliotheks-ID nur bei sichtbaren Bibliotheks-IDs, P13), Bibliothekseintrag ist eigene Fundstelle; nicht ladbare Seiten skipped; remaining zählt nur Seiten mit Änderungen. - R8 Seiten-Umschreiber: globale Instanzen nie umgeschrieben (skipped; Bibliotheks-ID nur bei sichtbaren Bibliotheks-IDs, P13), Bibliothekseintrag ist eigene Fundstelle; nicht ladbare Seiten skipped; remaining zählt nur Seiten mit Änderungen.
- P8a mit post_ids: nur die genannten Seiten können das Schreiben blockieren, HARD_CAP gilt nicht, eine genannte Seite ohne Bearbeitungsrecht steht in skipped. Ohne post_ids verweigert jede blockierte Live-Fundstelle oder zu viele Fundstellen den ganzen Aufruf, nichts wird geschrieben. - P8a mit post_ids: nur die genannten Seiten können das Schreiben blockieren, HARD_CAP gilt nicht (die Rechteprüfung der Live-Seiten entfällt dann vorab, loadWritable() prüft jede Seite einzeln und meldet skipped). Eine genannte veröffentlichte, private oder geplante Seite ohne Bearbeitungsrecht verweigert den ganzen Aufruf (forbidden, blocked_ids, nichts geschrieben; der Probelauf listet sie in blocked_ids); eine genannte Seite, die nicht live ist (Entwurf usw.) und nicht bearbeitet werden darf, steht in skipped. Ohne post_ids verweigert jede blockierte Live-Fundstelle oder zu viele Fundstellen den ganzen Aufruf, nichts wird geschrieben.
- R9 Kein unfiltered_html-Zwang: der Umschreiber ändert nur Preset-Verweise und entfernt lokale Werte, Speichern über DiviSaver. - R9 Kein unfiltered_html-Zwang: der Umschreiber ändert nur Preset-Verweise und entfernt lokale Werte, Speichern über DiviSaver.
- R10 detach nur Presets: Eingabe preset = Objekt {module|group, preset}; variable → unsupported (4c); ohne post_ids zuerst verschachtelte Verweise über DesignChange (Snapshot, design_rev), danach Seiten (≤ 50); scheitert die erste Seite danach, nennt der Fehler Snapshot und diff; Standard-Presets nur ausdrückliche Verweise. - R10 detach nur Presets: Eingabe preset = Objekt {module|group, preset}; variable → unsupported (4c); ohne post_ids zuerst verschachtelte Verweise über DesignChange (Snapshot, design_rev), danach Seiten (≤ 50); scheitert die erste Seite danach, nennt der Fehler Snapshot und diff; Standard-Presets nur ausdrückliche Verweise.
- lit/divi-presets-assign: preset: null und null in group_presets werden abgewiesen (Hinweis auf lit/divi-design-detach); ein leerer Probelauf ist ein normales „nichts zu tun“. - lit/divi-presets-assign: preset: null und null in group_presets werden abgewiesen (Hinweis auf lit/divi-design-detach); ein leerer Probelauf ist ein normales „nichts zu tun“.
@@ -66,8 +66,10 @@ final class DiviDesignDetachAbility implements Ability
. 'diese Seiten (Verweise in Presets bleiben dann). Zuerst Probelauf (pages; bei Verweisen in Presets diff ' . 'diese Seiten (Verweise in Presets bleiben dann). Zuerst Probelauf (pages; bei Verweisen in Presets diff '
. 'und design_rev), nach Rückfrage beim Nutzer confirm: true (mit design_rev, wenn der Probelauf eine ' . 'und design_rev), nach Rückfrage beim Nutzer confirm: true (mit design_rev, wenn der Probelauf eine '
. 'nennt); je Aufruf höchstens 50 Seiten, remaining nennt den Rest. Mit post_ids können nur die ' . 'nennt); je Aufruf höchstens 50 Seiten, remaining nennt den Rest. Mit post_ids können nur die '
. 'genannten Seiten das Schreiben blockieren, die Obergrenze für sehr viele Seiten gilt dann nicht, und ' . 'genannten Seiten das Schreiben blockieren und die Obergrenze für sehr viele Seiten gilt nicht; eine '
. 'eine genannte Seite ohne Bearbeitungsrecht steht in skipped. Danach kann lit/divi-presets-delete ' . 'genannte veröffentlichte, private oder geplante Seite ohne Bearbeitungsrecht verweigert den ganzen '
. 'Aufruf (forbidden, blocked_ids), eine genannte andere Seite ohne '
. 'Bearbeitungsrecht steht in skipped. Danach kann lit/divi-presets-delete '
. 'das Preset löschen. ' . self::VARIABLES; . 'das Preset löschen. ' . self::VARIABLES;
} }
@@ -58,8 +58,10 @@ final class DiviPresetsAssignAbility implements Ability
. 'only_with_preset; clear_overrides: true entfernt lokale Werte, die das Preset überdecken. Zuerst ' . 'only_with_preset; clear_overrides: true entfernt lokale Werte, die das Preset überdecken. Zuerst '
. 'Probelauf (betroffene Seiten und Module), nach Rückfrage beim Nutzer confirm: true; je Aufruf höchstens ' . 'Probelauf (betroffene Seiten und Module), nach Rückfrage beim Nutzer confirm: true; je Aufruf höchstens '
. '50 Seiten, jede mit Revision; remaining nennt den Rest. Mit post_ids können nur die genannten Seiten ' . '50 Seiten, jede mit Revision; remaining nennt den Rest. Mit post_ids können nur die genannten Seiten '
. 'das Schreiben blockieren, die Obergrenze für sehr viele Seiten gilt dann nicht, und eine genannte ' . 'das Schreiben blockieren und die Obergrenze für sehr viele Seiten gilt nicht; eine genannte '
. 'Seite ohne Bearbeitungsrecht steht in skipped. Verweise entfernen: lit/divi-design-detach.'; . 'veröffentlichte, private oder geplante Seite ohne Bearbeitungsrecht verweigert den ganzen Aufruf '
. '(forbidden, blocked_ids), eine genannte andere Seite ohne '
. 'Bearbeitungsrecht steht in skipped. Verweise entfernen: lit/divi-design-detach.';
} }
public function inputSchema(): array public function inputSchema(): array
@@ -120,7 +120,7 @@ tree_rev() { # $1 = post ID
# 1. Two draft pages with one button without preset each. # 1. Two draft pages with one button without preset each.
PAGE1=$(create_page "$PREFIX Seite 1") PAGE1=$(create_page "$PREFIX Seite 1")
PAGE2=$(create_page "$PREFIX Seite 2") PAGE2=$(create_page "$PREFIX Seite 2")
[ -n "$PAGE1" ] && [ -n "$PAGE2" ] || fail "pages not created" [[ $PAGE1 =~ ^[1-9][0-9]*$ && $PAGE2 =~ ^[1-9][0-9]*$ ]] || fail "pages not created"
for id in "$PAGE1" "$PAGE2"; do for id in "$PAGE1" "$PAGE2"; do
button_attrs "$id" | jq -e '(.modulePreset // null) == null and (.groupPreset // null) == null' >/dev/null \ button_attrs "$id" | jq -e '(.modulePreset // null) == null and (.groupPreset // null) == null' >/dev/null \
|| fail "page $id starts with a preset" || fail "page $id starts with a preset"
@@ -268,6 +268,29 @@ final class PageRewriterTest extends TestCase
self::assertSame([[101, SaveScope::Page]], $this->saved); self::assertSame([[101, SaveScope::Page]], $this->saved);
} }
/** A named live post the caller may not edit blocks the whole call (forbidden), also with post_ids. */
public function testNamedBlockedLivePageRefusesTheCallEvenWithPostIds(): void
{
$rewriter = $this->rewriter(
[
self::page(101, self::layout(self::BUTTON), ['post_status' => 'publish']),
self::page(102, self::layout(self::BUTTON), ['post_status' => 'publish']),
],
self::report(2, [102], 2)
);
$preview = $rewriter->preview(self::query(), self::assign(), [101, 102]);
self::assertSame([102], $preview['blocked_ids']);
$error = self::assertToolError(
$rewriter->apply(self::ABILITY, self::query(), self::assign(), [101, 102], self::SUMMARY),
'forbidden'
);
self::assertSame([102], $error['blocked_ids']);
self::assertSame([], $this->saved);
self::assertSame([], $this->log->records);
}
public function testTooManyLiveUsagesAreUnsupported(): void public function testTooManyLiveUsagesAreUnsupported(): void
{ {
$rewriter = $this->rewriter( $rewriter = $this->rewriter(