Files
WP-LIT-MCP/src/Integrations/WordPress/Render/PageRenderSummaryAbility.php
T

140 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\WordPress\Render;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Abilities\Annotations;
use LIT\MCP\Integrations\WordPress\PostGuard;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
use WP_Error;
defined('ABSPATH') || exit;
/**
* lit/page-render-summary: what a visitor sees in the content area – headings, texts, links,
* images with alt text and obvious problems (spec §5.7, §8.1). Works for every managed entry,
* Divi or not; the content is rendered in-process, header/footer/Theme Builder are not included.
*/
final class PageRenderSummaryAbility implements Ability
{
/** At most this many global-module notes per summary. */
private const GLOBAL_NOTES = 20;
public function __construct(private ContentSchema $schema, private PageRenderer $renderer)
{
}
public function name(): string
{
return 'lit/page-render-summary';
}
public function label(): string
{
return 'Gerenderte Seite (Auszug)';
}
public function description(): string
{
return 'Rendert den Inhaltsbereich eines Eintrags wie im Frontend und liefert einen strukturierten Auszug: '
. 'Überschriften, Texte, Links, Bilder mit Alt-Text und Warnungen (Hierarchie-Sprünge, fehlende '
. 'Alt-Texte). Header, Footer und Theme-Builder-Vorlagen sind nicht enthalten. Das Rendern führt die '
. 'üblichen Inhalts-Filter aus (Shortcodes, Divi, andere Plugins) und kann dabei Netzwerkanfragen '
. 'auslösen, z. B. oEmbed-Abrufe, die von WordPress oder anderen Plugins zwischengespeichert werden. '
. 'Gut zum Prüfen nach Änderungen. Globale Module (Divi-Bibliothek) fehlen im Auszug und werden '
. 'unter notes genannt.';
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => ['post_id' => ['type' => 'integer', 'minimum' => 1]],
'required' => ['post_id'],
'additionalProperties' => false,
];
}
public function outputSchema(): ?array
{
return [
'type' => 'object',
'properties' => [
'post_id' => ['type' => 'integer'],
'url' => ['type' => 'string'],
'headings' => ['type' => 'array', 'items' => ['type' => 'object']],
'texts' => ['type' => 'array', 'items' => ['type' => 'string']],
'links' => ['type' => 'array', 'items' => ['type' => 'object']],
'images' => ['type' => 'array', 'items' => ['type' => 'object']],
'warnings' => ['type' => 'array', 'items' => ['type' => 'string']],
'notes' => ['type' => 'array', 'items' => ['type' => 'string']],
],
'required' => ['post_id', 'headings', 'texts', 'links', 'images', 'warnings', 'notes'],
];
}
public function annotations(): array
{
return Annotations::read();
}
public function isDirect(): bool
{
return true;
}
public function permission(array $input): bool|WP_Error
{
$post = PostGuard::editable($input['post_id'] ?? null, $this->schema, 'post_id', true);
return $post instanceof WP_Error ? $post : true;
}
public function execute(array $input): mixed
{
$post = PostGuard::editable($input['post_id'] ?? null, $this->schema, 'post_id', true);
if ($post instanceof WP_Error) {
return $post;
}
$notes = [
'Nur der Inhaltsbereich; Header, Footer und Theme-Builder-Vorlagen fehlen.',
'Das Rendern führt Plugin-/Theme-Filter aus (Shortcodes, oEmbed u. Ä.) und kann dabei '
. 'Netzwerkanfragen auslösen, die von WordPress oder anderen Plugins zwischengespeichert werden.',
];
if ((string) $post->post_password !== '') {
$notes[] = 'Der Eintrag ist passwortgeschützt; Besucher sehen zuerst das Passwortformular.';
}
foreach (array_slice(self::globalModuleIds((string) $post->post_content), 0, self::GLOBAL_NOTES) as $id) {
$notes[] = sprintf(
'Globales Modul %s ist im Auszug nicht enthalten (beim Aufruf über die Schnittstelle rendert Divi '
. 'globale Module nicht); Inhalt mit lit-divi-outline mit post_id %s lesen.',
$id,
$id
);
}
return array_merge(
['post_id' => (int) $post->ID, 'url' => (string) get_permalink($post)],
RenderSummary::fromHtml($this->renderer->render($post)),
['notes' => $notes]
);
}
/**
* IDs of the global modules in $content, found the way Divi finds them (a string scan for
* "globalModule", BlockParserStore::get_global_layout_content – parse_blocks() is unreliable there).
*
* @return list<string>
*/
private static function globalModuleIds(string $content): array
{
preg_match_all('/"globalModule":"?(\d+)"?/', $content, $matches);
return array_values(array_unique($matches[1]));
}
}