Files
WP-LIT-MCP/src/Integrations/Divi/Pages/DiviDuplicateNodeAbility.php
T
janniclas.loosenandClaude Opus 5.5 e04a5304f7 refactor: annotation presets (L5)
The 41 abilities return Annotations::read(), ::write(idempotent) or
::destructive(idempotent) instead of hand-written arrays (same arrays, same
key order).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 18:33:14 +02:00

82 lines
2.7 KiB
PHP

<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Pages;
use LIT\MCP\Abilities\Annotations;
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
use WP_Error;
/**
* lit/divi-duplicate-node: copies a node with everything inside right behind the original
* (spec §5.7). The copy's admin label gets " (Kopie)"; read-only nodes (global modules,
* foreign blocks) are copied unchanged, as a whole, byte-identical reference.
*/
final class DiviDuplicateNodeAbility extends DiviWriteAbility
{
public function name(): string
{
return 'lit/divi-duplicate-node';
}
public function label(): string
{
return 'Divi-Knoten duplizieren';
}
public function description(): string
{
return 'Dupliziert einen Divi-Knoten samt Inhalt direkt hinter das Original; das Admin-Label der Kopie endet '
. 'auf „(Kopie)“. tree_rev aus lit-divi-outline ist Pflicht.';
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => array_merge(DiviSchema::write(), [
'id' => ['type' => 'string', 'description' => 'Pfad-ID des zu duplizierenden Knotens.'],
]),
'required' => ['post_id', 'tree_rev', 'id'],
'additionalProperties' => false,
];
}
public function annotations(): array
{
return Annotations::write(false);
}
public function isDirect(): bool
{
return false;
}
protected function change(DiviPage $page, array $input, DiviComposition $composition): DiviChange|WP_Error
{
$id = is_string($input['id'] ?? null) ? $input['id'] : '';
$node = $page->index->find($id);
if ($node === null) {
return DiviErrors::unknownNode($id, $page->post);
}
$copy = $composition->editor()->duplicate($page->index, $node);
if ($copy->isDivi() && ! $copy->isOpaque()) {
// The raw stored desktop value (never NodeSummary::label()'s display-shortened
// text): a long or multi-line label must be copied verbatim, only " (Kopie)" added.
// Setting only the ".desktop.value" leaf keeps every other breakpoint untouched.
// No client input: the stored label plus a constant suffix, so no choke point (S39).
$raw = AttrMap::desktop($node->attrs(), 'module.meta.adminLabel');
$label = is_string($raw) ? $raw : '';
$copy->setAttrs(AttrMap::set(
$copy->attrs(),
'module.meta.adminLabel.desktop.value',
($label === '' ? $node->shortName() : $label) . ' (Kopie)'
));
}
return (new DiviChange(sprintf('Knoten %s dupliziert', $id)))->record($copy, 'duplicated');
}
}