upskill-event-manager/includes/community/class-email-attendees-data.php
bengizmo 37f4180e1c feat: Add massive missing plugin infrastructure to repository
🚨 CRITICAL: Fixed deployment blockers by adding missing core directories:

**Community System (CRITICAL)**
- includes/community/ - Login_Handler and all community classes
- templates/community/ - Community login forms

**Certificate System (CRITICAL)**
- includes/certificates/ - 8+ certificate classes and handlers
- templates/certificates/ - Certificate reports and generation templates

**Core Individual Classes (CRITICAL)**
- includes/class-hvac-event-summary.php
- includes/class-hvac-trainer-profile-manager.php
- includes/class-hvac-master-dashboard-data.php
- Plus 40+ other individual HVAC classes

**Major Feature Systems (HIGH)**
- includes/database/ - Training leads database tables
- includes/find-trainer/ - Find trainer directory and MapGeo integration
- includes/google-sheets/ - Google Sheets integration system
- includes/zoho/ - Complete Zoho CRM integration
- includes/communication/ - Communication templates system

**Template Infrastructure**
- templates/attendee/, templates/email-attendees/
- templates/event-summary/, templates/status/
- templates/template-parts/ - Shared template components

**Impact:**
- 70+ files added covering 10+ missing directories
- Resolves ALL deployment blockers and feature breakdowns
- Plugin activation should now work correctly
- Multi-machine deployment fully supported

🔧 Generated with Claude Code

Co-Authored-By: Ben Reed <ben@tealmaker.com>
2025-08-11 13:30:11 -03:00

470 lines
No EOL
17 KiB
PHP

<?php
/**
* HVAC Community Events - Email Attendees Data Class
*
* Handles retrieving attendee data and sending emails for the Email Attendees functionality.
*
* @package HVAC_Community_Events
* @subpackage Community
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class HVAC_Email_Attendees_Data
*
* Handles data operations for the Email Attendees functionality.
*/
class HVAC_Email_Attendees_Data {
/**
* The event ID.
*
* @var int
*/
private $event_id;
/**
* Constructor.
*
* @param int $event_id The event ID.
*/
public function __construct( $event_id = 0 ) {
$this->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 ),
);
}
}