settings->connected() && $this->settings->moderation_key() !== '') { wp_enqueue_script('kommentti-moderation', Settings::API_URL.'/assets/moderation-embed.js', [], '1', ['in_footer' => true, 'strategy' => 'defer']); } } public function page(): void { if (!current_user_can('moderate_comments')) { return; } $settings = $this->settings->all(); $connected = $this->settings->connected(); $hasKey = $connected && $this->settings->moderation_key() !== ''; require KOMMENTTI_PLUGIN_DIR.'/templates/moderation.php'; } public function save(): never { if (!current_user_can('moderate_comments')) { wp_die(esc_html__('A connected site and comment moderation permission are required.', 'kommentti'), '', ['response' => 403]); } check_admin_referer('kommentti_moderation_save'); try { $key = is_string($_POST['moderation_key'] ?? null) ? sanitize_text_field(wp_unslash($_POST['moderation_key'])) : ''; $clear = is_string($_POST['clear_moderation_key'] ?? null) ? sanitize_text_field(wp_unslash($_POST['clear_moderation_key'])) : ''; $this->settings->save_moderation_key($key, $clear === '1'); $notice = __('Personal connection saved.', 'kommentti'); } catch (Throwable) { $notice = __('Check your personal key and site connection.', 'kommentti'); } set_transient('kommentti_moderation_notice_'.get_current_user_id(), $notice, 60); wp_safe_redirect(admin_url('edit-comments.php?page=kommentti-moderation')); exit; } public function grant(string $challenge): array { if (!current_user_can('moderate_comments') || !$this->settings->connected() || !preg_match('/^[A-Za-z0-9_-]{43}$/D', $challenge)) { throw new RuntimeException('denied'); } $origin = $this->settings->origin(); $admin = wp_parse_url(admin_url('/')); $adminOrigin = ($admin['scheme'] ?? '').'://'.($admin['host'] ?? '').(isset($admin['port']) ? ':'.$admin['port'] : ''); $key = $this->settings->moderation_key(); if ($adminOrigin !== $origin || !str_starts_with($origin, 'https://') || !preg_match('/^kmt_mod_[A-Za-z0-9]{64}$/D', $key)) { throw new RuntimeException('denied'); } $response = wp_safe_remote_post(Settings::API_URL.'/v1/moderation/sites/'.$this->settings->all()['site_id'].'/grants', [ 'timeout' => 8, 'redirection' => 0, 'sslverify' => true, 'limit_response_size' => 65536, 'headers' => ['Authorization' => 'Bearer '.$key, 'Accept' => 'application/json', 'Content-Type' => 'application/json'], 'body' => wp_json_encode(['origin' => $origin, 'challenge' => $challenge]), ]); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { throw new RuntimeException('denied'); } $grant = json_decode(wp_remote_retrieve_body($response), true); if (!is_array($grant) || !is_string($grant['code'] ?? null) || !preg_match('/^[A-Za-z0-9]{64}$/D', $grant['code'])) { throw new RuntimeException('invalid_grant'); } return ['code' => $grant['code'], 'expires_in' => 60]; } public function ajax(): never { if (!current_user_can('moderate_comments') || !check_ajax_referer('kommentti_moderation_grant', 'nonce', false)) { wp_send_json_error(['message' => __('A connected site and comment moderation permission are required.', 'kommentti')], 403); } try { $challenge = is_string($_POST['challenge'] ?? null) ? sanitize_text_field(wp_unslash($_POST['challenge'])) : ''; wp_send_json_success($this->grant($challenge)); } catch (Throwable) { wp_send_json_error(['message' => __('Check your personal key and site connection.', 'kommentti')], 403); } } }