'object', 'properties' => [ 'post_id' => ['type' => 'integer', 'minimum' => 1], 'id' => ['type' => 'string', 'description' => 'Nur diesen Teilbaum, z. B. "s1".'], 'depth' => [ 'type' => 'integer', 'minimum' => 0, 'maximum' => 10, 'description' => 'Maximale Tiefe unterhalb des Startknotens (0 = nur der Knoten).', ], ], 'required' => ['post_id'], 'additionalProperties' => false, ]; } public function outputSchema(): ?array { return [ 'type' => 'object', 'properties' => [ 'post_id' => ['type' => 'integer'], 'title' => ['type' => 'string'], 'status' => ['type' => 'string'], 'tree_rev' => ['type' => 'string'], 'rev' => ['type' => 'string'], 'outline' => ['type' => 'string'], 'nodes' => [ 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'id' => ['type' => 'string'], 'type' => ['type' => 'string'], 'label' => ['type' => 'string'], 'excerpt' => ['type' => 'string'], 'info' => ['type' => 'string'], 'presets' => ['type' => 'array', 'items' => ['type' => 'string']], 'children' => ['type' => 'integer'], 'editable' => ['type' => 'boolean'], 'depth' => ['type' => 'integer'], ], 'required' => [ 'id', 'type', 'label', 'excerpt', 'info', 'presets', 'children', 'editable', 'depth', ], ], ], 'edit_url' => ['type' => 'string'], 'hints' => ['type' => 'array', 'items' => ['type' => 'string']], ], 'required' => ['post_id', 'tree_rev', 'rev', 'outline', 'nodes', 'hints'], ]; } 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 { $page = $this->loader->load($input['post_id'] ?? null, false); return $page instanceof WP_Error ? $page : true; } public function execute(array $input): mixed { $page = $this->loader->load($input['post_id'] ?? null, false); if ($page instanceof WP_Error) { return $page; } $rootId = isset($input['id']) && is_string($input['id']) && $input['id'] !== '' ? $input['id'] : null; if ($rootId !== null && preg_match(NodeIndex::ID_PATTERN, $rootId) !== 1) { return Errors::invalidInput('id ist keine Pfad-ID.', ['id' => 'z. B. "s0.r1.c0.m2"']); } if ($rootId !== null && $page->index->find($rootId) === null) { return DiviPageLoader::unknownNode($rootId, $page->post); } $outline = new Outline($this->design->read()->presetNames()); $rows = $outline->rows($page->index, $rootId, isset($input['depth']) ? (int) $input['depth'] : null); return [ 'post_id' => (int) $page->post->ID, 'title' => (string) $page->post->post_title, 'status' => (string) $page->post->post_status, 'tree_rev' => $page->treeRev(), 'rev' => Rev::ofPost($page->post), 'outline' => $outline->text($rows), 'nodes' => $rows, 'edit_url' => $page->editUrl(), 'hints' => $this->hints($page, $rows), ]; } /** * @param list> $rows * @return list */ private function hints(DiviPage $page, array $rows): array { $hints = []; if ($rows === []) { $hints[] = $page->isDiviLayout() ? 'Die Seite hat noch keinen Divi-Inhalt.' : 'Der Inhalt ist kein Divi-Layout (Block-Editor oder klassisch); lit-content-get zeigt ihn.'; } if (in_array(false, array_column($rows, 'editable'), true)) { $hints[] = 'Knoten mit "nur lesbar" (globale Module, fremde Blöcke) ändern die Divi-Werkzeuge nicht; ' . 'ihren Inhalt nur im Divi-Builder bearbeiten.'; } return $hints; } }