event_id = intval( $event_id ); } /** * Check if the event is valid. * * @return bool Whether the event exists and is valid. */ public function is_valid_event() { if ( empty( $this->event_id ) ) { return false; } $event = get_post( $this->event_id ); return ( $event && $event->post_type === 'tribe_events' ); } /** * Check if the current user can view and email attendees for this event. * * @return bool Whether the user can view and email attendees. */ public function user_can_email_attendees() { if ( ! is_user_logged_in() ) { return false; } $event = get_post( $this->event_id ); if ( ! $event ) { return false; } // Allow event author or admins with edit_posts capability return ( get_current_user_id() === (int) $event->post_author || current_user_can( 'edit_posts' ) ); } /** * Get all attendees for the event. * * @return array Array of attendee data. */ public function get_attendees() { if ( ! $this->is_valid_event() ) { return array(); } $processed_attendees = array(); // First try using The Events Calendar's function if (function_exists('tribe_tickets_get_attendees')) { $attendees = tribe_tickets_get_attendees( $this->event_id ); if ( ! empty( $attendees ) ) { foreach ( $attendees as $attendee ) { $email = isset( $attendee['holder_email'] ) ? $attendee['holder_email'] : ''; if (empty($email) && isset($attendee['purchaser_email'])) { $email = $attendee['purchaser_email']; } $name = isset( $attendee['holder_name'] ) ? $attendee['holder_name'] : ''; if (empty($name) && isset($attendee['purchaser_name'])) { $name = $attendee['purchaser_name']; } $ticket_name = isset( $attendee['ticket_name'] ) ? $attendee['ticket_name'] : ''; // Only include attendees with valid emails if ( ! empty( $email ) && is_email( $email ) ) { $processed_attendees[] = array( 'name' => $name, 'email' => $email, 'ticket_name' => $ticket_name, 'attendee_id' => isset( $attendee['attendee_id'] ) ? $attendee['attendee_id'] : 0, 'order_id' => isset( $attendee['order_id'] ) ? $attendee['order_id'] : 0, ); } } } } // If no attendees found or function doesn't exist, fall back to direct query if (empty($processed_attendees)) { $processed_attendees = $this->get_attendees_fallback(); } return $processed_attendees; } /** * Fallback method to get attendees directly from the database * * @return array Array of attendee data */ private function get_attendees_fallback() { $processed_attendees = array(); // Query for attendees directly from the database $attendees_query = new WP_Query([ 'post_type' => 'tribe_tpp_attendees', 'posts_per_page' => -1, 'meta_query' => [ [ 'key' => '_tribe_tpp_event', 'value' => $this->event_id, 'compare' => '=', ], ], ]); if ($attendees_query->have_posts()) { while ($attendees_query->have_posts()) { $attendees_query->the_post(); $attendee_id = get_the_ID(); // Get associated ticket $ticket_id = get_post_meta($attendee_id, '_tribe_tpp_product', true); $ticket_name = $ticket_id ? get_the_title($ticket_id) : 'General Admission'; // Get purchaser details $name = get_post_meta($attendee_id, '_tribe_tickets_full_name', true); if (empty($name)) { $name = get_post_meta($attendee_id, '_tribe_tpp_full_name', true); } if (empty($name)) { $name = get_the_title($attendee_id); } $email = get_post_meta($attendee_id, '_tribe_tickets_email', true); if (empty($email)) { $email = get_post_meta($attendee_id, '_tribe_tpp_email', true); } // Get order info $order_id = get_post_meta($attendee_id, '_tribe_tpp_order', true); // Only include attendees with valid emails if (!empty($email) && is_email($email)) { $processed_attendees[] = array( 'name' => $name, 'email' => $email, 'ticket_name' => $ticket_name, 'attendee_id' => $attendee_id, 'order_id' => $order_id, ); } } wp_reset_postdata(); } return $processed_attendees; } /** * Get attendees filtered by ticket type. * * @param string $ticket_type The ticket type to filter by. * @return array Filtered attendees. */ public function get_attendees_by_ticket_type( $ticket_type ) { $attendees = $this->get_attendees(); if ( empty( $ticket_type ) ) { return $attendees; } return array_filter( $attendees, function( $attendee ) use ( $ticket_type ) { return $attendee['ticket_name'] === $ticket_type; }); } /** * Get all ticket types for the event. * * @return array Array of ticket types. */ public function get_ticket_types() { $attendees = $this->get_attendees(); $ticket_types = array(); foreach ( $attendees as $attendee ) { if ( ! empty( $attendee['ticket_name'] ) && ! in_array( $attendee['ticket_name'], $ticket_types ) ) { $ticket_types[] = $attendee['ticket_name']; } } return $ticket_types; } /** * Get the event details. * * @return array Event details. */ public function get_event_details() { if ( ! $this->is_valid_event() ) { return array(); } $event = get_post( $this->event_id ); return array( 'id' => $this->event_id, 'title' => get_the_title( $event ), 'start_date' => tribe_get_start_date( $event, false, 'F j, Y' ), 'start_time' => tribe_get_start_date( $event, false, 'g:i a' ), 'end_date' => tribe_get_end_date( $event, false, 'F j, Y' ), 'end_time' => tribe_get_end_date( $event, false, 'g:i a' ), ); } /** * Send email to attendees. * * @param array $recipients Array of recipient emails or attendee IDs. * @param string $subject The email subject. * @param string $message The email message. * @param string $cc Optional CC email addresses. * @return array Result with status and message. */ public function send_email( $recipients, $subject, $message, $cc = '' ) { // Start debug log $debug_log = "=== Email Sending Debug ===\n"; if ( empty( $recipients ) || empty( $subject ) || empty( $message ) ) { $debug_log .= "Error: Missing required fields\n"; if (class_exists('HVAC_Logger')) { HVAC_Logger::error('Email sending failed: Missing required fields', 'Email System'); } return array( 'success' => false, 'message' => 'Missing required fields (recipients, subject, or message).', ); } if ( ! $this->is_valid_event() || ! $this->user_can_email_attendees() ) { $debug_log .= "Error: Permission denied\n"; if (class_exists('HVAC_Logger')) { HVAC_Logger::error('Email sending failed: Permission denied', 'Email System'); } return array( 'success' => false, 'message' => 'You do not have permission to email attendees for this event.', ); } $headers = array('Content-Type: text/html; charset=UTF-8'); $event_details = $this->get_event_details(); $event_title = $event_details['title']; $debug_log .= "Event: {$event_title} (ID: {$this->event_id})\n"; // Add CC if provided if ( ! empty( $cc ) ) { $cc_emails = explode( ',', $cc ); foreach ( $cc_emails as $cc_email ) { $cc_email = trim( $cc_email ); if ( is_email( $cc_email ) ) { $headers[] = 'Cc: ' . $cc_email; $debug_log .= "Added CC: {$cc_email}\n"; } } } // Add sender information from the logged-in trainer $current_user = wp_get_current_user(); // Get trainer profile data if available $trainer_name = $current_user->display_name; $trainer_email = $current_user->user_email; // Check if user is a trainer and has profile data if (in_array('hvac_trainer', $current_user->roles)) { // Try to get trainer business name first $business_name = get_user_meta($current_user->ID, 'business_name', true); if (!empty($business_name)) { $trainer_name = $business_name; } // Try to get trainer contact email if different $contact_email = get_user_meta($current_user->ID, 'contact_email', true); if (!empty($contact_email) && is_email($contact_email)) { $trainer_email = $contact_email; } } $from_name = $trainer_name; $from_email = $trainer_email; $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>'; $debug_log .= "From: {$from_name} <{$from_email}>\n"; $debug_log .= "User role: " . implode(', ', $current_user->roles) . "\n"; // Process recipients $all_attendees = $this->get_attendees(); $debug_log .= "Total attendees found: " . count($all_attendees) . "\n"; $attendee_emails = array(); $sent_count = 0; $error_count = 0; $debug_log .= "Recipients provided: " . count($recipients) . "\n"; // Handle numeric IDs or email addresses foreach ( $recipients as $recipient ) { $debug_log .= "Processing recipient: {$recipient}\n"; if ( is_numeric( $recipient ) ) { $debug_log .= "Recipient is numeric ID\n"; // Find attendee by ID foreach ( $all_attendees as $attendee ) { if ( $attendee['attendee_id'] == $recipient ) { $attendee_emails[$attendee['email']] = $attendee['name']; $debug_log .= "Matched with attendee: {$attendee['name']} <{$attendee['email']}>\n"; break; } } } elseif ( is_email( $recipient ) ) { $debug_log .= "Recipient is email address\n"; // Add directly if it's an email $attendee_name = ''; foreach ( $all_attendees as $attendee ) { if ( $attendee['email'] === $recipient ) { $attendee_name = $attendee['name']; $debug_log .= "Matched with attendee name: {$attendee_name}\n"; break; } } $attendee_emails[$recipient] = $attendee_name; } else { $debug_log .= "Invalid recipient format\n"; } } $debug_log .= "Recipients to email: " . count($attendee_emails) . "\n"; if (empty($attendee_emails)) { $debug_log .= "No valid recipients found! Using fallback to direct send.\n"; // Fallback - directly use the first selected email foreach ($recipients as $recipient) { if (is_email($recipient)) { $attendee_emails[$recipient] = ''; $debug_log .= "Added direct recipient: {$recipient}\n"; break; } } } // Subject with event title $email_subject = sprintf( '[%s] %s', $event_title, $subject ); $debug_log .= "Email subject: {$email_subject}\n"; // Send to each recipient individually for personalization foreach ( $attendee_emails as $email => $name ) { $debug_log .= "Sending to: {$email}\n"; // Personalize message with attendee name if available $personalized_message = $message; if ( ! empty( $name ) ) { $personalized_message = "Hello " . $name . ",\n\n" . $message; $debug_log .= "Personalized with name: {$name}\n"; } // Log complete mail params for debugging $debug_log .= "Mail parameters:\n"; $debug_log .= "To: {$email}\n"; $debug_log .= "Subject: {$email_subject}\n"; $debug_log .= "Headers: " . print_r($headers, true) . "\n"; // Note: consolidated error logging is added below // Add detailed logging $debug_log .= "Headers: " . print_r($headers, true) . "\n"; $debug_log .= "Sending mail with wp_mail()\n"; // Add robust error logging add_action('wp_mail_failed', function($wp_error) use (&$debug_log) { $debug_log .= "Mail error: " . $wp_error->get_error_message() . "\n"; $debug_log .= "Error data: " . print_r($wp_error->get_error_data(), true) . "\n"; if (class_exists('HVAC_Logger')) { HVAC_Logger::error('WordPress Mail Error: ' . $wp_error->get_error_message() . ' - ' . print_r($wp_error->get_error_data(), true), 'Email System'); } }); // Try to log environment information $debug_log .= "Mail environment:\n"; $debug_log .= "WordPress version: " . get_bloginfo('version') . "\n"; if (function_exists('phpversion')) { $debug_log .= "PHP version: " . phpversion() . "\n"; } // Check if WP Mail SMTP is active $active_plugins = get_option('active_plugins', array()); $wp_mail_smtp_active = false; foreach ($active_plugins as $plugin) { if (strpos($plugin, 'wp-mail-smtp') !== false) { $wp_mail_smtp_active = true; $debug_log .= "WP Mail SMTP plugin is active\n"; break; } } // Send with standard wp_mail $mail_sent = wp_mail($email, $email_subject, wpautop($personalized_message), $headers); $debug_log .= "wp_mail result: " . ($mail_sent ? 'Success' : 'Failed') . "\n"; if ( $mail_sent ) { $sent_count++; } else { $error_count++; } } // Log the complete debug information if (class_exists('HVAC_Logger')) { HVAC_Logger::info($debug_log, 'Email System'); } // Return results if ( $error_count > 0 ) { return array( 'success' => $sent_count > 0, 'message' => sprintf( 'Email sent to %d recipients. Failed to send to %d recipients.', $sent_count, $error_count ), ); } return array( 'success' => true, 'message' => sprintf( 'Email successfully sent to %d recipients.', $sent_count ), ); } }