*/ private const TYPES = [ 'not_found' => 'Prodotto non trovato', 'incomplete_data' => 'Dati incompleti', 'wrong_data' => 'Dati errati', 'wrong_images' => 'Immagini mancanti o errate', 'provider_problem' => 'Problema con un provider', 'import_problem' => 'Problema nell\'importazione', 'other' => 'Altro', ]; public static function register(): void { if (function_exists('add_action')) { add_action('admin_post_tfsi_support_report', [self::class, 'handle']); } } /** @return array */ public static function issueTypes(): array { if (! function_exists('__')) { return self::TYPES; } return [ 'not_found' => __('Prodotto non trovato', 'productgather'), 'incomplete_data' => __('Dati incompleti', 'productgather'), 'wrong_data' => __('Dati errati', 'productgather'), 'wrong_images' => __('Immagini mancanti o errate', 'productgather'), 'provider_problem' => __('Problema con un provider', 'productgather'), 'import_problem' => __('Problema nell\'importazione', 'productgather'), 'other' => __('Altro', 'productgather'), ]; } public static function handle(): void { if (! current_user_can('manage_options')) { $result = ['status' => 'forbidden', 'accepted' => false]; } elseif (! isset($_POST['tfsi_support_nonce']) || ! is_string($_POST['tfsi_support_nonce']) || false === wp_verify_nonce( sanitize_text_field(wp_unslash($_POST['tfsi_support_nonce'])), self::ACTION )) { $result = ['status' => 'invalid', 'accepted' => false]; } else { // submit() retains its independent capability/nonce checks and // field-specific text, textarea, email and diagnostic allowlists. $result = self::submit(wp_unslash($_POST)); } $url = function_exists('admin_url') ? admin_url('admin.php?page=tfsi_support') : 'admin.php?page=tfsi_support'; if (function_exists('add_query_arg')) { $url = add_query_arg('tfsi_support_status', (string) ($result['status'] ?? 'failed'), $url); } else { $url .= '&tfsi_support_status=' . rawurlencode((string) ($result['status'] ?? 'failed')); } $url .= '#tfsi-support-report'; if (function_exists('wp_safe_redirect')) { wp_safe_redirect($url); exit; } } /** * @param array $post * @return array{status:string,accepted:bool} */ public static function submit(array $post): array { if (! function_exists('current_user_can') || ! current_user_can('manage_options')) { return ['status' => 'forbidden', 'accepted' => false]; } $nonce = self::text($post[self::NONCE_FIELD] ?? ''); if ( $nonce === '' || ! function_exists('wp_verify_nonce') || wp_verify_nonce($nonce, self::ACTION) === false ) { return ['status' => 'invalid', 'accepted' => false]; } $rateKey = self::rateLimitKey(); if ($rateKey !== '' && function_exists('get_transient') && get_transient($rateKey)) { return ['status' => 'rate_limited', 'accepted' => false]; } $type = self::key($post['support_type'] ?? ''); if (! isset(self::TYPES[$type])) { $type = 'other'; } $description = self::textarea($post['support_description'] ?? ''); $replyEmail = self::email($post['support_email'] ?? ''); $query = self::text($post['support_query'] ?? ''); $includeDiagnostics = ! empty($post['support_include_diagnostics']); $lines = [ __('ProductGather Free — segnalazione volontaria', 'productgather'), '', __('Tipo: ', 'productgather') . \TFGraphics\SmartImport\Admin\Presentation\UiTranslations::translate(self::TYPES[$type]), ]; if ($description !== '') { $lines[] = __('Nota facoltativa:', 'productgather'); $lines[] = $description; } if ($query !== '') { $lines[] = ''; $lines[] = __('Ricerca interessata (mostrata all’utente prima dell’invio):', 'productgather'); $lines[] = $query; } if ($includeDiagnostics) { $lines[] = ''; $lines[] = __('Diagnostica tecnica autorizzata dall’utente:', 'productgather'); foreach (self::diagnostics($post) as $label => $value) { if ($value !== '') { $lines[] = \TFGraphics\SmartImport\Admin\Presentation\UiTranslations::translate($label) . ': ' . $value; } } } $subject = '[ProductGather Free] ' . \TFGraphics\SmartImport\Admin\Presentation\UiTranslations::translate(self::TYPES[$type]); $headers = []; if ($replyEmail !== '') { $headers[] = 'Reply-To: ' . $replyEmail; } $accepted = function_exists('wp_mail') ? (bool) wp_mail(self::recipient(), $subject, implode("\n", $lines), $headers) : false; if ($rateKey !== '' && function_exists('set_transient')) { set_transient($rateKey, 1, self::RATE_LIMIT_SECONDS); } return ['status' => $accepted ? 'sent' : 'failed', 'accepted' => $accepted]; } public static function recipient(): string { $recipient = self::RECIPIENT; if (function_exists('apply_filters')) { $recipient = (string) apply_filters('tfsi_support_recipient', $recipient); } return self::email($recipient); } /** * Only a strict allowlist is accepted. Secrets, cookies, tokens, nonces, * sessions, customer/order data and arbitrary headers are never read. * * @param array $post * @return array */ public static function diagnostics(array $post): array { $wooVersion = defined('WC_VERSION') ? (string) WC_VERSION : ''; if ($wooVersion === '' && function_exists('WC')) { try { $woo = WC(); if (is_object($woo) && isset($woo->version)) { $wooVersion = (string) $woo->version; } } catch (\Throwable) { $wooVersion = ''; } } return [ 'Plugin version' => defined('TFSI_VERSION') ? (string) TFSI_VERSION : '', 'Build' => defined('TFSI_BUILD') ? (string) TFSI_BUILD : '', 'WordPress' => function_exists('get_bloginfo') ? (string) get_bloginfo('version') : '', 'WooCommerce' => $wooVersion, 'PHP' => PHP_VERSION, 'Locale' => function_exists('determine_locale') ? (string) determine_locale() : '', 'Area' => self::text($post['support_area'] ?? ''), 'Trace ID' => self::text($post['support_trace_id'] ?? ''), 'Tipo ricerca' => self::text($post['support_search_type'] ?? ''), 'Provider tentati' => self::text($post['support_providers'] ?? ''), 'Stato provider' => self::text($post['support_provider_status'] ?? ''), 'Codici errore' => self::text($post['support_error_codes'] ?? ''), 'Tempi essenziali' => self::text($post['support_timings'] ?? ''), ]; } private static function rateLimitKey(): string { if (! function_exists('get_current_user_id')) { return ''; } $userId = (int) get_current_user_id(); return $userId > 0 ? 'tfsi_support_report_' . $userId : ''; } private static function text(mixed $value): string { $value = is_scalar($value) ? (string) $value : ''; if (function_exists('sanitize_text_field')) { return trim((string) sanitize_text_field($value)); } return trim((string) preg_replace('/[\x00-\x1F\x7F]/u', '', $value)); } private static function textarea(mixed $value): string { $value = is_scalar($value) ? (string) $value : ''; if (function_exists('sanitize_textarea_field')) { return trim((string) sanitize_textarea_field($value)); } return trim((string) preg_replace('/<[^>]*>/', '', $value)); } private static function email(mixed $value): string { $value = is_scalar($value) ? trim((string) $value) : ''; if ($value === '') { return ''; } if (function_exists('sanitize_email')) { return (string) sanitize_email($value); } return filter_var($value, FILTER_VALIDATE_EMAIL) ? $value : ''; } private static function key(mixed $value): string { $value = is_scalar($value) ? (string) $value : ''; if (function_exists('sanitize_key')) { return (string) sanitize_key($value); } return strtolower((string) preg_replace('/[^a-z0-9_\-]/i', '', $value)); } }