feat: Divi read tools accept library entries (library access, E1/E3)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Library;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Abilities\Errors;
|
||||
use LIT\MCP\Integrations\WordPress\PostGuard;
|
||||
use WP_Error;
|
||||
use WP_Post;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Rights on Divi Library entries (spec §4.5, E3 – never laxer than Divi): the library at all needs
|
||||
* edit_posts and the Divi role option divi_library; one entry additionally edit_post on it (Divi's
|
||||
* builder routes do not check that, F9). The Divi tools reach library entries only through
|
||||
* item(); every other ID goes on to PostGuard unchanged.
|
||||
* Not final so abilities can be tested with a mock.
|
||||
*/
|
||||
class LibraryAccess
|
||||
{
|
||||
/** Tool group "Divi-Bibliothek" (Tools tab); its switches gate library entries (E9, ruling P9). */
|
||||
public const GROUP = 'divi-library';
|
||||
|
||||
/**
|
||||
* @param (Closure(): bool)|null $writable Whether the Tools tab lets this connection write to the
|
||||
* library (group "Divi-Bibliothek" not read-only or disabled); null = always.
|
||||
* @param (Closure(): bool)|null $visible Whether library entries exist for this connection at all
|
||||
* (group "Divi-Bibliothek" not disabled, ruling P9); null = always.
|
||||
*/
|
||||
public function __construct(
|
||||
private DiviLibraryStore $store,
|
||||
private DiviRoles $roles,
|
||||
private ?Closure $writable = null,
|
||||
private ?Closure $visible = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function store(): DiviLibraryStore
|
||||
{
|
||||
return $this->store;
|
||||
}
|
||||
|
||||
public function roles(): DiviRoles
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The library entry $postId after the checks for reading, or their refusal; null when $postId
|
||||
* names no library entry (the caller goes on with PostGuard). With the library hidden (ruling
|
||||
* P9), an entry is refused exactly like an ID that does not exist – before any rights check.
|
||||
*/
|
||||
public function item(mixed $postId, bool $forWrite): WP_Post|WP_Error|null
|
||||
{
|
||||
$id = PostGuard::id($postId);
|
||||
$post = $id === null ? null : get_post($id);
|
||||
if (! $post instanceof WP_Post || $post->post_type !== DiviLibraryStore::POST_TYPE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->visible !== null && ! ($this->visible)()) {
|
||||
return PostGuard::notFound((int) $post->ID);
|
||||
}
|
||||
|
||||
return $this->canRead($post) ?? $post;
|
||||
}
|
||||
|
||||
/** Listing the library: edit_posts and the Divi role option divi_library. */
|
||||
public function canList(): ?WP_Error
|
||||
{
|
||||
if (! current_user_can('edit_posts')) {
|
||||
return Errors::forbidden('Keine Berechtigung, die Divi-Bibliothek zu nutzen.');
|
||||
}
|
||||
|
||||
return $this->roles->refusal('divi_library');
|
||||
}
|
||||
|
||||
/** Reading one entry: canList() and edit_post on the entry. */
|
||||
public function canRead(WP_Post $item): ?WP_Error
|
||||
{
|
||||
$refusal = $this->canList();
|
||||
if ($refusal !== null) {
|
||||
return $refusal;
|
||||
}
|
||||
|
||||
return current_user_can('edit_post', (int) $item->ID)
|
||||
? null
|
||||
: Errors::forbidden(sprintf('Keine Berechtigung, Bibliothekseintrag %d zu bearbeiten.', (int) $item->ID));
|
||||
}
|
||||
|
||||
public function isGlobal(WP_Post $item): bool
|
||||
{
|
||||
return $this->store->isGlobal((int) $item->ID);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user