lit/divi-catalog's path_kinds previously named one kind per whole attribute path, built from Divi's metadata component name alone. For a composite path (Divi groups several named sub-fields, "subName", under one path – e.g. buttonOne.innerContent holds "text" and "linkUrl") that was wrong: the whole path got one guessed kind (or none), even though the sub-fields can differ (fullwidth-header's buttonOne/buttonTwo.innerContent -> "text", though it also carries a "linkUrl"; image.innerContent -> "image", though the value also carries alt/id/titleText the metadata never names on its own). AttrPathIndex::kinds() now resolves a composite path to a map subName => kind instead, preferring Divi's features.dynamicContent.type over the (often shared/generic) field component for a named sub-field – this is what tells a button's plain text sub-field apart from its linkUrl sub-field, both of which use the same generic "divi/text" component. A plain (non- composite) path is unaffected. Any subName the metadata never names (or that disagrees between candidates) is therefore simply absent from the map, and ModuleCatalog::pathKind() defaults it, like every unresolved path, to "unknown" - the strictest routing for 2b's sanitizer, never a guess. ModuleCatalog::pathKind() gains an optional $subName parameter to read a composite path's map. docs/guides/divi-attrs.md documents the new shape and that lit-divi-catalog without `module` lists all modules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
159 lines
6.0 KiB
PHP
159 lines
6.0 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Integrations\Divi\Schema;
|
||
|
||
use LIT\MCP\Abilities\Ability;
|
||
use LIT\MCP\Abilities\Errors;
|
||
use LIT\MCP\Abilities\JsonMap;
|
||
use WP_Error;
|
||
|
||
/**
|
||
* lit/divi-catalog: the modules of this Divi installation with their short-form fields;
|
||
* with "module" the details of one module incl. all known attribute paths (spec §5.7).
|
||
*/
|
||
final class DiviCatalogAbility implements Ability
|
||
{
|
||
public const CODE_NOTE = 'Code-Modul (code) nur verwenden, wenn kein natives Modul passt – es ist im '
|
||
. 'Divi-Builder nicht visuell bearbeitbar.';
|
||
|
||
public function __construct(private DiviModules $modules)
|
||
{
|
||
}
|
||
|
||
public function name(): string
|
||
{
|
||
return 'lit/divi-catalog';
|
||
}
|
||
|
||
public function label(): string
|
||
{
|
||
return 'Divi-Modulkatalog';
|
||
}
|
||
|
||
public function description(): string
|
||
{
|
||
return 'Listet die Divi-Module dieser Website (Name, Titel, Art, erlaubte Kind-Module) und welche eine '
|
||
. 'Kurzform haben; ohne module alle Module. Mit module: Kurzform-Felder und alle gültigen '
|
||
. 'Attributpfade des Moduls für die Vollform, je Pfad nach Möglichkeit mit Feldart (path_kinds), bei '
|
||
. 'zusammengesetzten Pfaden als Zuordnung Unterfeld => Feldart.';
|
||
}
|
||
|
||
public function inputSchema(): array
|
||
{
|
||
return [
|
||
'type' => 'object',
|
||
'properties' => [
|
||
'module' => ['type' => 'string', 'description' => 'Modul, z. B. "button" oder "divi/button".'],
|
||
],
|
||
'additionalProperties' => false,
|
||
];
|
||
}
|
||
|
||
public function outputSchema(): ?array
|
||
{
|
||
return [
|
||
'type' => 'object',
|
||
'properties' => [
|
||
'modules' => [
|
||
'type' => 'array',
|
||
'items' => [
|
||
'type' => 'object',
|
||
'properties' => [
|
||
'name' => ['type' => 'string'],
|
||
'title' => ['type' => 'string'],
|
||
'category' => ['type' => 'string'],
|
||
'children' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||
'shortform' => ['type' => 'boolean'],
|
||
],
|
||
'required' => ['name', 'title', 'category', 'children', 'shortform'],
|
||
],
|
||
],
|
||
'module' => [
|
||
'type' => 'object',
|
||
'properties' => [
|
||
'name' => ['type' => 'string'],
|
||
'title' => ['type' => 'string'],
|
||
'category' => ['type' => 'string'],
|
||
'children' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||
'item_type' => ['type' => ['string', 'null']],
|
||
'shortform' => ['type' => 'object'],
|
||
'paths' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||
'path_kinds' => [
|
||
'type' => 'object',
|
||
'description' => 'Bekannter Attributpfad => Feldart (text, richtext, code, url, '
|
||
. 'image, color, enum, onoff, number, list, icon), soweit aus den Divi-Metadaten '
|
||
. 'ableitbar. Bei einem zusammengesetzten Pfad (mehrere benannte Unterfelder unter '
|
||
. 'einem Pfad, z. B. buttonOne.innerContent mit "text" und "linkUrl") ist der Wert '
|
||
. 'stattdessen eine Zuordnung Unterfeld => Feldart, soweit für die einzelnen '
|
||
. 'Unterfelder ableitbar. Fehlende Pfade oder Unterfelder gelten als unbekannt.',
|
||
],
|
||
],
|
||
'required' => ['name', 'title', 'category', 'children', 'shortform', 'paths', 'path_kinds'],
|
||
],
|
||
'notes' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||
],
|
||
'required' => ['notes'],
|
||
];
|
||
}
|
||
|
||
public function annotations(): array
|
||
{
|
||
return ['readonly' => true, 'destructive' => false, 'idempotent' => true];
|
||
}
|
||
|
||
public function isDirect(): bool
|
||
{
|
||
return true;
|
||
}
|
||
|
||
public function permission(array $input): bool|WP_Error
|
||
{
|
||
return current_user_can('edit_posts')
|
||
? true
|
||
: Errors::forbidden('Keine Berechtigung, Inhalte zu bearbeiten.');
|
||
}
|
||
|
||
public function execute(array $input): mixed
|
||
{
|
||
$catalog = $this->modules->catalog();
|
||
$notes = [self::CODE_NOTE, 'Details zur Vollform: lit-guide mit topic "divi-attrs".'];
|
||
|
||
if (! is_string($input['module'] ?? null) || $input['module'] === '') {
|
||
$modules = [];
|
||
foreach ($catalog->summary() as $module) {
|
||
$shortName = ModuleCatalog::shortName($module['name']);
|
||
$modules[] = $module + ['shortform' => ShortformMap::has($shortName)];
|
||
}
|
||
|
||
return ['modules' => $modules, 'notes' => $notes];
|
||
}
|
||
|
||
$name = ModuleCatalog::normalize($input['module']);
|
||
if (! $catalog->has($name)) {
|
||
return Errors::notFound(
|
||
sprintf('Modul "%s" gibt es auf dieser Website nicht.', $input['module']),
|
||
'Verfügbare Module mit lit-divi-catalog ohne module abrufen.'
|
||
);
|
||
}
|
||
|
||
$short = ModuleCatalog::shortName($name);
|
||
$item = ShortformMap::childType($short);
|
||
|
||
return [
|
||
'module' => [
|
||
'name' => $name,
|
||
'title' => $catalog->title($name),
|
||
'category' => $catalog->category($name),
|
||
'children' => $catalog->childrenOf($name),
|
||
'item_type' => $item,
|
||
'shortform' => JsonMap::of(ShortformMap::fields($short)),
|
||
'paths' => $catalog->paths($name),
|
||
'path_kinds' => JsonMap::of($catalog->kinds($name)),
|
||
],
|
||
'notes' => $notes,
|
||
];
|
||
}
|
||
}
|