From bb5487082e989220db2c52debd514af52599c669 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Tue, 20 Oct 2026 00:13:44 +0200 Subject: [PATCH] =?UTF-8?q?fix(audit=20L4):=20Restore=20pr=C3=BCft=20die?= =?UTF-8?q?=20Fundstellen=20aller=20Diff-Zeilen=20(auch=20aktivierte=20und?= =?UTF-8?q?=20wieder=20angelegte=20IDs,=20Standard-Preset-Wechsel)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- .../Divi/Design/PresetRestorePlanner.php | 14 ++++++-------- .../Divi/Design/RestoreUsages.php | 9 ++------- .../Divi/Design/ColorRestorePlannerTest.php | 19 +++++++++++++++++++ .../Divi/Design/PresetRestorePlannerTest.php | 17 +++++++++++++++++ 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/Integrations/Divi/Design/PresetRestorePlanner.php b/src/Integrations/Divi/Design/PresetRestorePlanner.php index 339dff2..bab2331 100644 --- a/src/Integrations/Divi/Design/PresetRestorePlanner.php +++ b/src/Integrations/Divi/Design/PresetRestorePlanner.php @@ -70,7 +70,7 @@ final class PresetRestorePlanner implements RestorePlanner $after->data(), $diff, sprintf('Snapshot %s wiederherstellen – %s', $snapshotId, PresetDiff::summary($diff)), - $this->usagesOf($before, $diff), + $this->usagesOf($before, $after, $diff), ['Seiten, die Presets verwenden, die es im Snapshot nicht gibt, behalten den Verweis; das Modul fällt ' . 'dann auf das Standard-Preset zurück.'], true @@ -78,20 +78,18 @@ final class PresetRestorePlanner implements RestorePlanner } /** - * Audit L4: the pages using a preset the restore changes or removes (spec §4 step 4, ruling P8a); a default + * Audit L4: the pages using a preset the restore touches in any way, also re-created ones (spec §4 step 4, ruling P8a); a default * preset also counts its implicit users (like lit/divi-presets-delete). The orphan warning stays (spec §5). * * @param list> $diff */ - private function usagesOf(PresetBook $before, array $diff): ?UsageReport + private function usagesOf(PresetBook $before, PresetBook $after, array $diff): ?UsageReport { $targets = []; foreach ($diff as $row) { - if (in_array($row['action'], RestoreUsages::TOUCHING, true)) { - $scope = new PresetScope((string) $row['kind'], (string) $row['scope']); - $targets[] = ['scope' => $scope, 'id' => (string) $row['id'], - 'default' => $before->defaultId($scope) === $row['id']]; - } + $scope = new PresetScope((string) $row['kind'], (string) $row['scope']); + $targets[] = ['scope' => $scope, 'id' => (string) $row['id'], + 'default' => $before->defaultId($scope) === $row['id'] || $after->defaultId($scope) === $row['id']]; } return $targets === [] diff --git a/src/Integrations/Divi/Design/RestoreUsages.php b/src/Integrations/Divi/Design/RestoreUsages.php index b77605c..d872aac 100644 --- a/src/Integrations/Divi/Design/RestoreUsages.php +++ b/src/Integrations/Divi/Design/RestoreUsages.php @@ -11,14 +11,11 @@ defined('ABSPATH') || exit; /** * The usages a snapshot restore has to report (audit L4, spec §4 step 4): the pages that use an entry the restore - * changes, removes or deactivates – DesignChange then blocks the confirmed restore for pages the caller may not edit + * changes, removes, (de)activates or re-creates (an old ID that pages still reference) – DesignChange then blocks the confirmed restore for pages the caller may not edit * (ruling P8a) and the dry run lists them as "affected". */ final class RestoreUsages { - /** Diff actions that change what a page shows (colors: deactivated, variables: archived). */ - public const TOUCHING = ['changed', 'removed', 'deactivated', 'archived']; - /** * @param list> $diff Rows with "id" (gcid-/gvid-) and "action" */ @@ -26,9 +23,7 @@ final class RestoreUsages { $ids = []; foreach ($diff as $row) { - if (in_array($row['action'], self::TOUCHING, true)) { - $ids[] = (string) $row['id']; - } + $ids[] = (string) $row['id']; } return $ids === [] ? null : $usages->reportFor(new ReferenceUsageQuery(array_values(array_unique($ids)))); diff --git a/tests/Unit/Integrations/Divi/Design/ColorRestorePlannerTest.php b/tests/Unit/Integrations/Divi/Design/ColorRestorePlannerTest.php index b358d77..1dfd089 100644 --- a/tests/Unit/Integrations/Divi/Design/ColorRestorePlannerTest.php +++ b/tests/Unit/Integrations/Divi/Design/ColorRestorePlannerTest.php @@ -139,4 +139,23 @@ final class ColorRestorePlannerTest extends TestCase self::assertInstanceOf(DesignPlan::class, $plan); self::assertNull($plan->usages); } + + /** Audit L4 (re-review): a color the snapshot brings back (old ID, pages may still reference it) is looked up too. */ + public function testReportsThePagesUsingAColorTheSnapshotRecreates(): void + { + $usages = Mockery::mock(GlobalUsages::class); + $usages->expects('reportFor')->once()->with(Mockery::on( + static fn (mixed $query): bool => $query instanceof ReferenceUsageQuery && $query->matches('gcid-alt000001') + ))->andReturn(UsageFixture::blockedReport()); + + $plan = self::planner(true, $usages)->plan( + self::snapshot(), + self::snapshot() + ['gcid-alt000001' => self::color('Alt', '#123456')], + self::SNAPSHOT_ID + ); + + self::assertInstanceOf(DesignPlan::class, $plan); + self::assertSame(['added'], array_column($plan->diff, 'action')); + self::assertTrue($plan->usages?->blocks()); + } } diff --git a/tests/Unit/Integrations/Divi/Design/PresetRestorePlannerTest.php b/tests/Unit/Integrations/Divi/Design/PresetRestorePlannerTest.php index a2c3895..696f499 100644 --- a/tests/Unit/Integrations/Divi/Design/PresetRestorePlannerTest.php +++ b/tests/Unit/Integrations/Divi/Design/PresetRestorePlannerTest.php @@ -89,4 +89,21 @@ final class PresetRestorePlannerTest extends TestCase self::assertInstanceOf(DesignPlan::class, $plan); self::assertNull($plan->usages); } + + /** Audit L4 (re-review): a preset becoming the default reaches modules without an own preset – looked up as default. */ + public function testALookupCoversPresetsTheSnapshotMakesDefault(): void + { + $older = PresetBookTest::stored(); + $older['module']['divi/button']['default'] = 'aaaaaaaaaa'; + $usages = Mockery::mock(GlobalUsages::class); + $usages->expects('reportFor')->once()->with(Mockery::on( + static fn (mixed $query): bool => $query instanceof PresetUsageQuery + && $query->matches('') + ))->andReturn(UsageFixture::blockedReport()); + + $plan = self::planner(true, $usages)->plan(PresetBookTest::stored(), $older, 'presets-1'); + + self::assertInstanceOf(DesignPlan::class, $plan); + self::assertTrue($plan->usages?->blocks()); + } }