'object', 'properties' => [ 'revision_id' => ['type' => 'integer', 'minimum' => 1], 'rev' => [ 'type' => 'string', 'description' => 'rev des Eintrags aus lit-content-get (optimistische Sperre).', ], 'confirm' => ['type' => 'boolean', 'description' => 'true nach Bestätigung durch den Nutzer.'], ], 'required' => ['revision_id', 'rev'], 'additionalProperties' => false, ]; } public function outputSchema(): ?array { return ToolResult::schema(); } public function annotations(): array { return ['readonly' => false, 'destructive' => true, 'idempotent' => true]; } public function isDirect(): bool { return false; } public function permission(array $input): bool|WP_Error { $pair = $this->load($input); return $pair instanceof WP_Error ? $pair : true; } public function execute(array $input): mixed { $pair = $this->load($input); if ($pair instanceof WP_Error) { return $pair; } [$revision, $post] = $pair; $postId = (int) $post->ID; if (($input['confirm'] ?? false) !== true) { return Errors::confirmRequired(sprintf( '"%s" (ID %d) auf den Stand vom %s zurücksetzen?', $post->post_title, $postId, $revision->post_date )); } if (! Rev::matches($input, $post)) { return Errors::revMismatch(); } // wp_restore_post_revision() saves via wp_update_post(), which would publish a missed schedule. $unstable = PostGuard::statusStable($post, 'revision_id'); if ($unstable !== null) { return $unstable; } $backupId = RevisionPoint::capture($postId); $restored = wp_restore_post_revision((int) $revision->ID); if (! $restored) { return Errors::writeFailed( 'WordPress konnte die Revision nicht wiederherstellen.', [], null, $backupId === null ? null : Errors::notSavedKeeping(sprintf('Revision %d', $backupId)) ); } // The restore is saved – failures from here on are logged and reported as warnings only. $change = (new PostChangeResult($this->name(), $this->log, $postId))->afterSave($input, 'Wiederherstellen'); $updated = $change->reread($post) ?? $post; $summary = sprintf('"%s" auf Stand vom %s zurückgesetzt', $updated->post_title, $revision->post_date); $postType = (string) $post->post_type; $change->log($postType, $summary, $backupId); return $change->result($summary . sprintf(' (Status unverändert: %s).', $updated->post_status), $updated) ->changed((string) $postId, $postType, 'restored', (string) $updated->post_title) ->revisionId($backupId) ->urls((string) get_preview_post_link($updated), (string) get_edit_post_link($postId, 'raw')) ->toArray(); } /** * @param array $input * @return array{0: WP_Post, 1: WP_Post}|WP_Error */ private function load(array $input): array|WP_Error { $revisionId = PostGuard::id($input['revision_id'] ?? null); $revision = $revisionId === null ? null : get_post($revisionId); if (! $revision instanceof WP_Post || $revision->post_type !== 'revision') { return Errors::notFound('Revision nicht gefunden.', 'Revisionen über lit/revisions-list abrufen.'); } // field 'revision_id': post_parent 0 (an orphaned revision) is the only id this // input carries, so a validation error must name revision_id, not post_id. $post = PostGuard::editable((int) $revision->post_parent, $this->schema, 'revision_id'); return $post instanceof WP_Error ? $post : [$revision, $post]; } }