feat(compat): Profile, Gates und Pods über den Kompatibilitätskern
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b511ef6348
commit
c85131603b
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Core;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Access;
|
||||
use LIT\MCP\Guides\GuideRepository;
|
||||
use LIT\MCP\Integrations\Integration;
|
||||
@@ -12,8 +13,11 @@ defined('ABSPATH') || exit;
|
||||
|
||||
final class CoreIntegration implements Integration
|
||||
{
|
||||
/** @param list<EnvironmentComponent> $components What lit-site-status reports besides Pods and Events Calendar. */
|
||||
public function __construct(private string $guidesDirectory, private array $components = [])
|
||||
/**
|
||||
* @param (Closure(): list<EnvironmentComponent>)|null $components What lit-site-status reports besides the Events
|
||||
* Calendar; asked when the status is built.
|
||||
*/
|
||||
public function __construct(private string $guidesDirectory, private ?Closure $components = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Core;
|
||||
|
||||
use Closure;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Detects the themes/plugins LIT MCP integrates with: the components it is given (Divi, wired in Plugin) first, then
|
||||
* Pods and The Events Calendar.
|
||||
* Detects the themes/plugins LIT MCP integrates with: the components it is given (Divi, Pods; wired in Plugin) first,
|
||||
* then The Events Calendar.
|
||||
*/
|
||||
class EnvironmentProbe
|
||||
{
|
||||
/** @param list<EnvironmentComponent> $components Components of integrations, reported in this order. */
|
||||
public function __construct(private array $components = [])
|
||||
/**
|
||||
* @param (Closure(): list<EnvironmentComponent>)|null $components Components of integrations, reported in this
|
||||
* order; asked per call (the registry that knows them exists later).
|
||||
*/
|
||||
public function __construct(private ?Closure $components = null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,18 +31,11 @@ class EnvironmentProbe
|
||||
$tecActive = class_exists('Tribe__Events__Main');
|
||||
$described = array_map(
|
||||
static fn (EnvironmentComponent $component): array => $component->describe(),
|
||||
$this->components
|
||||
$this->components === null ? [] : ($this->components)()
|
||||
);
|
||||
|
||||
return [
|
||||
...$described,
|
||||
[
|
||||
'id' => 'pods',
|
||||
'active' => defined('PODS_VERSION'),
|
||||
'version' => defined('PODS_VERSION') ? (string) PODS_VERSION : null,
|
||||
'problems' => [],
|
||||
'release' => null,
|
||||
],
|
||||
[
|
||||
'id' => 'the-events-calendar',
|
||||
'active' => $tecActive,
|
||||
|
||||
@@ -9,6 +9,7 @@ use LIT\MCP\Abilities\Ability;
|
||||
use LIT\MCP\Abilities\ToolAvailability;
|
||||
use LIT\MCP\Admin\SettingsPage;
|
||||
use LIT\MCP\ChangeLog\ChangeLog;
|
||||
use LIT\MCP\Compatibility\ProvidesCompatibility;
|
||||
use LIT\MCP\Integrations\Bootable;
|
||||
use LIT\MCP\Integrations\Divi\Admin\DiviTab;
|
||||
use LIT\MCP\Integrations\Divi\Cli\DiviCheckCommand;
|
||||
@@ -75,6 +76,7 @@ use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
|
||||
use LIT\MCP\Integrations\Divi\Release\CompatibilityCheck;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviCheckRoute;
|
||||
use LIT\MCP\Integrations\Divi\Release\FeatureOverview;
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviProfile;
|
||||
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
|
||||
use LIT\MCP\Integrations\Divi\Schema\DiviCatalogAbility;
|
||||
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
|
||||
@@ -99,7 +101,7 @@ defined('ABSPATH') || exit;
|
||||
* DiviWriter; each binds the composition to its entry via DiviWriter::composition().
|
||||
* contentComposer() gives lit/content-create the Divi short form (spec §6.2). boot() registers Divi's own hooks.
|
||||
*/
|
||||
final class DiviIntegration implements Integration, Bootable
|
||||
final class DiviIntegration implements Integration, Bootable, ProvidesCompatibility
|
||||
{
|
||||
private DiviModules $modules;
|
||||
private DesignSystemReader $design;
|
||||
@@ -186,6 +188,11 @@ final class DiviIntegration implements Integration, Bootable
|
||||
return DiviModules::isAvailable();
|
||||
}
|
||||
|
||||
public function compatibilities(): array
|
||||
{
|
||||
return [DiviProfile::profile()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Divi's own post types (library, Theme Builder, canvases, speculation), which the content tools leave alone:
|
||||
* ContentSchema's excluded post types. Static because the schema is built before this integration (Plugin).
|
||||
|
||||
@@ -6,7 +6,6 @@ namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
use LIT\MCP\Abilities\Ability;
|
||||
use LIT\MCP\Abilities\AbilityGuard;
|
||||
use LIT\MCP\Abilities\Errors;
|
||||
use LIT\MCP\ChangeLog\ChangeLog;
|
||||
use LIT\MCP\Compatibility\ReleaseState;
|
||||
use WP_Error;
|
||||
@@ -19,8 +18,8 @@ defined('ABSPATH') || exit;
|
||||
* answers divi_incompatible / divi_untested with the affected capability instead of failing inside Divi. A refused
|
||||
* write is logged with result "refused" (as before); a refused read changes nothing and is not. A tool that declares
|
||||
* nothing (RequiresDiviFeatures, or DeclaresRequirements for lit/content-create without divi) is left alone; the
|
||||
* adapters still check each write themselves. A declared ID that is no Divi capability (FeatureSets::unknownIds())
|
||||
* locks the call as divi_incompatible with details.unknown – the lock fails closed.
|
||||
* adapters still check each write themselves. IDs that are no Divi capability are ignored here; CapabilityGate
|
||||
* checks them (and locks the unknown ones).
|
||||
*/
|
||||
final class DiviFeatureGate implements AbilityGuard
|
||||
{
|
||||
@@ -30,11 +29,6 @@ final class DiviFeatureGate implements AbilityGuard
|
||||
|
||||
public function refusal(Ability $ability, array $input): ?WP_Error
|
||||
{
|
||||
$unknown = FeatureSets::unknownIds($ability, $input);
|
||||
if ($unknown !== []) {
|
||||
return $this->refuseUnknown($ability, $unknown);
|
||||
}
|
||||
|
||||
$lock = ReleaseGate::lockOf(FeatureSets::declaredBy($ability, $input));
|
||||
if ($lock === null) {
|
||||
return null;
|
||||
@@ -53,34 +47,6 @@ final class DiviFeatureGate implements AbilityGuard
|
||||
return $lock->toError();
|
||||
}
|
||||
|
||||
/** @param non-empty-list<string> $unknown Declared IDs that are no Divi capability. */
|
||||
private function refuseUnknown(Ability $ability, array $unknown): WP_Error
|
||||
{
|
||||
$status = ReleaseGate::status();
|
||||
$ids = implode(', ', $unknown);
|
||||
if (! self::readsOnly($ability)) {
|
||||
$this->log?->record(
|
||||
$ability->name(),
|
||||
'divi',
|
||||
$status->version,
|
||||
sprintf('Abgelehnt: %s – unbekannte Fähigkeit %s.', $status->versionLabel(), $ids),
|
||||
result: 'refused'
|
||||
);
|
||||
}
|
||||
|
||||
return Errors::make(
|
||||
'divi_incompatible',
|
||||
sprintf(
|
||||
'%s: Das Werkzeug verlangt unbekannte Fähigkeiten (%s). Das ist gesperrt. Es wurde nichts geändert.',
|
||||
$status->versionLabel(),
|
||||
$ids
|
||||
),
|
||||
'Den Nutzer informieren, dass LIT MCP aktualisiert werden muss.',
|
||||
423,
|
||||
['unknown' => $unknown]
|
||||
);
|
||||
}
|
||||
|
||||
/** A refusal is the whole answer: there is no result to annotate (the lock of reading tools is gone, E2). */
|
||||
public function annotate(Ability $ability, mixed $result): mixed
|
||||
{
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
use LIT\MCP\Compatibility\Profile;
|
||||
use LIT\MCP\Compatibility\Tier;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** Divi as a compatibility profile: every version needs a release, the matrix comes from the release gate. */
|
||||
final class DiviProfile
|
||||
{
|
||||
public static function profile(): Profile
|
||||
{
|
||||
return new Profile(
|
||||
'divi',
|
||||
'Divi',
|
||||
Tier::ReleasePerVersion,
|
||||
DiviFeature::cases(),
|
||||
array_values(DiviAdapters::manifests()),
|
||||
version: DiviEnvironment::version(...),
|
||||
matrix: static fn () => ReleaseGate::status()->matrix
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ 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. Every declared
|
||||
* ID is a Divi ID; one that is no DiviFeature is skipped here but locks the call (unknownIds(), fail closed).
|
||||
* 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>
|
||||
@@ -44,25 +44,6 @@ final class FeatureSets
|
||||
return self::merge($features);
|
||||
}
|
||||
|
||||
/**
|
||||
* The IDs a core tool declares for $input that are no DiviFeature. Such a call cannot be judged, so
|
||||
* DiviFeatureGate refuses it instead of letting it run unlocked.
|
||||
*
|
||||
* @param array<string, mixed> $input
|
||||
* @return list<string>
|
||||
*/
|
||||
public static function unknownIds(Ability $ability, array $input): array
|
||||
{
|
||||
if ($ability instanceof RequiresDiviFeatures || ! $ability instanceof DeclaresRequirements) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_values(array_filter(
|
||||
$ability->requirements($input),
|
||||
static fn (string $id): bool => DiviFeature::tryFrom($id) === null
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<DiviFeature> ...$lists
|
||||
* @return list<DiviFeature> Every capability once, in the order of first appearance.
|
||||
|
||||
@@ -5,6 +5,10 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Integrations;
|
||||
|
||||
use LIT\MCP\Abilities\Ability;
|
||||
use LIT\MCP\Compatibility\Compatibility;
|
||||
use LIT\MCP\Compatibility\Profile;
|
||||
use LIT\MCP\Compatibility\ProvidesCompatibility;
|
||||
use LogicException;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
@@ -17,6 +21,7 @@ final class IntegrationRegistry
|
||||
private ?array $loaded = null;
|
||||
|
||||
private bool $booted = false;
|
||||
private ?Compatibility $compatibility = null;
|
||||
|
||||
public function __construct(Integration ...$integrations)
|
||||
{
|
||||
@@ -63,7 +68,8 @@ final class IntegrationRegistry
|
||||
}
|
||||
|
||||
/**
|
||||
* Abilities of the available integrations, grouped by integration id (registry order).
|
||||
* Abilities of the available integrations, grouped by integration id (registry order). An integration without
|
||||
* abilities (Pods) is no group.
|
||||
*
|
||||
* @return array<string, list<Ability>>
|
||||
*/
|
||||
@@ -71,6 +77,9 @@ final class IntegrationRegistry
|
||||
{
|
||||
$groups = [];
|
||||
foreach ($this->loaded() as [$integration, $own]) {
|
||||
if ($own === []) {
|
||||
continue;
|
||||
}
|
||||
$groups[$integration->id()] = array_merge($groups[$integration->id()] ?? [], $own);
|
||||
}
|
||||
|
||||
@@ -101,6 +110,29 @@ final class IntegrationRegistry
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/** @return list<Profile> Profiles of every registered integration that provides some, available or not. */
|
||||
public function compatibilities(): array
|
||||
{
|
||||
$profiles = [];
|
||||
foreach ($this->integrations as $integration) {
|
||||
if ($integration instanceof ProvidesCompatibility) {
|
||||
array_push($profiles, ...$integration->compatibilities());
|
||||
}
|
||||
}
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* The compatibility state of all profiles, one shared instance (its matrices are memoised per request).
|
||||
*
|
||||
* @throws LogicException When two profiles or capabilities share an ID (a programming error, PluginTest).
|
||||
*/
|
||||
public function compatibility(): Compatibility
|
||||
{
|
||||
return $this->compatibility ??= new Compatibility($this->compatibilities());
|
||||
}
|
||||
|
||||
/** @return list<string> Ids of all known integrations, available or not. */
|
||||
public function ids(): array
|
||||
{
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Pods;
|
||||
|
||||
use LIT\MCP\Compatibility\Call;
|
||||
use LIT\MCP\Compatibility\ConstantRequirement;
|
||||
use LIT\MCP\Compatibility\Manifest;
|
||||
use LIT\MCP\Compatibility\Profile;
|
||||
use LIT\MCP\Compatibility\Tier;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Pods as a compatibility profile: the calls PodsFieldSource makes, checked against Pods 3.3.9.2. Argument counts
|
||||
* are those PodsFieldSource passes. A hard problem (missing function or constant, Pods below 2.8) locks the
|
||||
* affected capabilities; Pods has no per-version release.
|
||||
*/
|
||||
final class PodsCompatibility
|
||||
{
|
||||
public static function profile(): Profile
|
||||
{
|
||||
$all = PodsFeature::cases();
|
||||
|
||||
return new Profile(
|
||||
'pods',
|
||||
'Pods',
|
||||
Tier::LockOnFailure,
|
||||
$all,
|
||||
[new Manifest(
|
||||
'Pods',
|
||||
[
|
||||
new Call('pods_api', 0, [PodsFeature::SchemaRead]),
|
||||
new Call('PodsAPI::load_pod', 1, [PodsFeature::SchemaRead]),
|
||||
new Call('pods', 2, [PodsFeature::FieldRead, PodsFeature::FieldWrite]),
|
||||
new Call('Pods::field', 1, [PodsFeature::FieldRead]),
|
||||
new Call('Pods::save', 4, [PodsFeature::FieldWrite]),
|
||||
new Call('pods_has_permissions', 1, [PodsFeature::FieldPermissions]),
|
||||
new Call('pods_permission', 1, [PodsFeature::FieldPermissions]),
|
||||
],
|
||||
[new ConstantRequirement('PODS_VERSION', $all)]
|
||||
)],
|
||||
static fn (): bool => defined('PODS_VERSION'),
|
||||
static fn (): ?string => defined('PODS_VERSION') ? (string) PODS_VERSION : null,
|
||||
'2.8'
|
||||
);
|
||||
}
|
||||
}
|
||||
Generated
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Pods;
|
||||
|
||||
use LIT\MCP\Compatibility\Capability;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
// The installed PHPCompatibility sniffs predate enums (PHP 8.1) and read their methods as plain functions.
|
||||
// phpcs:disable PHPCompatibility.Variables.ForbiddenThisUseContexts.OutsideObjectContext
|
||||
|
||||
/** What the plugin can do with Pods, as far as a Pods update may break it separately. */
|
||||
enum PodsFeature: string implements Capability
|
||||
{
|
||||
case SchemaRead = 'pods-schema-read';
|
||||
case FieldRead = 'pods-field-read';
|
||||
case FieldWrite = 'pods-field-write';
|
||||
case FieldPermissions = 'pods-field-permissions';
|
||||
|
||||
public function id(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::SchemaRead => 'Pods-Schema lesen',
|
||||
self::FieldRead => 'Pods-Felder lesen',
|
||||
self::FieldWrite => 'Pods-Felder schreiben',
|
||||
self::FieldPermissions => 'Pods-Feldrechte prüfen',
|
||||
};
|
||||
}
|
||||
|
||||
public function isWrite(): bool
|
||||
{
|
||||
return $this === self::FieldWrite;
|
||||
}
|
||||
}
|
||||
+36
-9
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Pods;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Integrations\WordPress\Schema\FieldDefinition;
|
||||
use LIT\MCP\Integrations\WordPress\Schema\FieldSource;
|
||||
use RuntimeException;
|
||||
@@ -21,6 +22,10 @@ defined('ABSPATH') || exit;
|
||||
* They are mapped like Pods' own view does: hidden → HIDDEN; read_only → READONLY; restricted
|
||||
* and not permitted for the current user → READONLY with read_only_restricted, else HIDDEN.
|
||||
* write() refuses every field that is not writable before anything reaches Pods.
|
||||
*
|
||||
* Every part is gated by a compatibility capability ($usable, from Compatibility::usable): the schema (SchemaRead,
|
||||
* else no fields), reading (FieldRead, else null), writing (FieldWrite and FieldRead, else write() throws and the
|
||||
* fields are READONLY) and the permission check (FieldPermissions, else no field counts as permitted).
|
||||
*/
|
||||
final class PodsFieldSource implements FieldSource
|
||||
{
|
||||
@@ -45,8 +50,13 @@ final class PodsFieldSource implements FieldSource
|
||||
/** @var callable(object): ?bool Pods field => null (unrestricted) or whether the current user may use it. */
|
||||
private $permitted;
|
||||
|
||||
public function __construct(?callable $loadPod = null, ?callable $podFor = null, ?callable $permitted = null)
|
||||
{
|
||||
/** @param Closure(PodsFeature): bool $usable Whether a Pods capability may be used (a locked one is never called). */
|
||||
public function __construct(
|
||||
private Closure $usable,
|
||||
?callable $loadPod = null,
|
||||
?callable $podFor = null,
|
||||
?callable $permitted = null
|
||||
) {
|
||||
$this->loadPod = $loadPod ?? static fn (string $name): mixed => pods_api()->load_pod(['name' => $name]);
|
||||
$this->podFor = $podFor ?? static fn (string $name, int $id): mixed => pods($name, $id);
|
||||
$this->permitted = $permitted ?? static function (object $field): ?bool {
|
||||
@@ -59,11 +69,6 @@ final class PodsFieldSource implements FieldSource
|
||||
};
|
||||
}
|
||||
|
||||
public static function isAvailable(): bool
|
||||
{
|
||||
return function_exists('pods_api') && function_exists('pods');
|
||||
}
|
||||
|
||||
public function id(): string
|
||||
{
|
||||
return 'pods';
|
||||
@@ -71,6 +76,10 @@ final class PodsFieldSource implements FieldSource
|
||||
|
||||
public function fieldsFor(string $postType): array
|
||||
{
|
||||
if (! ($this->usable)(PodsFeature::SchemaRead)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pod = ($this->loadPod)($postType);
|
||||
if (! is_object($pod) || $pod->get_type() !== 'post_type') {
|
||||
return [];
|
||||
@@ -91,6 +100,10 @@ final class PodsFieldSource implements FieldSource
|
||||
|
||||
public function read(int $postId, FieldDefinition $field): mixed
|
||||
{
|
||||
if (! ($this->usable)(PodsFeature::FieldRead)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pod = ($this->podFor)((string) get_post_type($postId), $postId);
|
||||
if (! is_object($pod)) {
|
||||
return null;
|
||||
@@ -110,6 +123,10 @@ final class PodsFieldSource implements FieldSource
|
||||
|
||||
public function write(int $postId, string $postType, array $values, array $definitions): void
|
||||
{
|
||||
if (! $this->canWrite()) {
|
||||
throw new RuntimeException('Pods fields cannot be written: Pods is locked by the compatibility check.');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
foreach ($values as $key => $value) {
|
||||
$status = isset($definitions[$key]) ? $definitions[$key]->status : null;
|
||||
@@ -180,11 +197,21 @@ final class PodsFieldSource implements FieldSource
|
||||
return FieldDefinition::HIDDEN;
|
||||
}
|
||||
|
||||
if (($this->permitted)($field) === false) {
|
||||
// Fail closed: without the permission check no field counts as permitted.
|
||||
if (! ($this->usable)(PodsFeature::FieldPermissions) || ($this->permitted)($field) === false) {
|
||||
return self::flag($field, 'read_only_restricted') ? FieldDefinition::READONLY : FieldDefinition::HIDDEN;
|
||||
}
|
||||
|
||||
return self::flag($field, 'read_only') ? FieldDefinition::READONLY : $open;
|
||||
$status = self::flag($field, 'read_only') ? FieldDefinition::READONLY : $open;
|
||||
|
||||
// Pods writing is locked (it needs FieldWrite and FieldRead) → nothing is writable.
|
||||
return $status !== FieldDefinition::HIDDEN && ! $this->canWrite() ? FieldDefinition::READONLY : $status;
|
||||
}
|
||||
|
||||
/** A write snapshots the value first (read), so FieldRead is as necessary as FieldWrite. */
|
||||
private function canWrite(): bool
|
||||
{
|
||||
return ($this->usable)(PodsFeature::FieldWrite) && ($this->usable)(PodsFeature::FieldRead);
|
||||
}
|
||||
|
||||
/** A Pods boolean option (stored as 1/'1'/true, like pods_v_bool()). */
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Pods;
|
||||
|
||||
use LIT\MCP\Compatibility\ProvidesCompatibility;
|
||||
use LIT\MCP\Integrations\Integration;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Pods has no tools of its own (the content tools read its fields through PodsFieldSource); this integration only
|
||||
* brings the compatibility profile. Without abilities it is no tool group.
|
||||
*/
|
||||
final class PodsIntegration implements Integration, ProvidesCompatibility
|
||||
{
|
||||
public function id(): string
|
||||
{
|
||||
return 'pods';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Pods';
|
||||
}
|
||||
|
||||
public function isAvailable(): bool
|
||||
{
|
||||
return defined('PODS_VERSION');
|
||||
}
|
||||
|
||||
public function abilities(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function compatibilities(): array
|
||||
{
|
||||
return [PodsCompatibility::profile()];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user