Files
WP-LIT-MCP/src/Integrations/Divi/Pages/DiviDeleteAbility.php
T

127 lines
4.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\Divi\Pages;
use LIT\MCP\Abilities\Annotations;
use LIT\MCP\Abilities\Errors;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use WP_Error;
defined('ABSPATH') || exit;
/**
* 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).',
],
]),
'required' => ['post_id', 'tree_rev', 'ids'],
'additionalProperties' => false,
];
}
public function annotations(): array
{
return Annotations::destructive(false);
}
/** Like lit/divi-move: TreeEditor::remove() only, no composition of client values. */
protected function baseFeatures(): array
{
return [DiviFeature::PageRead, DiviFeature::PageWrite, DiviFeature::Catalog, DiviFeature::DesignRead];
}
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);
}
}