diff --git a/src/Integrations/Divi/Pages/DiviWriter.php b/src/Integrations/Divi/Pages/DiviWriter.php index af1cad6..7608f35 100644 --- a/src/Integrations/Divi/Pages/DiviWriter.php +++ b/src/Integrations/Divi/Pages/DiviWriter.php @@ -127,7 +127,7 @@ final class DiviWriter $e->revisionId ); } - throw $e; + throw self::withRestorePoint($page, $e); } $outline = $this->outline($index, $change); @@ -177,6 +177,38 @@ final class DiviWriter return admin_url('revision.php?revision=' . $revisionId); } + /** + * Audit L3 on the failure path: for a library entry or a Theme Builder layout the error names the WordPress + * revision screen (restore_url) and says so in the hint of a partial write, instead of lit/revisions-list and + * lit/revision-restore, which answer "not found" for these types. Pages keep the default hint. + */ + private static function withRestorePoint(DiviPage $page, WriteFailed $e): WriteFailed + { + if (! $page->isLibraryItem() && ! $page->isThemeBuilderLayout()) { + return $e; + } + + $restore = self::restoreUrl($page, $e->revisionId); + $details = $restore === null ? [] : ['restore_url' => $restore]; + if ($e->rolledBack) { + return $details === [] ? $e : $e->withDetails($details); + } + + $back = $restore === null + ? '' + : sprintf( + ' Den vorherigen Stand (Revision %d) bei Bedarf im Revisionsvergleich von WordPress wiederherstellen ' + . '(restore_url).', + (int) $e->revisionId + ); + + return $e->withDetails( + $details, + 'Den Aufruf nicht einfach wiederholen. Zuerst den aktuellen Stand mit lit-divi-outline prüfen.' . $back + . ' Dem Nutzer Bescheid geben.' + ); + } + /** * save() for the Divi tools (E2): pages as before; a library entry keeps its shape, a global one * needs confirm (dry run first), gets a backup when revisions are off and clears every page's diff --git a/tests/Unit/Integrations/Divi/Library/GlobalEditsTest.php b/tests/Unit/Integrations/Divi/Library/GlobalEditsTest.php index b0e046e..911ff9e 100644 --- a/tests/Unit/Integrations/Divi/Library/GlobalEditsTest.php +++ b/tests/Unit/Integrations/Divi/Library/GlobalEditsTest.php @@ -438,6 +438,40 @@ final class GlobalEditsTest extends TestCase self::assertCount(1, $this->log->partials); } + /** + * Audit L3 on the failure path (re-review): lit/revision-restore answers not found for library entries, so a + * failed save names the WordPress revision screen instead. + */ + public function testAFailedSaveNamesTheRevisionScreenNotRevisionRestore(): void + { + LibraryFixtures::given([LibraryFixtures::item(1059)]); + $this->log = FakeChangeLog::partialsOnly(); + $cause = new \RuntimeException('hook'); + foreach ( + [ + WriteFailed::partial(DiviSaver::REASON, $cause, postId: 1059, revisionId: 811), + WriteFailed::rolledBack(DiviSaver::REASON, $cause, postId: 1059, revisionId: 811), + ] as $failure + ) { + $this->failure = $failure; + try { + (new DiviUpdateAbility($this->writer()))->execute(self::relabel(1059, true)); + self::fail('WriteFailed expected'); + } catch (WriteFailed $e) { + $error = $e->toError(); + } + + $data = $error->get_error_data(); + self::assertSame('https://kunde.test/wp-admin/revision.php?revision=811', $data['restore_url']); + self::assertStringNotContainsString('lit/revision-restore', $data['hint']); + self::assertStringNotContainsString('lit/revisions-list', $data['hint']); + if (! $failure->rolledBack) { + self::assertStringContainsString('restore_url', $data['hint']); + self::assertStringContainsString('Revision 811', $data['hint']); + } + } + } + /** Ruling P14 (M-2): one page is "Seite". */ public function testDeleteNoticeUsesTheSingularForOnePage(): void { diff --git a/tests/Unit/Integrations/Divi/Pages/DiviWriterThemeBuilderTest.php b/tests/Unit/Integrations/Divi/Pages/DiviWriterThemeBuilderTest.php index 7b134de..ce17c83 100644 --- a/tests/Unit/Integrations/Divi/Pages/DiviWriterThemeBuilderTest.php +++ b/tests/Unit/Integrations/Divi/Pages/DiviWriterThemeBuilderTest.php @@ -155,6 +155,23 @@ final class DiviWriterThemeBuilderTest extends TestCase self::assertSame([], $this->saved); } + /** Audit L3 on the failure path: a failed layout save names the WordPress revision screen. */ + public function testAFailedSaveNamesTheRevisionScreen(): void + { + $this->failures = [WriteFailed::rolledBack(DiviSaver::REASON, new \RuntimeException('hook'), postId: 1062, revisionId: 811)]; + $ability = new DiviInsertAbility($this->writer([ThemeBuilderFixtures::template()])); + + try { + $ability->execute($this->insert($this->body, [])); + self::fail('WriteFailed expected'); + } catch (WriteFailed $e) { + $error = $e->toError(); + } + + self::assertSame('https://kunde.test/wp-admin/revision.php?revision=811', $error->get_error_data()['restore_url']); + self::assertStringNotContainsString('lit/revision-restore', $error->get_error_data()['hint']); + } + /** Without a ThemeBuilderEdits (no Theme Builder wiring) a layout is never written. */ public function testWithoutThemeBuilderEditsLayoutsAreNotWritten(): void {