[''.esc_html__('Settings', 'kommentti').'', ...$links]); } public function menu(): void { add_options_page('Kommentti', 'Kommentti', 'manage_options', 'kommentti', [$this, 'page']); } public function assets(string $hook): void { if ($hook === 'settings_page_kommentti') { wp_enqueue_style('kommentti-admin', plugins_url('assets/admin.css', KOMMENTTI_PLUGIN_FILE), [], KOMMENTTI_VERSION); wp_enqueue_script('kommentti-admin', plugins_url('assets/admin.js', KOMMENTTI_PLUGIN_FILE), [], KOMMENTTI_VERSION, true); wp_localize_script('kommentti-admin', 'KommenttiAdmin', ['copied' => __('Copied', 'kommentti'), 'copyFailed' => __('Select and copy the value.', 'kommentti')]); } } private function authorize(string $action): void { if (!current_user_can('manage_options')) { wp_die(esc_html__('Administrator permission is required.', 'kommentti'), '', ['response' => 403]); } check_admin_referer($action); } private function redirect(string $message): never { set_transient('kommentti_notice_'.get_current_user_id(), $message, 60); wp_safe_redirect(admin_url('options-general.php?page=kommentti')); exit; } public function save(): never { $this->authorize('kommentti_save'); try { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- authorize() validates both manage_options and the action nonce above. $this->settings->save(wp_unslash($_POST)); $message = __('Settings saved. Use Test connection to verify the site and API.', 'kommentti'); } catch (Throwable $exception) { $message = $exception instanceof \InvalidArgumentException || $exception instanceof \RuntimeException ? $exception->getMessage() : __('The settings could not be saved.', 'kommentti'); } $this->redirect($message); } public function check(): never { $this->authorize('kommentti_check'); $this->api->diagnose(); $this->redirect(__('Connection test completed. Review the status below.', 'kommentti')); } public function clear_cache(): never { $this->authorize('kommentti_clear_cache'); update_option('kommentti_cache_generation', wp_generate_uuid4(), false); delete_transient('kommentti_api_backoff'); do_action('kommentti_purge_site'); $this->redirect(__('Comment cache cleared. Clear your page or CDN cache separately if needed.', 'kommentti')); } public function page(): void { if (!current_user_can('manage_options')) { return; } $settings = $this->settings->all(); $status = get_transient('kommentti_connection_status') ?: []; $apiStatus = get_transient('kommentti_api_status') ?: []; if (($apiStatus['checked_at'] ?? 0) >= ($status['checked_at'] ?? 0) && isset($apiStatus['code'])) { $status['api_code'] = $apiStatus['code']; } $webhook = get_option('kommentti_webhook_status', []); $noticeKey = 'kommentti_notice_'.get_current_user_id(); $notice = get_transient($noticeKey); delete_transient($noticeKey); $locale = $this->settings->locale(); $portal = Settings::WEB_URL.($locale === 'en' ? '' : '/'.$locale).'/developers'; $counts = wp_count_comments(); require KOMMENTTI_PLUGIN_DIR.'/templates/settings.php'; } public function meta_boxes(): void { if (current_user_can('manage_options')) { foreach ($this->settings->all()['post_types'] as $postType) { add_meta_box('kommentti-thread', __('Kommentti discussion', 'kommentti'), [$this, 'thread_box'], $postType, 'side'); } } } public function thread_box(WP_Post $post): void { wp_nonce_field('kommentti_thread_'.$post->ID, 'kommentti_thread_nonce'); echo '
'.esc_html__('The default discussion ID is the WordPress post ID. Change it only to connect an existing Kommentti discussion.', 'kommentti').'
'; echo ''; echo ''.esc_html__('Changing the ID connects a different discussion; it does not move or delete comments.', 'kommentti').'
'; } public function save_thread(int $postId, WP_Post $post): void { $nonce = is_string($_POST['kommentti_thread_nonce'] ?? null) ? sanitize_text_field(wp_unslash($_POST['kommentti_thread_nonce'])) : null; if (!current_user_can('manage_options') || !current_user_can('edit_post', $postId) || !is_string($nonce) || !wp_verify_nonce($nonce, 'kommentti_thread_'.$postId) || wp_is_post_autosave($postId) || wp_is_post_revision($postId) || !is_string($_POST['kommentti_thread_id'] ?? null)) { return; } $thread = sanitize_text_field(wp_unslash($_POST['kommentti_thread_id'])); if ($thread === '') { delete_post_meta($postId, '_kommentti_thread_id'); } elseif (strlen($thread) <= 200 && !preg_match('/[\x00-\x1F\x7F]/', $thread)) { update_post_meta($postId, '_kommentti_thread_id', $thread); } else { return; } update_post_meta($postId, '_kommentti_cache_generation', wp_generate_uuid4()); } }