Files
WP-LIT-MCP/src/Integrations/Divi/Pages/DiviDeleteAbility.php
T
janniclas.loosenandClaude Opus 5.5 d2cc82f31b refactor: small Divi tree helpers out of DiviWriter (M3)
Block::isPlaceholder replaces the divi/placeholder literals (ledger T4);
NodeIndex::parentBlock/sectionLabel replace the two parentBlock copies and
DiviWriter::sectionLabel; DiviChange::idOf and Outline::summary replace the
duplicated id resolution and outline text; DiviSchema holds the shared
input properties. Behaviour unchanged.

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

118 lines
3.9 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\Divi\Pages;
use LIT\MCP\Abilities\Errors;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
use WP_Error;
/**
* lit/divi-delete: removes nodes with everything inside (spec §5.7, destructive). Needs
* confirm: true after asking the user; without it the answer lists what would be removed –
* nodes inside another node that is deleted anyway do not count twice. The revision in the
* result allows undoing it (lit/revision-restore).
*/
final class DiviDeleteAbility extends DiviWriteAbility
{
public function name(): string
{
return 'lit/divi-delete';
}
public function label(): string
{
return 'Divi-Knoten löschen';
}
public function description(): string
{
return 'Entfernt Divi-Knoten samt Inhalt. Erst nach Rückfrage beim Nutzer mit confirm: true; ohne confirm '
. 'nennt die Antwort, was entfernt würde. Rückgängig über die Revision in revision_id. tree_rev ist '
. 'Pflicht.';
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => array_merge(DiviSchema::write(), [
'ids' => [
'type' => 'array',
'minItems' => 1,
'maxItems' => 20,
'items' => ['type' => 'string'],
'description' => 'Pfad-IDs der zu löschenden Knoten (aus lit-divi-outline).',
],
'confirm' => ['type' => 'boolean', 'description' => 'true erst nach Rückfrage beim Nutzer.'],
]),
'required' => ['post_id', 'tree_rev', 'ids'],
'additionalProperties' => false,
];
}
public function annotations(): array
{
return ['readonly' => false, 'destructive' => true, 'idempotent' => false];
}
protected function change(DiviPage $page, array $input, DiviComposition $composition): DiviChange|WP_Error
{
/** @var array<string, Block> $nodes */
$nodes = [];
foreach (is_array($input['ids'] ?? null) ? $input['ids'] : [] as $id) {
$node = is_string($id) ? $page->index->find($id) : null;
if ($node === null) {
return DiviErrors::unknownNode((string) $id, $page->post);
}
$nodes[$id] = $node;
}
// A node inside another node that is deleted anyway needs no own removal.
$nodes = array_filter(
$nodes,
static fn (Block $node, string $id): bool => array_filter(
array_keys($nodes),
static fn (string $other): bool => $other !== $id && str_starts_with($id, $other . '.')
) === [],
ARRAY_FILTER_USE_BOTH
);
if (($input['confirm'] ?? false) !== true) {
return Errors::confirmRequired(
sprintf('%d Divi-Knoten würden samt Inhalt entfernt: %s.', count($nodes), $this->describe($nodes)),
['nodes' => array_keys($nodes)]
);
}
$editor = $composition->editor();
$change = new DiviChange(sprintf('%d Divi-Knoten gelöscht', count($nodes)));
foreach ($nodes as $id => $node) {
$change->removed((string) $id, $node);
$editor->remove($page->index, $node);
}
return $change;
}
/** @param array<string, Block> $nodes */
private function describe(array $nodes): string
{
$parts = [];
foreach ($nodes as $id => $node) {
$label = NodeSummary::label($node);
$parts[] = sprintf(
'%s %s%s (%d Kind-Knoten)',
$id,
$node->shortName(),
$label === '' ? '' : ' "' . $label . '"',
count($node->children())
);
}
return implode(', ', $parts);
}
}