feat: {{link:ID}} placeholder via Divi's post_link_url dynamic content

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 00:32:59 +02:00
co-authored by Claude Opus 5.5
parent 404f0f34b9
commit 788943e809
11 changed files with 304 additions and 21 deletions
@@ -37,7 +37,14 @@ final class DiviCompositionTest extends TestCase
return DiviFixtures::dynamicContentRegistry();
});
return new DiviComposition($modules, $design, ComposeFixture::sanitizer(), ComposeFixture::media(), $registry);
return new DiviComposition(
$modules,
$design,
ComposeFixture::sanitizer(),
ComposeFixture::media(),
$registry,
ComposeFixture::links()
);
}
private static function siteTitle(): string
@@ -74,6 +81,24 @@ final class DiviCompositionTest extends TestCase
self::assertSame($composition->references(), $composition->references());
}
/** E13a: the composition hands its link targets to Placeholders; the result still passes ValueCleaner. */
public function testInternalLinksUseTheLinkTargets(): void
{
$composer = $this->composition()->forPost(7)->composer();
$column = Block::create('divi/column');
$page = $composer->compose([['type' => 'button', 'text' => 'Kontakt', 'url' => '{{link:839}}']], $column, 'nodes');
self::assertSame([], $page['errors']);
self::assertStringContainsString(
'post_link_url_page',
json_encode($page['blocks'][0]->attrs(), JSON_THROW_ON_ERROR)
);
$missing = $composer->compose([['type' => 'button', 'url' => '{{link:999}}']], $column, 'nodes');
self::assertStringContainsString('Eintrag 999', $missing['errors']['nodes[0].url']);
}
public function testWithoutAPostTheDefaultMapApplies(): void
{
$composition = $this->composition();
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
use Brain\Monkey\Functions;
use LIT\MCP\Integrations\Divi\Pages\WpLinkTargets;
use LIT\MCP\Tests\Unit\TestCase;
use WP_Post;
final class WpLinkTargetsTest extends TestCase
{
public function testOnlyReadableLivingPostsCanBeLinked(): void
{
$posts = [
839 => new WP_Post(['ID' => 839, 'post_type' => 'page', 'post_status' => 'draft']),
841 => new WP_Post(['ID' => 841, 'post_type' => 'page', 'post_status' => 'trash']),
842 => new WP_Post(['ID' => 842, 'post_type' => 'page', 'post_status' => 'private']),
843 => new WP_Post(['ID' => 843, 'post_type' => 'revision', 'post_status' => 'inherit']),
844 => new WP_Post(['ID' => 844, 'post_type' => 'page', 'post_status' => 'auto-draft']),
];
Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => $posts[$id] ?? null);
Functions\when('current_user_can')->alias(
static fn (string $cap, int $id): bool => $cap === 'read_post' && $id !== 842
);
$links = new WpLinkTargets();
self::assertSame('page', $links->postTypeOf(839));
self::assertNull($links->postTypeOf(841), 'trashed');
self::assertNull($links->postTypeOf(842), 'not readable');
self::assertNull($links->postTypeOf(843), 'revision');
self::assertNull($links->postTypeOf(844), 'auto-draft');
self::assertNull($links->postTypeOf(900), 'missing');
}
}