PluginProbe
404 Solution / 4.3.0
404 Solution v4.3.0
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / feedback / FeedbackEmailFallback.php

FeedbackEmailFallback.php in 404 Solution 4.3.0, at includes/feedback/FeedbackEmailFallback.php

125 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Last-resort wp_mail() transport for feedback reports. Used when the HTTP
9 * POST in FeedbackHttpClient fails. Routes to type-specific email body
10 * builders so the email and HTTP transports share a single source of truth
11 * for what the developer receives.
12 *
13 * - 'uninstall' delegates to UninstallModal::sendFeedbackEmail
14 * - 'error'/'heartbeat' delegates to Logging::emailLogFileToDeveloper
15 * - 'support_request' built inline (user message + reply email + standard
16 * diagnostic block + log excerpt)
17 *
18 * Also owns the development-environment detection that FeedbackPayloadBuilder
19 * uses to tag local reports.
20 */
21 class ABJ_404_Solution_FeedbackEmailFallback {
22
23 /**
24 * Send the payload via wp_mail(), routing to the type-specific body
25 * builder. Returns true on send success.
26 *
27 * @param array<string, mixed> $payload
28 * @param string $type
29 * @return bool
30 */
31 public static function send(array $payload, string $type): bool {
32 if (!function_exists('wp_mail')) {
33 return false;
34 }
35 if ($type === 'uninstall' && class_exists('ABJ_404_Solution_UninstallModal')) {
36 return ABJ_404_Solution_UninstallModal::sendFeedbackEmail($payload);
37 }
38 if ($type === 'support_request') {
39 return self::supportRequest($payload);
40 }
41 if (($type === 'error' || $type === 'heartbeat') && function_exists('abj_service_optional')) {
42 try {
43 $logger = abj_service_optional('logging');
44 if (is_object($logger) && method_exists($logger, 'emailLogFileToDeveloper')) {
45 return (bool) $logger->emailLogFileToDeveloper($payload);
46 }
47 } catch (\Throwable $e) {
48 ABJ_404_Solution_FeedbackTransportLog::log('warn', 'FeedbackEmailFallback delegate (' . $type . ') failed: ' . $e->getMessage());
49 }
50 }
51 $to = defined('ABJ404_AUTHOR_EMAIL') ? ABJ404_AUTHOR_EMAIL : '404solution@ajexperience.com';
52 $version = defined('ABJ404_VERSION') ? ABJ404_VERSION : '';
53 $subject = sprintf('[404 Solution] %s report (HTTP fallback) v%s', $type, $version);
54 $json = function_exists('wp_json_encode') ? wp_json_encode($payload, JSON_PRETTY_PRINT) : json_encode($payload, JSON_PRETTY_PRINT);
55 $body = is_string($json) ? $json : '';
56 $headers = array('Content-Type: text/plain; charset=UTF-8');
57 $result = wp_mail($to, $subject, $body, $headers);
58 return (bool)$result;
59 }
60
61 /**
62 * Build the wp_mail() body for type='support_request' fallbacks. The
63 * user-facing fields (their message and reply address) lead the body so
64 * a reviewer can act on the request without scrolling past the
65 * diagnostic block. The standard payload dump and any captured log
66 * excerpt follow as appendices.
67 *
68 * @param array<string, mixed> $payload
69 * @return bool
70 */
71 private static function supportRequest(array $payload): bool {
72 $to = defined('ABJ404_AUTHOR_EMAIL') ? ABJ404_AUTHOR_EMAIL : '404solution@ajexperience.com';
73 $version = defined('ABJ404_VERSION') ? ABJ404_VERSION : '';
74 $subject = sprintf('[404 Solution] Support request v%s', $version);
75
76 $userMessage = isset($payload['user_message']) && is_scalar($payload['user_message'])
77 ? (string)$payload['user_message'] : '';
78 $replyEmail = isset($payload['reply_email']) && is_scalar($payload['reply_email'])
79 ? (string)$payload['reply_email'] : '';
80 $triggeredFrom = isset($payload['triggered_from']) && is_scalar($payload['triggered_from'])
81 ? (string)$payload['triggered_from'] : '';
82 $logExcerpt = isset($payload['debug_log_excerpt']) && is_scalar($payload['debug_log_excerpt'])
83 ? (string)$payload['debug_log_excerpt'] : '';
84
85 $bodyLines = array();
86 $bodyLines[] = '=== USER SUPPORT REQUEST ===';
87 $bodyLines[] = '';
88 $bodyLines[] = 'Reply-To: ' . ($replyEmail !== '' ? $replyEmail : '(not provided)');
89 $bodyLines[] = 'Triggered from: ' . ($triggeredFrom !== '' ? $triggeredFrom : '(unknown)');
90 $bodyLines[] = '';
91 $bodyLines[] = '--- User message ---';
92 $bodyLines[] = $userMessage !== '' ? $userMessage : '(no message provided)';
93 $bodyLines[] = '';
94 $bodyLines[] = '=== DIAGNOSTICS ===';
95 $bodyLines[] = '';
96 $scalar = static function ($v, string $fallback = ''): string {
97 return is_scalar($v) ? (string)$v : $fallback;
98 };
99 $bodyLines[] = 'Plugin version: ' . $scalar($payload['plugin_version'] ?? null);
100 $bodyLines[] = 'PHP version: ' . $scalar($payload['php_version'] ?? null, PHP_VERSION);
101 $bodyLines[] = 'WP version: ' . $scalar($payload['wp_version'] ?? null);
102 $bodyLines[] = 'DB: ' . $scalar($payload['db_type'] ?? null) . ' ' . $scalar($payload['db_version'] ?? null);
103 $bodyLines[] = 'Site URL: ' . $scalar($payload['site_url'] ?? null);
104 $bodyLines[] = 'Multisite: ' . (!empty($payload['is_multisite']) ? 'yes' : 'no');
105 $bodyLines[] = '';
106 if ($logExcerpt !== '') {
107 $bodyLines[] = '=== DEBUG LOG EXCERPT ===';
108 $bodyLines[] = '';
109 $bodyLines[] = $logExcerpt;
110 $bodyLines[] = '';
111 }
112 $bodyLines[] = '=== FULL PAYLOAD (JSON) ===';
113 $json = function_exists('wp_json_encode') ? wp_json_encode($payload, JSON_PRETTY_PRINT) : json_encode($payload, JSON_PRETTY_PRINT);
114 $bodyLines[] = is_string($json) ? $json : '';
115
116 $body = implode("\n", $bodyLines);
117 $headers = array('Content-Type: text/plain; charset=UTF-8');
118 if ($replyEmail !== '') {
119 $headers[] = 'Reply-To: ' . $replyEmail;
120 }
121 $result = wp_mail($to, $subject, $body, $headers);
122 return (bool)$result;
123 }
124 }
125