feat: lit/content-create with Divi short form

New input divi (1-30 sections, short form like lit-divi-insert) builds a Divi 5
layout in one divi/placeholder. The whole description is checked before the
insert (Divi's map for "no entry yet"); the draft is inserted with its normal
WordPress fields only, then DiviContentComposer composes again bound to the new
entry and stores it via DiviSaver::saveNew (same kses window as save(), strict
DiviFlags). A refusal or failure deletes the draft. Result adds tree_rev and the
outline.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 16:46:50 +02:00
co-authored by Claude Opus 5.5
parent 3efcfaabec
commit 5965e32acb
12 changed files with 861 additions and 23 deletions
@@ -212,4 +212,59 @@ final class DiviSaverTest extends TestCase
self::assertStringContainsString('Divi-Kennzeichen', $saved['warnings'][0]);
self::assertStringContainsString('divi builder flags failed', (string) file_get_contents($this->log));
}
public function testSaveNewWritesTheFirstContentWithoutKsesAndSetsTheFlagsStrictly(): void
{
Functions\when('current_user_can')->justReturn(false);
Functions\when('has_filter')->justReturn(10);
Functions\expect('wp_update_post')->once()->with(['ID' => 42, 'post_content' => 'NEU'], true)
->andReturnUsing(function (): int {
$this->calls[] = 'update';
return 42;
});
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 42, 'post_content' => 'NEU']));
Functions\expect('wp_save_post_revision')->never();
self::assertSame([], (new DiviSaver())->saveNew(42, 'NEU'));
self::assertSame(
// Same kses window as save(), but no restore point: the new draft has no earlier state.
[
'kses off', 'update', 'kses on',
'meta:_et_pb_use_divi_5=on', 'meta:_et_pb_use_builder=on', 'meta:_et_pb_show_page_creation=off',
],
$this->calls
);
}
public function testSaveNewReportsAContentChangedOnSaveAndKeepsKsesForUnfilteredUsers(): void
{
Functions\when('current_user_can')->justReturn(true);
Functions\when('has_filter')->justReturn(10);
Functions\when('wp_update_post')->justReturn(42);
Functions\when('get_post')->justReturn(new WP_Post(['ID' => 42, 'post_content' => 'ANDERS']));
self::assertSame([DiviSaver::CHANGED_ON_SAVE], (new DiviSaver())->saveNew(42, 'NEU'));
self::assertNotContains('kses off', $this->calls);
}
public function testSaveNewThrowsWhenTheUpdateIsRefusedOrAFlagIsMissing(): void
{
Functions\when('current_user_can')->justReturn(false);
Functions\when('has_filter')->justReturn(10);
Functions\when('wp_update_post')->justReturn(new WP_Error('db', 'kaputt'));
try {
(new DiviSaver())->saveNew(42, 'NEU');
self::fail('RuntimeException expected');
} catch (RuntimeException $e) {
self::assertStringContainsString('kaputt', $e->getMessage());
}
self::assertSame(['kses off', 'kses on'], $this->calls, 'kses comes back, no flags on a failed write');
Functions\when('wp_update_post')->justReturn(42);
Functions\when('get_post_meta')->justReturn('');
$this->expectException(RuntimeException::class);
(new DiviSaver())->saveNew(42, 'NEU');
}
}