wp_create_nonce('abj404_uninstall_nonce'), 'pluginSlug' => self::getPluginSlug(), 'redirectCount' => $redirectCount, 'i18n' => array( 'dialogTitle' => __('404 Solution - Deactivation Options', '404-solution'), 'btnCancel' => __('Cancel', '404-solution'), 'btnSkipFeedback' => __('Deactivate without feedback', '404-solution'), 'btnDeactivate' => __('Email Feedback & Deactivate', '404-solution'), 'btnSaving' => __('Processing...', '404-solution'), 'btnDeactivating' => __('Deactivating', '404-solution'), ) )); // Custom CSS for modal styling wp_add_inline_style('wp-jquery-ui-dialog', ' .abj404-uninstall-dialog .ui-dialog-titlebar { background: #d63638; color: white; } .abj404-uninstall-dialog .ui-dialog-titlebar-close { color: white; } .abj404-uninstall-dialog .ui-dialog-titlebar-close:hover { background: #b32d2e; } .abj404-uninstall-dialog .button-danger { background: #d63638; border-color: #d63638; color: white; } .abj404-uninstall-dialog .button-danger:hover { background: #b32d2e; border-color: #b32d2e; } .abj404-uninstall-content label { display: block; margin: 8px 0; cursor: pointer; } .abj404-uninstall-content label input[type="checkbox"], .abj404-uninstall-content label input[type="radio"] { margin-right: 8px; } .abj404-uninstall-content .description { margin: 0; color: #646970; font-size: 12px; } .abj404-uninstall-content h3 { margin-top: 18px; margin-bottom: 8px; border-bottom: 1px solid #dcdcde; padding-bottom: 6px; font-size: 14px; } .abj404-uninstall-content h3:first-child { margin-top: 0; } .abj404-uninstall-reasons { margin-left: 25px; } .abj404-uninstall-reasons label { margin: 6px 0; font-size: 13px; } .abj404-followup-section { margin: 12px 0 !important; padding: 12px !important; } .abj404-followup-section p { margin: 0 0 8px 0 !important; font-size: 13px !important; } .abj404-followup-section label { margin: 4px 0 !important; font-size: 13px !important; } '); // Output modal HTML in footer add_action('admin_footer', array(__CLASS__, 'outputModalHTML')); } /** * Output the modal HTML structure */ public static function outputModalHTML() { $redirectCount = self::getRedirectCount(); ?> __('Insufficient permissions', '404-solution'))); return; } // Get preferences from AJAX request // Use filter_var to properly handle boolean values sent from JavaScript $preferences = array( 'delete_redirects' => isset($_POST['delete_redirects']) ? filter_var($_POST['delete_redirects'], FILTER_VALIDATE_BOOLEAN) : false, 'delete_logs' => isset($_POST['delete_logs']) ? filter_var($_POST['delete_logs'], FILTER_VALIDATE_BOOLEAN) : false, 'delete_cache' => true, // Always delete cache tables 'send_feedback' => isset($_POST['send_feedback']) ? filter_var($_POST['send_feedback'], FILTER_VALIDATE_BOOLEAN) : false, 'uninstall_reason' => isset($_POST['uninstall_reason']) ? sanitize_text_field($_POST['uninstall_reason']) : '', 'selected_issues' => isset($_POST['selected_issues']) ? sanitize_text_field($_POST['selected_issues']) : '', 'followup_details' => isset($_POST['followup_details']) ? sanitize_textarea_field($_POST['followup_details']) : '', 'better_plugin_name' => isset($_POST['better_plugin_name']) ? sanitize_text_field($_POST['better_plugin_name']) : '', 'other_reason_text' => isset($_POST['other_reason_text']) ? sanitize_textarea_field($_POST['other_reason_text']) : '', 'feedback_email' => isset($_POST['feedback_email']) ? sanitize_email($_POST['feedback_email']) : '', 'include_diagnostics' => isset($_POST['include_diagnostics']) ? filter_var($_POST['include_diagnostics'], FILTER_VALIDATE_BOOLEAN) : false ); // Debug logging (only in debug mode to avoid logging PII like email/feedback in production) if (defined('WP_DEBUG') && WP_DEBUG) { error_log('404 Solution: AJAX handler received deactivation preferences'); error_log('404 Solution: Raw POST send_feedback = ' . (isset($_POST['send_feedback']) ? $_POST['send_feedback'] : 'NOT SET')); error_log('404 Solution: Parsed send_feedback = ' . ($preferences['send_feedback'] ? 'true' : 'false')); error_log('404 Solution: Parsed preferences: ' . print_r($preferences, true)); } // Save preferences using site options for multisite compatibility // In multisite, use site_option for network-activated plugins, regular option for single-site $option_name = 'abj404_uninstall_preferences'; // Capture return value to verify save success $save_result = false; if (is_multisite() && self::isNetworkActivated()) { // Network-activated: Use site option (accessible across all sites) $save_result = update_site_option($option_name, $preferences); } else { // Single-site or site-specific activation: Use regular option $save_result = update_option($option_name, $preferences, false); // autoload=false } // Verify the save was successful (false could mean unchanged OR failure) if ($save_result === false) { // Read back the option to verify it was actually saved $saved_value = is_multisite() && self::isNetworkActivated() ? get_site_option($option_name) : get_option($option_name); // If the saved value doesn't match what we tried to save, it's a real failure if ($saved_value !== $preferences) { wp_send_json_error(array( 'message' => __('Could not save preferences. Your choices may not be preserved.', '404-solution') )); return; } // If values match, the false return was just because value was unchanged (which is OK) } // Send feedback email only if user explicitly opted in $should_send_email = $preferences['send_feedback']; if (defined('WP_DEBUG') && WP_DEBUG) { error_log('404 Solution: send_feedback=' . ($preferences['send_feedback'] ? 'true' : 'false') . ', should_send_email=' . ($should_send_email ? 'true' : 'false')); } $email_sent = false; if ($should_send_email) { $email_sent = self::sendFeedbackEmail($preferences); } // Build success message if ($should_send_email) { // User sent feedback - show appropriate message $message = $email_sent ? __('Feedback sent successfully.', '404-solution') : __('Feedback could not be sent.', '404-solution'); } else { // User skipped feedback - minimal message (won't be shown anyway due to instant redirect) $message = ''; } // Return success (failures are already handled above) wp_send_json_success(array('message' => $message)); } /** * Check if plugin is network-activated * * @return bool True if network-activated, false otherwise */ private static function isNetworkActivated() { if (!is_multisite()) { return false; } if (!function_exists('is_plugin_active_for_network')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } return is_plugin_active_for_network(plugin_basename(ABJ404_FILE)); } /** * Get the plugin slug for JavaScript * * @return string Plugin directory slug */ private static function getPluginSlug() { // Get plugin directory name from plugin file path $pluginPath = plugin_basename(ABJ404_FILE); $parts = explode('/', $pluginPath); return $parts[0]; } /** * Get the count of redirects for display * * @return int Number of redirects */ private static function getRedirectCount() { global $wpdb; $dao = ABJ_404_Solution_DataAccess::getInstance(); $table_name = $dao->getPrefixedTableName('abj404_redirects'); // Check if table exists $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name)) === $table_name; if (!$table_exists) { return 0; } $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status != " . ABJ404_STATUS_TRASH); return $count ? intval($count) : 0; } /** * Send feedback email to plugin author * * @param array $preferences User preferences including feedback * @return bool True if email sent successfully, false otherwise */ private static function sendFeedbackEmail($preferences) { // Get site information global $wp_version; $site_name = get_bloginfo('name'); $admin_email = get_option('admin_email'); // Get plugin information $redirect_count = self::getRedirectCount(); // Gather system information (excluding site URL for privacy) $system_info = array( 'WordPress Version' => $wp_version, 'PHP Version' => phpversion(), 'Plugin Version' => defined('ABJ404_VERSION') ? ABJ404_VERSION : 'Unknown', 'Multisite' => is_multisite() ? 'Yes' : 'No', 'Active Plugins' => self::getActivePluginsList(), 'Redirect Count' => $redirect_count ); // Build email subject $subject = sprintf('[404 Solution] Deactivation Feedback from %s', $site_name); // Build email body $body = "Deactivation feedback received:\n\n"; $body .= "═══════════════════════════════════════\n"; $body .= "USER FEEDBACK\n"; $body .= "═══════════════════════════════════════\n\n"; if (!empty($preferences['uninstall_reason'])) { $body .= "Reason: " . ucfirst(str_replace('-', ' ', $preferences['uninstall_reason'])) . "\n\n"; } // Show selected issues (checkboxes) if (!empty($preferences['selected_issues'])) { $body .= "Specific Issues:\n"; $issues = explode(',', $preferences['selected_issues']); foreach ($issues as $issue) { $body .= " ☑ " . ucfirst(str_replace('-', ' ', $issue)) . "\n"; } $body .= "\n"; } // Show additional details from follow-up textarea if (!empty($preferences['followup_details'])) { $body .= "Additional Details:\n" . $preferences['followup_details'] . "\n\n"; } // Show better plugin name if provided if (!empty($preferences['better_plugin_name'])) { $body .= "Switching to: " . $preferences['better_plugin_name'] . "\n\n"; } // Show other reason details if provided if (!empty($preferences['other_reason_text'])) { $body .= "Other Reason Details:\n" . $preferences['other_reason_text'] . "\n\n"; } if (!empty($preferences['feedback_email'])) { $body .= "User Email: " . $preferences['feedback_email'] . "\n\n"; } // Include diagnostics if user opted in if (!empty($preferences['include_diagnostics'])) { // Plugin debug log excerpt $body .= "═══════════════════════════════════════\n"; $body .= "PLUGIN DEBUG LOG\n"; $body .= "═══════════════════════════════════════\n\n"; try { $logger = ABJ_404_Solution_Logging::getInstance(); $logExcerpt = $logger->getSanitizedLogExcerptForSupport(); $body .= $logExcerpt . "\n\n"; } catch (Exception $e) { $body .= "Unable to retrieve log excerpt\n\n"; } // System information $body .= "═══════════════════════════════════════\n"; $body .= "SYSTEM INFORMATION\n"; $body .= "═══════════════════════════════════════\n\n"; foreach ($system_info as $label => $value) { $body .= sprintf("%-20s: %s\n", $label, $value); } } $body .= "\n═══════════════════════════════════════\n"; $body .= "This feedback was sent automatically when the user deactivated the plugin.\n"; // Set email headers $headers = array( 'Content-Type: text/plain; charset=UTF-8', 'From: ' . $site_name . ' <' . $admin_email . '>' ); // Add reply-to if user provided email if (!empty($preferences['feedback_email'])) { $headers[] = 'Reply-To: ' . $preferences['feedback_email']; } // Send email to plugin author $to = defined('ABJ404_AUTHOR_EMAIL') ? ABJ404_AUTHOR_EMAIL : '404solution@ajexperience.com'; // Log email attempt (only in debug mode) if (defined('WP_DEBUG') && WP_DEBUG) { error_log('404 Solution: Attempting to send feedback email to ' . $to); error_log('404 Solution: Email subject: ' . $subject); error_log('404 Solution: Feedback checkbox was checked: send_feedback=' . ($preferences['send_feedback'] ? 'true' : 'false')); } // Hook to log wp_mail failures (only in debug mode) if (defined('WP_DEBUG') && WP_DEBUG) { add_action('wp_mail_failed', function($error) { error_log('404 Solution: wp_mail() FAILED - ' . $error->get_error_message()); }); } $result = wp_mail($to, $subject, $body, $headers); // Log result (only in debug mode) if (defined('WP_DEBUG') && WP_DEBUG) { if ($result) { error_log('404 Solution: wp_mail() returned TRUE - email sent successfully'); } else { error_log('404 Solution: wp_mail() returned FALSE - email send failed'); } } return $result; } /** * Get list of active plugins * * @return string Comma-separated list of active plugin names */ private static function getActivePluginsList() { if (!function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $all_plugins = get_plugins(); $active_plugins = get_option('active_plugins', array()); $active_plugin_names = array(); foreach ($active_plugins as $plugin_path) { if (isset($all_plugins[$plugin_path])) { $active_plugin_names[] = $all_plugins[$plugin_path]['Name']; } } return !empty($active_plugin_names) ? implode(', ', array_slice($active_plugin_names, 0, 10)) . (count($active_plugin_names) > 10 ? '...' : '') : 'None'; } }