terminate = $terminate ?? static function (): void { exit; }; } public function slug(): string { return 'protokoll'; } public function label(): string { return __('Protokoll', 'wp-lit-mcp'); } public function registerSettings(): void { } public function registerActions(): void { add_action('admin_post_' . self::CLEAR_ACTION, [$this, 'handleClear']); } /** @return array{ability: string, user_id: int} */ public function filters(): array { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only list filters. $ability = isset($_GET['ability']) ? sanitize_text_field(wp_unslash((string) $_GET['ability'])) : ''; $userId = isset($_GET['user_id']) ? absint($_GET['user_id']) : 0; // phpcs:enable WordPress.Security.NonceVerification.Recommended return [ 'ability' => preg_match('#^[a-z0-9-]+/[a-z0-9-]+$#', $ability) === 1 ? $ability : '', 'user_id' => $userId, ]; } /** * Additional whitelisted filters (ruling C5/Q13: ability, result, user, date range). * Kept separate from filters() so that method's return shape stays exactly what is * unit-tested (and what earlier callers of this tab may already rely on). * * @return array{result: string, date_from: string, date_to: string} */ public function extraFilters(): array { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only list filters. $result = isset($_GET['result']) ? sanitize_text_field(wp_unslash((string) $_GET['result'])) : ''; $dateFrom = isset($_GET['date_from']) ? sanitize_text_field(wp_unslash((string) $_GET['date_from'])) : ''; $dateTo = isset($_GET['date_to']) ? sanitize_text_field(wp_unslash((string) $_GET['date_to'])) : ''; // phpcs:enable WordPress.Security.NonceVerification.Recommended return [ 'result' => in_array($result, self::RESULTS, true) ? $result : '', 'date_from' => self::isDate($dateFrom) ? $dateFrom : '', 'date_to' => self::isDate($dateTo) ? $dateTo : '', ]; } private static function isDate(string $value): bool { return preg_match('#^\d{4}-\d{2}-\d{2}$#', $value) === 1; } public function render(): void { if (! class_exists('WP_List_Table')) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } $filters = $this->filters(); $extra = $this->extraFilters(); $days = $this->limits->logRetentionDays(); printf( '
%s %s
', esc_html__('Das Protokoll enthält nur Verweise, keine Inhalte.', 'wp-lit-mcp'), sprintf( /* translators: %s: link "N Tagen" to the Limits tab */ esc_html__('Einträge werden nach %s automatisch gelöscht.', 'wp-lit-mcp'), sprintf( '%s', esc_url(SettingsPage::url('limits')), esc_html(sprintf( /* translators: %d: number of days */ _n('%d Tag', '%d Tagen', $days, 'wp-lit-mcp'), $days )) ) ) ); echo ''; $this->renderClearForm(); } /** * @param array{ability: string, user_id: int} $filters * @param array{result: string, date_from: string, date_to: string} $extra */ private function renderFilterControls(array $filters, array $extra): void { self::filterLabel('ability', __('Nach Tool filtern', 'wp-lit-mcp')); echo ''; self::filterLabel('user-id', __('Nach Benutzer filtern', 'wp-lit-mcp')); wp_dropdown_users([ 'name' => 'user_id', 'id' => 'lit-mcp-filter-user-id', 'show_option_all' => __('Alle Benutzer', 'wp-lit-mcp'), 'selected' => $filters['user_id'], ]); self::filterLabel('result', __('Nach Ergebnis filtern', 'wp-lit-mcp')); echo ''; self::filterLabel('date-from', __('Von Datum', 'wp-lit-mcp')); printf( '', esc_attr($extra['date_from']) ); self::filterLabel('date-to', __('Bis Datum', 'wp-lit-mcp')); printf( '', esc_attr($extra['date_to']) ); submit_button(__('Filtern', 'wp-lit-mcp'), '', 'filter_action', false, ['id' => 'lit-mcp-filter-submit']); } private static function filterLabel(string $field, string $text): void { printf( '', esc_attr($field), esc_html($text) ); } private function renderClearForm(): void { printf(''; } public function handleClear(): void { if (! current_user_can('manage_options')) { wp_die(esc_html__('Keine Berechtigung.', 'wp-lit-mcp'), '', ['response' => 403]); } check_admin_referer(self::CLEAR_ACTION); $this->log->clear(); wp_safe_redirect(SettingsPage::url($this->slug(), ['lit_mcp_notice' => 'cleared'])); ($this->terminate)(); } }