get_unit_multiplier( $trigger_unit ); if ( ! $seconds_multiplier ) { return null; } $offset_seconds = $trigger_value * $seconds_multiplier; switch ( $schedule['trigger_type'] ) { case 'before_event': $trigger_timestamp = $event_timestamp - $offset_seconds; break; case 'after_event': // Use event end date if available, otherwise start date $event_end_date = get_post_meta( $schedule['event_id'], '_EventEndDate', true ); $end_timestamp = $event_end_date ? strtotime( $event_end_date ) : $event_timestamp; $trigger_timestamp = $end_timestamp + $offset_seconds; break; case 'on_registration': // Immediate trigger - return current time return current_time( 'mysql' ); case 'custom_date': // Custom date should be provided in schedule data return isset( $schedule['custom_date'] ) ? $schedule['custom_date'] : null; default: return null; } // Ensure trigger time is in the future if ( $trigger_timestamp <= time() ) { return null; } return date( 'Y-m-d H:i:s', $trigger_timestamp ); } /** * Get unit multiplier for converting to seconds * * @param string $unit Time unit * @return int|false Multiplier or false if invalid */ private function get_unit_multiplier( $unit ) { $multipliers = array( 'minutes' => MINUTE_IN_SECONDS, 'hours' => HOUR_IN_SECONDS, 'days' => DAY_IN_SECONDS, 'weeks' => WEEK_IN_SECONDS ); return isset( $multipliers[$unit] ) ? $multipliers[$unit] : false; } /** * Get recipients for a schedule based on target audience settings * * @param array $schedule Schedule configuration * @return array Array of recipient data */ public function get_schedule_recipients( $schedule ) { $recipients = array(); switch ( $schedule['target_audience'] ) { case 'all_attendees': $recipients = $this->get_all_event_attendees( $schedule['event_id'] ); break; case 'confirmed_attendees': $recipients = $this->get_confirmed_attendees( $schedule['event_id'] ); break; case 'pending_attendees': $recipients = $this->get_pending_attendees( $schedule['event_id'] ); break; case 'custom_list': $recipients = $this->parse_custom_recipient_list( $schedule['custom_recipient_list'] ); break; } // Apply additional conditions if specified if ( ! empty( $schedule['conditions'] ) ) { $recipients = $this->apply_recipient_conditions( $recipients, $schedule['conditions'] ); } return $recipients; } /** * Get all attendees for an event * * @param int $event_id Event ID * @return array Array of attendee data */ private function get_all_event_attendees( $event_id ) { if ( empty( $event_id ) ) { return array(); } // Use the Email Attendees Data class for consistent attendee retrieval $email_data = new HVAC_Email_Attendees_Data( $event_id ); $attendees = $email_data->get_attendees(); $recipients = array(); foreach ( $attendees as $attendee ) { $recipients[] = array( 'email' => $attendee['email'], 'name' => $attendee['name'], 'attendee_id' => $attendee['attendee_id'], 'ticket_name' => $attendee['ticket_name'], 'status' => 'confirmed' // Default status ); } return $recipients; } /** * Get confirmed attendees only * * @param int $event_id Event ID * @return array Array of confirmed attendee data */ private function get_confirmed_attendees( $event_id ) { $all_attendees = $this->get_all_event_attendees( $event_id ); // For now, treat all attendees as confirmed // This can be enhanced later based on ticket status if needed return array_filter( $all_attendees, function( $attendee ) { return $attendee['status'] === 'confirmed'; }); } /** * Get pending attendees only * * @param int $event_id Event ID * @return array Array of pending attendee data */ private function get_pending_attendees( $event_id ) { $all_attendees = $this->get_all_event_attendees( $event_id ); return array_filter( $all_attendees, function( $attendee ) { return $attendee['status'] === 'pending'; }); } /** * Parse custom recipient list from text input * * @param string $recipient_list Comma or line-separated email list * @return array Array of recipient data */ private function parse_custom_recipient_list( $recipient_list ) { if ( empty( $recipient_list ) ) { return array(); } $recipients = array(); $lines = preg_split( '/[\r\n,]+/', $recipient_list ); foreach ( $lines as $line ) { $line = trim( $line ); if ( empty( $line ) ) { continue; } // Check if line contains both name and email if ( preg_match( '/(.+?)\s*<(.+?)>/', $line, $matches ) ) { $name = trim( $matches[1] ); $email = trim( $matches[2] ); } else { // Just email address $email = $line; $name = ''; } if ( is_email( $email ) ) { $recipients[] = array( 'email' => $email, 'name' => $name, 'attendee_id' => 0, 'ticket_name' => '', 'status' => 'custom' ); } } return $recipients; } /** * Apply additional conditions to filter recipients * * @param array $recipients Current recipient list * @param array $conditions Filter conditions * @return array Filtered recipients */ private function apply_recipient_conditions( $recipients, $conditions ) { if ( empty( $conditions ) ) { return $recipients; } foreach ( $conditions as $condition ) { switch ( $condition['type'] ) { case 'ticket_type': $recipients = array_filter( $recipients, function( $recipient ) use ( $condition ) { return $recipient['ticket_name'] === $condition['value']; }); break; case 'exclude_emails': $exclude_list = array_map( 'trim', explode( ',', $condition['value'] ) ); $recipients = array_filter( $recipients, function( $recipient ) use ( $exclude_list ) { return ! in_array( $recipient['email'], $exclude_list ); }); break; } } return $recipients; } /** * Execute communication for a schedule * * @param array $schedule Schedule configuration * @param array $recipients Recipients to send to * @return bool Success status */ public function execute_communication( $schedule, $recipients ) { if ( empty( $recipients ) || empty( $schedule['template_id'] ) ) { return false; } // Get the email template $template = get_post( $schedule['template_id'] ); if ( ! $template || $template->post_type !== 'hvac_email_template' ) { return false; } $subject = $template->post_title; $message = $template->post_content; // Get event details for placeholder replacement $event_details = null; if ( ! empty( $schedule['event_id'] ) ) { $email_data = new HVAC_Email_Attendees_Data( $schedule['event_id'] ); $event_details = $email_data->get_event_details(); } $success_count = 0; $total_count = count( $recipients ); foreach ( $recipients as $recipient ) { // Replace placeholders in subject and message $personalized_subject = $this->replace_placeholders( $subject, $recipient, $event_details ); $personalized_message = $this->replace_placeholders( $message, $recipient, $event_details ); // Send email $headers = array( 'Content-Type: text/html; charset=UTF-8' ); // Add sender information $trainer = get_user_by( 'id', $schedule['trainer_id'] ); if ( $trainer ) { $from_name = $trainer->display_name; $from_email = $trainer->user_email; // Check for trainer business name $business_name = get_user_meta( $trainer->ID, 'business_name', true ); if ( ! empty( $business_name ) ) { $from_name = $business_name; } $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>'; } $mail_sent = wp_mail( $recipient['email'], $personalized_subject, wpautop( $personalized_message ), $headers ); if ( $mail_sent ) { $success_count++; } // Log individual send attempt if logger is available if ( class_exists( 'HVAC_Logger' ) ) { $status = $mail_sent ? 'sent' : 'failed'; HVAC_Logger::info( "Email {$status} to {$recipient['email']} for schedule {$schedule['schedule_id']}", 'Communication Engine' ); } } return $success_count === $total_count; } /** * Replace placeholders in email content * * @param string $content Email subject or content * @param array $recipient Recipient data * @param array|null $event_details Event details for placeholders * @return string Content with placeholders replaced */ private function replace_placeholders( $content, $recipient, $event_details = null ) { $placeholders = array( '{attendee_name}' => $recipient['name'], '{attendee_email}' => $recipient['email'], '{ticket_type}' => $recipient['ticket_name'] ); if ( $event_details ) { $placeholders['{event_title}'] = $event_details['title']; $placeholders['{event_date}'] = $event_details['start_date']; $placeholders['{event_time}'] = $event_details['start_time']; $placeholders['{event_start_date}'] = $event_details['start_date']; $placeholders['{event_start_time}'] = $event_details['start_time']; $placeholders['{event_end_date}'] = $event_details['end_date']; $placeholders['{event_end_time}'] = $event_details['end_time']; } // Add current date/time placeholders $placeholders['{current_date}'] = date( 'F j, Y' ); $placeholders['{current_time}'] = date( 'g:i a' ); $placeholders['{current_year}'] = date( 'Y' ); return str_replace( array_keys( $placeholders ), array_values( $placeholders ), $content ); } /** * Process registration-triggered communications * * @param int $attendee_id Attendee ID * @param int $event_id Event ID */ public function process_registration_triggers( $attendee_id, $event_id ) { global $wpdb; // Get all active schedules with registration triggers for this event $schedules_table = $wpdb->prefix . 'hvac_communication_schedules'; $schedules = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$schedules_table} WHERE event_id = %d AND trigger_type = 'on_registration' AND status = 'active'", $event_id ), ARRAY_A ); foreach ( $schedules as $schedule ) { // Get attendee details $attendee_post = get_post( $attendee_id ); if ( ! $attendee_post ) { continue; } $attendee_email = get_post_meta( $attendee_id, '_tribe_tickets_email', true ); if ( empty( $attendee_email ) ) { $attendee_email = get_post_meta( $attendee_id, '_tribe_tpp_email', true ); } $attendee_name = get_post_meta( $attendee_id, '_tribe_tickets_full_name', true ); if ( empty( $attendee_name ) ) { $attendee_name = get_post_meta( $attendee_id, '_tribe_tpp_full_name', true ); } if ( empty( $attendee_email ) || ! is_email( $attendee_email ) ) { continue; } // Create recipient array $recipients = array( array( 'email' => $attendee_email, 'name' => $attendee_name, 'attendee_id' => $attendee_id, 'ticket_name' => '', 'status' => 'confirmed' ) ); // Execute communication $this->execute_communication( $schedule, $recipients ); // Update schedule run tracking $schedule_manager = new HVAC_Communication_Schedule_Manager(); $schedule_manager->update_schedule_run_tracking( $schedule['schedule_id'] ); } } /** * Process event date changes and update affected schedules */ public function process_event_date_changes() { global $wpdb; // This would be called when event dates are updated // For now, it's a placeholder for future implementation if ( class_exists( 'HVAC_Logger' ) ) { HVAC_Logger::info( 'Processing event date changes', 'Communication Engine' ); } } /** * Validate recipients against event attendees * * @param array $recipients Recipients to validate * @param int $event_id Event ID * @return array Valid recipients only */ public function validate_recipients( $recipients, $event_id = null ) { if ( empty( $recipients ) ) { return array(); } $valid_recipients = array(); foreach ( $recipients as $recipient ) { // Basic email validation if ( empty( $recipient['email'] ) || ! is_email( $recipient['email'] ) ) { continue; } // If event ID provided, verify recipient is actually an attendee if ( $event_id ) { $all_attendees = $this->get_all_event_attendees( $event_id ); $is_attendee = false; foreach ( $all_attendees as $attendee ) { if ( $attendee['email'] === $recipient['email'] ) { $is_attendee = true; break; } } if ( ! $is_attendee && $recipient['status'] !== 'custom' ) { continue; } } $valid_recipients[] = $recipient; } return $valid_recipients; } /** * Get communication statistics for a schedule * * @param int $schedule_id Schedule ID * @return array Statistics array */ public function get_schedule_statistics( $schedule_id ) { global $wpdb; $logs_table = $wpdb->prefix . 'hvac_communication_logs'; $stats = $wpdb->get_row( $wpdb->prepare( "SELECT COUNT(*) as total_sends, COUNT(CASE WHEN status = 'sent' THEN 1 END) as successful_sends, COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed_sends, MAX(sent_date) as last_sent FROM {$logs_table} WHERE schedule_id = %d", $schedule_id ), ARRAY_A ); return $stats; } }