From d5fd1642b8a86759693d8f2978a94669def4e406 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Fri, 16 Oct 2026 18:10:47 +0200 Subject: [PATCH] fix: PresetComposer liest nur den von der Ability bereinigten Namen Co-Authored-By: Claude Opus 5.5 --- .../Divi/Design/DiviPresetsSetAbility.php | 5 +++-- .../Divi/Design/PresetComposer.php | 11 ++++++----- .../Divi/Design/DiviPresetsSetAbilityTest.php | 15 +++++++++++++++ .../Divi/Design/PresetComposerTest.php | 18 +++++++++++++++++- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Integrations/Divi/Design/DiviPresetsSetAbility.php b/src/Integrations/Divi/Design/DiviPresetsSetAbility.php index 0b36821..548a4eb 100644 --- a/src/Integrations/Divi/Design/DiviPresetsSetAbility.php +++ b/src/Integrations/Divi/Design/DiviPresetsSetAbility.php @@ -222,7 +222,7 @@ final class DiviPresetsSetAbility implements Ability /** * Scope, preset to change and source node of one item; null after noting the problem in $errors. * - * @param array $item Names are replaced by their cleaned form. + * @param array $item name/rename become the cleaned "name" PresetComposer reads (M-5). * @param array $errors * @return array{PresetScope, array|null, Block|null}|null */ @@ -274,7 +274,8 @@ final class DiviPresetsSetAbility implements Ability return null; } - $item[$key] = $name; + unset($item['rename']); + $item['name'] = $name; $taken = array_diff($book->idsNamed($scope, $name), [(string) ($existing['id'] ?? '')]); if ($taken !== []) { $errors[$field . '.' . $key] = sprintf( diff --git a/src/Integrations/Divi/Design/PresetComposer.php b/src/Integrations/Divi/Design/PresetComposer.php index c5952fa..8e10db0 100644 --- a/src/Integrations/Divi/Design/PresetComposer.php +++ b/src/Integrations/Divi/Design/PresetComposer.php @@ -43,7 +43,8 @@ final class PresetComposer /** * @param array|null $existing The preset to change, null for a new one. - * @param array $input One item of lit/divi-presets-set. + * @param array $input One item of lit/divi-presets-set; "name" is the resolved, cleaned + * name (new or renamed), "rename" is not read. * @return array{item: array, errors: array, invalid_paths: list} */ public function compose( @@ -238,16 +239,16 @@ final class PresetComposer } /** + * The name the ability resolved and cleaned ("name", also for a rename), else the existing preset's name. + * * @param array|null $existing * @param array $input * @param array $errors */ private function nameOf(?array $existing, array $input, string $field, array &$errors): string { - foreach (['rename', 'name'] as $key) { - if (is_string($input[$key] ?? null) && trim($input[$key]) !== '') { - return trim($input[$key]); - } + if (is_string($input['name'] ?? null) && $input['name'] !== '') { + return $input['name']; } if (is_string($existing['name'] ?? null)) { return $existing['name']; diff --git a/tests/Unit/Integrations/Divi/Design/DiviPresetsSetAbilityTest.php b/tests/Unit/Integrations/Divi/Design/DiviPresetsSetAbilityTest.php index 958225c..23290fc 100644 --- a/tests/Unit/Integrations/Divi/Design/DiviPresetsSetAbilityTest.php +++ b/tests/Unit/Integrations/Divi/Design/DiviPresetsSetAbilityTest.php @@ -240,6 +240,21 @@ final class DiviPresetsSetAbilityTest extends TestCase self::assertStringContainsString('1–100 Zeichen', $empty['fields']['items[0].rename']); } + /** Final review M-5: rename is cleaned by the ability and reaches the composer as the resolved name. */ + public function testRenameIsCleanedAndApplied(): void + { + $ability = $this->ability(); + $input = ['items' => [['module' => 'button', 'preset' => 'CTA primär', 'rename' => 'CTA neu']]]; + $dry = self::assertToolError($ability->execute($input), 'confirm_required'); + self::assertSame(['changed'], array_column($dry['diff'], 'action')); + + $this->snapshots->expects('save')->andReturn('presets-1'); + $this->snapshots->expects('attachLog'); + self::assertTrue($ability->execute($input + ['confirm' => true, 'design_rev' => $dry['design_rev']])['ok']); + $items = PresetBook::fromDivi($this->stored)->items(new PresetScope('module', 'divi/button')); + self::assertSame('CTA neu', $items['aaaaaaaaaa']['name']); + } + public function testNameAndRenameAreExclusive(): void { $ability = $this->ability(); diff --git a/tests/Unit/Integrations/Divi/Design/PresetComposerTest.php b/tests/Unit/Integrations/Divi/Design/PresetComposerTest.php index 795d378..c85c3de 100644 --- a/tests/Unit/Integrations/Divi/Design/PresetComposerTest.php +++ b/tests/Unit/Integrations/Divi/Design/PresetComposerTest.php @@ -95,7 +95,7 @@ final class PresetComposerTest extends TestCase $existing['attrs'] = ['button' => ['decoration' => ['border' => ['desktop' => ['value' => ['radius' => '4px']]]]]]; $result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), $existing, [ - 'rename' => 'CTA Primär', + 'name' => 'CTA Primär', 'attrs' => ['button.decoration.background' => ['desktop' => ['color' => '#000000']]], ], 'items[0]', null); @@ -105,6 +105,22 @@ final class PresetComposerTest extends TestCase self::assertSame('4px', $result['item']['attrs']['button']['decoration']['border']['desktop']['value']['radius']); } + /** Final review M-5: the ability resolves name/rename; the composer only takes "name" as given. */ + public function testOnlyTheResolvedNameCounts(): void + { + $existing = self::book()->items(new PresetScope('module', 'divi/button'))['aaaaaaaaaa']; + + $kept = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), $existing, [ + 'rename' => 'Ignoriert', + ], 'items[0]', null); + $missing = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [ + 'rename' => 'Ignoriert', + ], 'items[0]', null); + + self::assertSame('CTA primär', $kept['item']['name']); + self::assertSame('Name des neuen Presets fehlt', $missing['errors']['items[0].name']); + } + public function testFromNodeTakesTheDesignButNotTheContent(): void { $node = Block::create('divi/button', [