Neu: Tier, Profile, Compatibility, CapabilityLocked, CapabilityGate, CompatibilityComponent, ProvidesCompatibility, DiviProfile sowie PodsFeature, PodsCompatibility und PodsIntegration. Das CapabilityGate sperrt gesperrte und unbekannte Fähigkeits-IDs (übernimmt die Sperre unbekannter IDs vom DiviFeatureGate) und delegiert nur Divi ausdrücklich. Pods-Schema, -Lesen, -Schreiben und -Feldrechte laufen über usable(PodsFeature::…); Schreiben verlangt FieldRead und FieldWrite, damit ein Rollback keine Werte verliert. lit-site-status meldet Pods über die CompatibilityComponent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.4 KiB
PHP
101 lines
3.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Integrations\Divi\Release;
|
||
|
||
use LIT\MCP\Abilities\Ability;
|
||
use LIT\MCP\Abilities\DeclaresRequirements;
|
||
use LIT\MCP\Integrations\Divi\Library\DiviLibraryStore;
|
||
use LIT\MCP\Integrations\Divi\ThemeBuilder\DiviThemeBuilder;
|
||
use LIT\MCP\Integrations\WordPress\PostGuard;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
/** Helpers of RequiresDiviFeatures::diviFeatures(): joining lists and the capabilities an entry adds. */
|
||
final class FeatureSets
|
||
{
|
||
/**
|
||
* The capabilities $ability declares for $input: its own diviFeatures() (the Divi tools) or the IDs a core tool
|
||
* names through DeclaresRequirements (lit/content-create with divi). Anything else declares none; an ID that is no
|
||
* DiviFeature is ignored (CapabilityGate checks the IDs of other integrations and locks unknown ones).
|
||
*
|
||
* @param array<string, mixed> $input
|
||
* @return list<DiviFeature>
|
||
*/
|
||
public static function declaredBy(Ability $ability, array $input): array
|
||
{
|
||
if ($ability instanceof RequiresDiviFeatures) {
|
||
return self::merge($ability->diviFeatures($input));
|
||
}
|
||
|
||
if (! $ability instanceof DeclaresRequirements) {
|
||
return [];
|
||
}
|
||
|
||
$features = [];
|
||
foreach ($ability->requirements($input) as $id) {
|
||
$feature = DiviFeature::tryFrom($id);
|
||
if ($feature !== null) {
|
||
$features[] = $feature;
|
||
}
|
||
}
|
||
|
||
return self::merge($features);
|
||
}
|
||
|
||
/**
|
||
* @param list<DiviFeature> ...$lists
|
||
* @return list<DiviFeature> Every capability once, in the order of first appearance.
|
||
*/
|
||
public static function merge(array ...$lists): array
|
||
{
|
||
$merged = [];
|
||
foreach ($lists as $list) {
|
||
foreach ($list as $feature) {
|
||
$merged[$feature->value] ??= $feature;
|
||
}
|
||
}
|
||
|
||
return array_values($merged);
|
||
}
|
||
|
||
/**
|
||
* What the entry $postId adds to a tool that loads it. A page or post adds nothing; a library entry needs the
|
||
* Divi roles (LibraryAccess), written also the library check of global entries and the library save scope
|
||
* (DiviSaver, D5); a Theme Builder layout needs the roles, written also the template lookup of
|
||
* ThemeBuilderEdits and the Theme Builder save scope. An ID that names no entry, or one the caller may not edit,
|
||
* adds nothing – the tool answers "not found" or "forbidden" itself, and a lock never tells which kind of entry
|
||
* an ID is (ruling P12).
|
||
*
|
||
* @return list<DiviFeature>
|
||
*/
|
||
public static function ofEntry(mixed $postId, bool $write): array
|
||
{
|
||
$id = PostGuard::id($postId);
|
||
$type = $id === null ? false : get_post_type($id);
|
||
if ($id === null || ! is_string($type)) {
|
||
return [];
|
||
}
|
||
|
||
$isLibrary = $type === DiviLibraryStore::POST_TYPE;
|
||
if (! $isLibrary && DiviThemeBuilder::areaOf($type) === null) {
|
||
return [];
|
||
}
|
||
|
||
if (! current_user_can('edit_post', $id)) {
|
||
return [];
|
||
}
|
||
|
||
if ($isLibrary) {
|
||
return $write
|
||
? [DiviFeature::Roles, DiviFeature::LibraryRead, DiviFeature::LibraryWrite]
|
||
: [DiviFeature::Roles];
|
||
}
|
||
|
||
return $write
|
||
? [DiviFeature::Roles, DiviFeature::ThemeBuilderRead, DiviFeature::ThemeBuilderWrite]
|
||
: [DiviFeature::Roles];
|
||
}
|
||
}
|