Files
WP-LIT-MCP/src/Admin/SettingsPage.php
T

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])
);
}
}
}