feat: field validation, term resolution, post guard and revision points
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\WordPress;
|
||||
|
||||
use LIT\MCP\Abilities\Errors;
|
||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||
use WP_Error;
|
||||
use WP_Post;
|
||||
|
||||
/**
|
||||
* Loads entries and post types for abilities, including the capability checks
|
||||
* of spec §4.5. Every error is ready to be returned from permission()/execute().
|
||||
* Input is not schema-validated yet when permission() runs – hence the defensive id().
|
||||
*/
|
||||
final class PostGuard
|
||||
{
|
||||
public static function editable(mixed $postId, ContentSchema $schema, string $field = 'post_id'): WP_Post|WP_Error
|
||||
{
|
||||
$id = self::id($postId);
|
||||
if ($id === null) {
|
||||
return Errors::invalidInput(
|
||||
sprintf('%s fehlt oder ist ungültig.', $field),
|
||||
[$field => 'positive Ganzzahl erwartet']
|
||||
);
|
||||
}
|
||||
|
||||
$post = get_post($id);
|
||||
if (! $post instanceof WP_Post || ! $schema->isManaged((string) $post->post_type)) {
|
||||
return Errors::notFound(sprintf('Eintrag %d nicht gefunden (oder kein unterstützter Inhaltstyp).', $id));
|
||||
}
|
||||
|
||||
if (! current_user_can('edit_post', $id)) {
|
||||
return Errors::forbidden(sprintf('Keine Berechtigung, Eintrag %d zu bearbeiten.', $id));
|
||||
}
|
||||
|
||||
return $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* The brief declares this `: object|WP_Error`, but `object` already covers every
|
||||
* class type, so PHP refuses `object` inside a union ("Type object cannot be
|
||||
* part of a union type" – fatal at compile time). WP_Error is itself an object,
|
||||
* so a plain `object` return type is accurate and still lets callers branch with
|
||||
* `is_wp_error()` / `instanceof WP_Error`, exactly like editable() above.
|
||||
*/
|
||||
public static function postType(mixed $postType, ContentSchema $schema, string $cap): object
|
||||
{
|
||||
$name = is_string($postType) ? $postType : '';
|
||||
$object = $name !== '' && $schema->isManaged($name) ? get_post_type_object($name) : null;
|
||||
if (! is_object($object)) {
|
||||
return Errors::notFound(
|
||||
sprintf('Inhaltstyp "%s" ist nicht verfügbar.', $name),
|
||||
'Verfügbare Inhaltstypen über lit-content-types abrufen.'
|
||||
);
|
||||
}
|
||||
|
||||
$capability = isset($object->cap->{$cap}) ? (string) $object->cap->{$cap} : $cap;
|
||||
if (! current_user_can($capability)) {
|
||||
return Errors::forbidden(sprintf('Keine Berechtigung für Inhaltstyp "%s".', $name));
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public static function id(mixed $value): ?int
|
||||
{
|
||||
if (is_int($value)) {
|
||||
return $value > 0 ? $value : null;
|
||||
}
|
||||
|
||||
if (is_string($value) && ctype_digit($value) && (int) $value > 0) {
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user