Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
117 lines
3.4 KiB
PHP
117 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace LIT\MCP\Admin;
|
|
|
|
/**
|
|
* Settings page "Einstellungen → LIT MCP" with tabs (spec §8.4). Settings API, no JS framework.
|
|
*/
|
|
final class SettingsPage
|
|
{
|
|
public const SLUG = 'lit-mcp';
|
|
|
|
/** @var array<string, AdminTab> */
|
|
private array $tabs = [];
|
|
|
|
/** @param list<AdminTab> $tabs The first tab is the default. */
|
|
public function __construct(array $tabs)
|
|
{
|
|
foreach ($tabs as $tab) {
|
|
$this->tabs[$tab->slug()] = $tab;
|
|
}
|
|
}
|
|
|
|
public function register(): void
|
|
{
|
|
add_action('admin_menu', [$this, 'addMenu']);
|
|
add_action('admin_init', [$this, 'registerSettings']);
|
|
foreach ($this->tabs as $tab) {
|
|
$tab->registerActions();
|
|
}
|
|
}
|
|
|
|
public function addMenu(): void
|
|
{
|
|
add_options_page(
|
|
__('LIT MCP', 'wp-lit-mcp'),
|
|
__('LIT MCP', 'wp-lit-mcp'),
|
|
'manage_options',
|
|
self::SLUG,
|
|
[$this, 'render']
|
|
);
|
|
}
|
|
|
|
public function registerSettings(): void
|
|
{
|
|
foreach ($this->tabs as $tab) {
|
|
$tab->registerSettings();
|
|
}
|
|
}
|
|
|
|
/** @return list<string> */
|
|
public function tabSlugs(): array
|
|
{
|
|
return array_keys($this->tabs);
|
|
}
|
|
|
|
public function currentTab(): string
|
|
{
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only tab switch.
|
|
$requested = isset($_GET['tab']) ? sanitize_key(wp_unslash((string) $_GET['tab'])) : '';
|
|
|
|
return isset($this->tabs[$requested]) ? $requested : (string) array_key_first($this->tabs);
|
|
}
|
|
|
|
/** @param array<string, string> $args */
|
|
public static function url(string $tab = '', array $args = []): string
|
|
{
|
|
$query = array_merge(['page' => self::SLUG], $tab !== '' ? ['tab' => $tab] : [], $args);
|
|
|
|
return add_query_arg($query, admin_url('options-general.php'));
|
|
}
|
|
|
|
public function render(): void
|
|
{
|
|
if (! current_user_can('manage_options')) {
|
|
return;
|
|
}
|
|
|
|
$current = $this->currentTab();
|
|
echo '<div class="wrap"><h1>' . esc_html__('LIT MCP', 'wp-lit-mcp') . '</h1>';
|
|
$this->renderNotice();
|
|
echo '<nav class="nav-tab-wrapper">';
|
|
foreach ($this->tabs as $slug => $tab) {
|
|
printf(
|
|
'<a href="%s" class="nav-tab%s">%s</a>',
|
|
esc_url(self::url($slug)),
|
|
$slug === $current ? ' nav-tab-active' : '',
|
|
esc_html($tab->label())
|
|
);
|
|
}
|
|
echo '</nav>';
|
|
$this->tabs[$current]->render();
|
|
echo '</div>';
|
|
}
|
|
|
|
private function renderNotice(): void
|
|
{
|
|
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only.
|
|
$key = isset($_GET['lit_mcp_notice']) ? sanitize_key(wp_unslash((string) $_GET['lit_mcp_notice'])) : '';
|
|
$notice = match ($key) {
|
|
'revoked' => ['success', __('Verbindung widerrufen.', 'wp-lit-mcp')],
|
|
'revoke_failed' => ['error', __('Die Verbindung konnte nicht widerrufen werden.', 'wp-lit-mcp')],
|
|
'cleared' => ['success', __('Protokoll geleert.', 'wp-lit-mcp')],
|
|
default => null,
|
|
};
|
|
|
|
if ($notice !== null) {
|
|
printf(
|
|
'<div class="notice notice-%s is-dismissible"><p>%s</p></div>',
|
|
esc_attr($notice[0]),
|
|
esc_html($notice[1])
|
|
);
|
|
}
|
|
}
|
|
}
|