refactor: integration labels, AdminPostAction, ExposurePolicy key parsing (M9)
Integrations name their own admin group (Integration::label, registry labels()); ToolsTab no longer hard-codes ids and explains why it re-applies withGroups. AccessTab and LogTab share AdminPostAction (guard, redirect, terminate seam). ExposurePolicy parses option keys in one place, has one isRequired() check and a clear $groups docblock (null vs []). 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:
@@ -47,7 +47,12 @@ final class ExposurePolicy
|
|||||||
/** @var array<string, true> Group ids switched to "nur lesen". */
|
/** @var array<string, true> Group ids switched to "nur lesen". */
|
||||||
private array $readonlyGroups = [];
|
private array $readonlyGroups = [];
|
||||||
|
|
||||||
/** @var array<string, string>|null Ability name => group id; null until resolved. */
|
/**
|
||||||
|
* Ability name => group id. [] = no groups known (withGroups() not called: group entries have
|
||||||
|
* no effect); null = a lazy $groupSource is set and not resolved yet (groupOf() resolves it).
|
||||||
|
*
|
||||||
|
* @var array<string, string>|null
|
||||||
|
*/
|
||||||
private ?array $groups = [];
|
private ?array $groups = [];
|
||||||
|
|
||||||
/** @var (callable(): array<string, string>)|null Lazy source of $groups. */
|
/** @var (callable(): array<string, string>)|null Lazy source of $groups. */
|
||||||
@@ -60,17 +65,17 @@ final class ExposurePolicy
|
|||||||
public function __construct(array $overrides = [])
|
public function __construct(array $overrides = [])
|
||||||
{
|
{
|
||||||
foreach ($overrides as $key => $value) {
|
foreach ($overrides as $key => $value) {
|
||||||
$key = (string) $key;
|
['kind' => $kind, 'id' => $id] = self::parseKey((string) $key);
|
||||||
if (str_starts_with($key, self::GROUP_PREFIX)) {
|
if ($kind === 'group') {
|
||||||
if (is_string($value) && in_array($value, self::MODES, true)) {
|
if (is_string($value) && in_array($value, self::MODES, true)) {
|
||||||
$this->groupModes[substr($key, strlen(self::GROUP_PREFIX))] = $value;
|
$this->groupModes[$id] = $value;
|
||||||
}
|
}
|
||||||
} elseif (str_starts_with($key, self::READONLY_PREFIX)) {
|
} elseif ($kind === 'readonly') {
|
||||||
if ($value === true) {
|
if ($value === true) {
|
||||||
$this->readonlyGroups[substr($key, strlen(self::READONLY_PREFIX))] = true;
|
$this->readonlyGroups[$id] = true;
|
||||||
}
|
}
|
||||||
} elseif (is_string($value)) {
|
} elseif (is_string($value)) {
|
||||||
$this->tools[$key] = $value;
|
$this->tools[$id] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +108,7 @@ final class ExposurePolicy
|
|||||||
|
|
||||||
public function mode(Ability $ability): string
|
public function mode(Ability $ability): string
|
||||||
{
|
{
|
||||||
if (in_array($ability->name(), self::REQUIRED, true)) {
|
if (self::isRequired($ability->name())) {
|
||||||
return $this->defaultMode($ability);
|
return $this->defaultMode($ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +124,7 @@ final class ExposurePolicy
|
|||||||
{
|
{
|
||||||
$name = $ability->name();
|
$name = $ability->name();
|
||||||
$groupMode = $this->groupModeOf($name);
|
$groupMode = $this->groupModeOf($name);
|
||||||
if ($groupMode === null || in_array($name, self::REQUIRED, true)) {
|
if ($groupMode === null || self::isRequired($name)) {
|
||||||
return $this->defaultMode($ability);
|
return $this->defaultMode($ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +138,7 @@ final class ExposurePolicy
|
|||||||
$groupMode = $this->groupModeOf($name);
|
$groupMode = $this->groupModeOf($name);
|
||||||
|
|
||||||
return $groupMode !== null
|
return $groupMode !== null
|
||||||
&& ! in_array($name, self::REQUIRED, true)
|
&& ! self::isRequired($name)
|
||||||
&& ! self::isAllowed($name, $groupMode);
|
&& ! self::isAllowed($name, $groupMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +150,7 @@ final class ExposurePolicy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($ability->name(), self::REQUIRED, true)) {
|
if (self::isRequired($ability->name())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +218,7 @@ final class ExposurePolicy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($name, self::REQUIRED, true)) {
|
if (self::isRequired($name)) {
|
||||||
return $mode === self::DIRECT;
|
return $mode === self::DIRECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,17 +249,18 @@ final class ExposurePolicy
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str_starts_with($key, self::GROUP_PREFIX)) {
|
['kind' => $kind, 'id' => $id] = self::parseKey($key);
|
||||||
|
if ($kind === 'group') {
|
||||||
if (
|
if (
|
||||||
self::isGroupId(substr($key, strlen(self::GROUP_PREFIX)), $groupIds)
|
self::isGroupId($id, $groupIds)
|
||||||
&& is_string($value)
|
&& is_string($value)
|
||||||
&& in_array($value, self::MODES, true)
|
&& in_array($value, self::MODES, true)
|
||||||
) {
|
) {
|
||||||
$clean[$key] = $value;
|
$clean[$key] = $value;
|
||||||
}
|
}
|
||||||
} elseif (str_starts_with($key, self::READONLY_PREFIX)) {
|
} elseif ($kind === 'readonly') {
|
||||||
if (
|
if (
|
||||||
self::isGroupId(substr($key, strlen(self::READONLY_PREFIX)), $groupIds)
|
self::isGroupId($id, $groupIds)
|
||||||
&& ($value === true || $value === '1' || $value === 1)
|
&& ($value === true || $value === '1' || $value === 1)
|
||||||
) {
|
) {
|
||||||
$clean[$key] = true;
|
$clean[$key] = true;
|
||||||
@@ -272,6 +278,28 @@ final class ExposurePolicy
|
|||||||
return $clean;
|
return $clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What an option key names: "@group:<id>" a group's mode, "@readonly:<id>" a group's
|
||||||
|
* read-only switch, anything else an ability (id = the whole key).
|
||||||
|
*
|
||||||
|
* @return array{kind: 'group'|'readonly'|'tool', id: string}
|
||||||
|
*/
|
||||||
|
private static function parseKey(string $key): array
|
||||||
|
{
|
||||||
|
foreach (['group' => self::GROUP_PREFIX, 'readonly' => self::READONLY_PREFIX] as $kind => $prefix) {
|
||||||
|
if (str_starts_with($key, $prefix)) {
|
||||||
|
return ['kind' => $kind, 'id' => substr($key, strlen($prefix))];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['kind' => 'tool', 'id' => $key];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function isRequired(string $name): bool
|
||||||
|
{
|
||||||
|
return in_array($name, self::REQUIRED, true);
|
||||||
|
}
|
||||||
|
|
||||||
/** @param list<string>|null $groupIds */
|
/** @param list<string>|null $groupIds */
|
||||||
private static function isGroupId(string $id, ?array $groupIds): bool
|
private static function isGroupId(string $id, ?array $groupIds): bool
|
||||||
{
|
{
|
||||||
|
|||||||
+4
-11
@@ -15,17 +15,14 @@ final class AccessTab implements AdminTab
|
|||||||
public const GROUP = 'lit_mcp_access';
|
public const GROUP = 'lit_mcp_access';
|
||||||
public const REVOKE_ACTION = 'lit_mcp_revoke';
|
public const REVOKE_ACTION = 'lit_mcp_revoke';
|
||||||
|
|
||||||
/** @var callable(): void */
|
private AdminPostAction $action;
|
||||||
private $terminate;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Access $access,
|
private Access $access,
|
||||||
private ConnectionRepository $connections,
|
private ConnectionRepository $connections,
|
||||||
?callable $terminate = null
|
?callable $terminate = null
|
||||||
) {
|
) {
|
||||||
$this->terminate = $terminate ?? static function (): void {
|
$this->action = new AdminPostAction($terminate);
|
||||||
exit;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function slug(): string
|
public function slug(): string
|
||||||
@@ -121,9 +118,7 @@ final class AccessTab implements AdminTab
|
|||||||
|
|
||||||
public function handleRevoke(): void
|
public function handleRevoke(): void
|
||||||
{
|
{
|
||||||
if (! current_user_can('manage_options')) {
|
$this->action->guard();
|
||||||
wp_die(esc_html__('Keine Berechtigung.', 'wp-lit-mcp'), '', ['response' => 403]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- verified right below.
|
// phpcs:disable WordPress.Security.NonceVerification.Missing -- verified right below.
|
||||||
$uuid = isset($_POST['uuid']) && is_string($_POST['uuid'])
|
$uuid = isset($_POST['uuid']) && is_string($_POST['uuid'])
|
||||||
@@ -134,9 +129,7 @@ final class AccessTab implements AdminTab
|
|||||||
check_admin_referer(self::REVOKE_ACTION . '_' . $uuid);
|
check_admin_referer(self::REVOKE_ACTION . '_' . $uuid);
|
||||||
|
|
||||||
$revoked = $this->connections->revoke($userId, $uuid);
|
$revoked = $this->connections->revoke($userId, $uuid);
|
||||||
$notice = $revoked ? 'revoked' : 'revoke_failed';
|
$this->action->redirect($this->slug(), $revoked ? 'revoked' : 'revoke_failed');
|
||||||
wp_safe_redirect(SettingsPage::url($this->slug(), ['lit_mcp_notice' => $notice]));
|
|
||||||
($this->terminate)();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function warnOnce(string $code, string $message): void
|
private static function warnOnce(string $code, string $message): void
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Admin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The frame of an admin-post.php handler of the settings page: only administrators
|
||||||
|
* (manage_options, else 403), and after the action a redirect back to the tab with a notice.
|
||||||
|
* The terminate seam replaces exit in tests.
|
||||||
|
*/
|
||||||
|
final class AdminPostAction
|
||||||
|
{
|
||||||
|
/** @var callable(): void */
|
||||||
|
private $terminate;
|
||||||
|
|
||||||
|
/** @param (callable(): void)|null $terminate Called after the redirect; default exit. */
|
||||||
|
public function __construct(?callable $terminate = null)
|
||||||
|
{
|
||||||
|
$this->terminate = $terminate ?? static function (): void {
|
||||||
|
exit;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stops with 403 unless the current user may manage options. */
|
||||||
|
public function guard(): void
|
||||||
|
{
|
||||||
|
if (! current_user_can('manage_options')) {
|
||||||
|
wp_die(esc_html__('Keine Berechtigung.', 'wp-lit-mcp'), '', ['response' => 403]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Redirects to settings tab $slug with "lit_mcp_notice" = $notice and ends the request. */
|
||||||
|
public function redirect(string $slug, string $notice): void
|
||||||
|
{
|
||||||
|
wp_safe_redirect(SettingsPage::url($slug, ['lit_mcp_notice' => $notice]));
|
||||||
|
($this->terminate)();
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-11
@@ -16,14 +16,11 @@ final class LogTab implements AdminTab
|
|||||||
public const CLEAR_ACTION = 'lit_mcp_clear_log';
|
public const CLEAR_ACTION = 'lit_mcp_clear_log';
|
||||||
private const RESULTS = ['ok', 'partial'];
|
private const RESULTS = ['ok', 'partial'];
|
||||||
|
|
||||||
/** @var callable(): void */
|
private AdminPostAction $action;
|
||||||
private $terminate;
|
|
||||||
|
|
||||||
public function __construct(private ChangeLog $log, private Limits $limits, ?callable $terminate = null)
|
public function __construct(private ChangeLog $log, private Limits $limits, ?callable $terminate = null)
|
||||||
{
|
{
|
||||||
$this->terminate = $terminate ?? static function (): void {
|
$this->action = new AdminPostAction($terminate);
|
||||||
exit;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function slug(): string
|
public function slug(): string
|
||||||
@@ -210,13 +207,9 @@ final class LogTab implements AdminTab
|
|||||||
|
|
||||||
public function handleClear(): void
|
public function handleClear(): void
|
||||||
{
|
{
|
||||||
if (! current_user_can('manage_options')) {
|
$this->action->guard();
|
||||||
wp_die(esc_html__('Keine Berechtigung.', 'wp-lit-mcp'), '', ['response' => 403]);
|
|
||||||
}
|
|
||||||
|
|
||||||
check_admin_referer(self::CLEAR_ACTION);
|
check_admin_referer(self::CLEAR_ACTION);
|
||||||
$this->log->clear();
|
$this->log->clear();
|
||||||
wp_safe_redirect(SettingsPage::url($this->slug(), ['lit_mcp_notice' => 'cleared']));
|
$this->action->redirect($this->slug(), 'cleared');
|
||||||
($this->terminate)();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-16
@@ -68,8 +68,12 @@ final class ToolsTab implements AdminTab
|
|||||||
|
|
||||||
public function render(): void
|
public function render(): void
|
||||||
{
|
{
|
||||||
|
// Bind the policy to the groups of exactly the abilities listed below. The plugin's policy
|
||||||
|
// carries a lazy map of the same registry (resolved here anyway); a policy built without
|
||||||
|
// withGroups() would otherwise ignore every group mode and "nur lesen" switch on this page.
|
||||||
$policy = $this->policy->withGroups($this->registry->groupMap());
|
$policy = $this->policy->withGroups($this->registry->groupMap());
|
||||||
$groups = $this->registry->grouped();
|
$groups = $this->registry->grouped();
|
||||||
|
$labels = $this->registry->labels();
|
||||||
|
|
||||||
$this->renderIntro(isset($groups['divi']));
|
$this->renderIntro(isset($groups['divi']));
|
||||||
$this->renderCounter(count($policy->directNames($this->registry->abilities())));
|
$this->renderCounter(count($policy->directNames($this->registry->abilities())));
|
||||||
@@ -84,7 +88,7 @@ final class ToolsTab implements AdminTab
|
|||||||
echo '<div class="lit-mcp-groups">';
|
echo '<div class="lit-mcp-groups">';
|
||||||
$closed = $this->savedClosedBoxes();
|
$closed = $this->savedClosedBoxes();
|
||||||
foreach ($groups as $group => $abilities) {
|
foreach ($groups as $group => $abilities) {
|
||||||
$this->renderGroup($policy, $group, $abilities, $closed);
|
$this->renderGroup($policy, $group, $labels[$group] ?? $group, $abilities, $closed);
|
||||||
}
|
}
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
submit_button();
|
submit_button();
|
||||||
@@ -158,10 +162,14 @@ final class ToolsTab implements AdminTab
|
|||||||
* @param list<Ability> $abilities
|
* @param list<Ability> $abilities
|
||||||
* @param list<string>|null $closedBoxes
|
* @param list<string>|null $closedBoxes
|
||||||
*/
|
*/
|
||||||
private function renderGroup(ExposurePolicy $policy, string $group, array $abilities, ?array $closedBoxes): void
|
private function renderGroup(
|
||||||
{
|
ExposurePolicy $policy,
|
||||||
|
string $group,
|
||||||
|
string $label,
|
||||||
|
array $abilities,
|
||||||
|
?array $closedBoxes
|
||||||
|
): void {
|
||||||
$count = count($abilities);
|
$count = count($abilities);
|
||||||
$label = $this->groupLabel($group);
|
|
||||||
$boxId = 'lit-mcp-group-' . self::idPart($group);
|
$boxId = 'lit-mcp-group-' . self::idPart($group);
|
||||||
$modes = [ExposurePolicy::DIRECT => 0, ExposurePolicy::INDIRECT => 0, ExposurePolicy::DISABLED => 0];
|
$modes = [ExposurePolicy::DIRECT => 0, ExposurePolicy::INDIRECT => 0, ExposurePolicy::DISABLED => 0];
|
||||||
$mixed = false;
|
$mixed = false;
|
||||||
@@ -374,18 +382,6 @@ final class ToolsTab implements AdminTab
|
|||||||
return trim((string) preg_replace('/[^a-z0-9]+/', '-', strtolower($value)), '-');
|
return trim((string) preg_replace('/[^a-z0-9]+/', '-', strtolower($value)), '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function groupLabel(string $group): string
|
|
||||||
{
|
|
||||||
return match ($group) {
|
|
||||||
'core' => __('Grundlagen', 'wp-lit-mcp'),
|
|
||||||
'wordpress' => __('WordPress', 'wp-lit-mcp'),
|
|
||||||
'media' => __('Medien', 'wp-lit-mcp'),
|
|
||||||
'pods' => __('Pods', 'wp-lit-mcp'),
|
|
||||||
'divi' => __('Divi', 'wp-lit-mcp'),
|
|
||||||
default => $group,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private function modeLabel(string $mode): string
|
private function modeLabel(string $mode): string
|
||||||
{
|
{
|
||||||
return match ($mode) {
|
return match ($mode) {
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ final class CoreIntegration implements Integration
|
|||||||
return 'core';
|
return 'core';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return __('Grundlagen', 'wp-lit-mcp');
|
||||||
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
public function isAvailable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ final class DiviIntegration implements Integration
|
|||||||
return 'divi';
|
return 'divi';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return __('Divi', 'wp-lit-mcp');
|
||||||
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
public function isAvailable(): bool
|
||||||
{
|
{
|
||||||
return DiviModules::isAvailable();
|
return DiviModules::isAvailable();
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ interface Integration
|
|||||||
{
|
{
|
||||||
public function id(): string;
|
public function id(): string;
|
||||||
|
|
||||||
|
/** Translated name of the group in the admin UI, e.g. "Medien". */
|
||||||
|
public function label(): string;
|
||||||
|
|
||||||
public function isAvailable(): bool;
|
public function isAvailable(): bool;
|
||||||
|
|
||||||
/** @return list<Ability> */
|
/** @return list<Ability> */
|
||||||
|
|||||||
@@ -67,6 +67,17 @@ final class IntegrationRegistry
|
|||||||
return $map;
|
return $map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return array<string, string> Integration id => label, of the available integrations. */
|
||||||
|
public function labels(): array
|
||||||
|
{
|
||||||
|
$labels = [];
|
||||||
|
foreach ($this->loaded() as [$integration]) {
|
||||||
|
$labels[$integration->id()] = $integration->label();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $labels;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return list<string> Ids of all known integrations, available or not. */
|
/** @return list<string> Ids of all known integrations, available or not. */
|
||||||
public function ids(): array
|
public function ids(): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ final class MediaIntegration implements Integration
|
|||||||
return 'media';
|
return 'media';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return __('Medien', 'wp-lit-mcp');
|
||||||
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
public function isAvailable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ final class WordPressIntegration implements Integration
|
|||||||
return 'wordpress';
|
return 'wordpress';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function label(): string
|
||||||
|
{
|
||||||
|
return __('WordPress', 'wp-lit-mcp');
|
||||||
|
}
|
||||||
|
|
||||||
public function isAvailable(): bool
|
public function isAvailable(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ final class ToolsTabTest extends TestCase
|
|||||||
}
|
}
|
||||||
$integration = Mockery::mock(Integration::class);
|
$integration = Mockery::mock(Integration::class);
|
||||||
$integration->allows('id')->andReturn($id);
|
$integration->allows('id')->andReturn($id);
|
||||||
|
$integration->allows('label')->andReturn(
|
||||||
|
['core' => 'Grundlagen', 'media' => 'Medien', 'divi' => 'Divi', 'wordpress' => 'WordPress'][$id] ?? $id
|
||||||
|
);
|
||||||
$integration->allows('isAvailable')->andReturn($available);
|
$integration->allows('isAvailable')->andReturn($available);
|
||||||
$integration->allows('abilities')->andReturn($mocks);
|
$integration->allows('abilities')->andReturn($mocks);
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,17 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace LIT\MCP\Tests\Unit\Integrations;
|
namespace LIT\MCP\Tests\Unit\Integrations;
|
||||||
|
|
||||||
|
use Brain\Monkey\Functions;
|
||||||
use LIT\MCP\Abilities\Ability;
|
use LIT\MCP\Abilities\Ability;
|
||||||
|
use LIT\MCP\Integrations\Core\CoreIntegration;
|
||||||
|
use LIT\MCP\Integrations\Divi\DiviIntegration;
|
||||||
use LIT\MCP\Integrations\Integration;
|
use LIT\MCP\Integrations\Integration;
|
||||||
use LIT\MCP\Integrations\IntegrationRegistry;
|
use LIT\MCP\Integrations\IntegrationRegistry;
|
||||||
|
use LIT\MCP\Integrations\Media\MediaIntegration;
|
||||||
|
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||||
|
use LIT\MCP\Integrations\WordPress\WordPressIntegration;
|
||||||
|
use LIT\MCP\Limits;
|
||||||
|
use LIT\MCP\Tests\Support\FakeChangeLog;
|
||||||
use LIT\MCP\Tests\Unit\TestCase;
|
use LIT\MCP\Tests\Unit\TestCase;
|
||||||
use Mockery;
|
use Mockery;
|
||||||
|
|
||||||
@@ -49,6 +57,28 @@ final class IntegrationRegistryTest extends TestCase
|
|||||||
self::assertSame(['core', 'divi', 'pods'], $registry->ids());
|
self::assertSame(['core', 'divi', 'pods'], $registry->ids());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testLabelsOfTheAvailableIntegrations(): void
|
||||||
|
{
|
||||||
|
$core = $this->integration('core', true, []);
|
||||||
|
$core->allows('label')->andReturn('Grundlagen');
|
||||||
|
|
||||||
|
$registry = new IntegrationRegistry($core, $this->integration('pods', false, []));
|
||||||
|
|
||||||
|
self::assertSame(['core' => 'Grundlagen'], $registry->labels());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** M9: the integrations own their admin labels (formerly hard-coded in ToolsTab). */
|
||||||
|
public function testEveryIntegrationNamesItsGroup(): void
|
||||||
|
{
|
||||||
|
Functions\stubTranslationFunctions();
|
||||||
|
$schema = Mockery::mock(ContentSchema::class);
|
||||||
|
|
||||||
|
self::assertSame('Grundlagen', (new CoreIntegration(''))->label());
|
||||||
|
self::assertSame('WordPress', (new WordPressIntegration($schema, FakeChangeLog::forbidden()))->label());
|
||||||
|
self::assertSame('Medien', (new MediaIntegration($schema, FakeChangeLog::forbidden(), new Limits()))->label());
|
||||||
|
self::assertSame('Divi', (new DiviIntegration($schema, FakeChangeLog::forbidden()))->label());
|
||||||
|
}
|
||||||
|
|
||||||
private function ability(string $name, bool $direct): Ability
|
private function ability(string $name, bool $direct): Ability
|
||||||
{
|
{
|
||||||
$ability = Mockery::mock(Ability::class);
|
$ability = Mockery::mock(Ability::class);
|
||||||
|
|||||||
Reference in New Issue
Block a user