upskill-event-manager/includes/communication/class-communication-scheduler.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

596 lines
No EOL
22 KiB
PHP

<?php
/**
* HVAC Community Events - Communication Scheduler
*
* Main controller for automated communication scheduling system.
* Handles creation, management, and execution of scheduled communications.
*
* @package HVAC_Community_Events
* @subpackage Communication
* @version 1.0.0
*/
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class HVAC_Communication_Scheduler
*
* Core scheduling system for automated email communications.
*/
class HVAC_Communication_Scheduler {
/**
* Singleton instance
*/
private static $instance = null;
/**
* Schedule manager instance
*/
private $schedule_manager;
/**
* Trigger engine instance
*/
private $trigger_engine;
/**
* Communication logger instance
*/
private $logger;
/**
* Get singleton instance
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_dependencies();
$this->register_hooks();
// Debug logging
if ( class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::info( 'HVAC_Communication_Scheduler initialized', 'Scheduler' );
}
}
/**
* Initialize dependencies
*/
private function init_dependencies() {
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-schedule-manager.php';
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-trigger-engine.php';
require_once HVAC_PLUGIN_DIR . 'includes/communication/class-communication-logger.php';
$this->schedule_manager = new HVAC_Communication_Schedule_Manager();
$this->trigger_engine = new HVAC_Communication_Trigger_Engine();
$this->logger = new HVAC_Communication_Logger();
}
/**
* Register WordPress hooks
*/
private function register_hooks() {
// Cron hooks
add_action( 'hvac_process_communication_schedules', array( $this, 'process_scheduled_communications' ) );
// Event-based triggers
add_action( 'tribe_events_event_save_after', array( $this, 'on_event_saved' ) );
add_action( 'event_tickets_after_add_attendee', array( $this, 'on_attendee_registered' ) );
add_action( 'wp', array( $this, 'check_event_date_changes' ) );
// AJAX handlers
add_action( 'wp_ajax_hvac_create_schedule', array( $this, 'ajax_create_schedule' ) );
add_action( 'wp_ajax_hvac_update_schedule', array( $this, 'ajax_update_schedule' ) );
add_action( 'wp_ajax_hvac_delete_schedule', array( $this, 'ajax_delete_schedule' ) );
add_action( 'wp_ajax_hvac_get_schedules', array( $this, 'ajax_get_schedules' ) );
add_action( 'wp_ajax_hvac_toggle_schedule', array( $this, 'ajax_toggle_schedule' ) );
add_action( 'wp_ajax_hvac_preview_recipients', array( $this, 'ajax_preview_recipients' ) );
// Custom cron schedules
add_filter( 'cron_schedules', array( $this, 'add_custom_cron_schedules' ) );
// Initialize cron if not scheduled
add_action( 'wp_loaded', array( $this, 'setup_cron_schedules' ) );
}
/**
* Add custom cron schedules
*/
public function add_custom_cron_schedules( $schedules ) {
$schedules['hvac_every_5_minutes'] = array(
'interval' => 300,
'display' => __( 'Every 5 minutes', 'hvac-community-events' )
);
$schedules['hvac_every_15_minutes'] = array(
'interval' => 900,
'display' => __( 'Every 15 minutes', 'hvac-community-events' )
);
return $schedules;
}
/**
* Setup cron schedules
*/
public function setup_cron_schedules() {
if ( ! wp_next_scheduled( 'hvac_process_communication_schedules' ) ) {
wp_schedule_event( time(), 'hvac_every_15_minutes', 'hvac_process_communication_schedules' );
if ( class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::info( 'Communication scheduler cron job set up', 'Scheduler' );
}
}
}
/**
* Create a new communication schedule
*
* @param array $schedule_data Schedule configuration
* @return int|WP_Error Schedule ID on success, WP_Error on failure
*/
public function create_schedule( $schedule_data ) {
// Validate schedule data
$validation_result = $this->schedule_manager->validate_schedule_data( $schedule_data );
if ( is_wp_error( $validation_result ) ) {
return $validation_result;
}
// Check for conflicts
$conflict_check = $this->schedule_manager->check_schedule_conflicts( $schedule_data );
if ( is_wp_error( $conflict_check ) ) {
return $conflict_check;
}
// Calculate next run time
$next_run = $this->calculate_next_run_time( $schedule_data );
$schedule_data['next_run'] = $next_run;
// Save schedule
$schedule_id = $this->schedule_manager->save_schedule( $schedule_data );
if ( $schedule_id && class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::info( "Communication schedule created: ID $schedule_id", 'Scheduler' );
}
return $schedule_id;
}
/**
* Update an existing communication schedule
*
* @param int $schedule_id Schedule ID
* @param array $schedule_data Updated schedule configuration
* @return bool|WP_Error Success status
*/
public function update_schedule( $schedule_id, $schedule_data ) {
// Verify ownership
if ( ! $this->schedule_manager->user_can_edit_schedule( $schedule_id ) ) {
return new WP_Error( 'permission_denied', __( 'You do not have permission to edit this schedule.', 'hvac-community-events' ) );
}
// Validate data
$validation_result = $this->schedule_manager->validate_schedule_data( $schedule_data );
if ( is_wp_error( $validation_result ) ) {
return $validation_result;
}
// Recalculate next run time if trigger settings changed
$existing_schedule = $this->schedule_manager->get_schedule( $schedule_id );
$trigger_changed = (
$existing_schedule['trigger_type'] !== $schedule_data['trigger_type'] ||
$existing_schedule['trigger_value'] !== $schedule_data['trigger_value'] ||
$existing_schedule['trigger_unit'] !== $schedule_data['trigger_unit']
);
if ( $trigger_changed ) {
$schedule_data['next_run'] = $this->calculate_next_run_time( $schedule_data );
}
return $this->schedule_manager->update_schedule( $schedule_id, $schedule_data );
}
/**
* Delete a communication schedule
*
* @param int $schedule_id Schedule ID
* @return bool|WP_Error Success status
*/
public function delete_schedule( $schedule_id ) {
// Verify ownership
if ( ! $this->schedule_manager->user_can_edit_schedule( $schedule_id ) ) {
return new WP_Error( 'permission_denied', __( 'You do not have permission to delete this schedule.', 'hvac-community-events' ) );
}
$result = $this->schedule_manager->delete_schedule( $schedule_id );
if ( $result && class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::info( "Communication schedule deleted: ID $schedule_id", 'Scheduler' );
}
return $result;
}
/**
* Get schedules for a trainer
*
* @param int $trainer_id Trainer user ID
* @param int $event_id Optional specific event ID
* @return array Array of schedules
*/
public function get_trainer_schedules( $trainer_id, $event_id = null ) {
return $this->schedule_manager->get_schedules_by_trainer( $trainer_id, $event_id );
}
/**
* Process all due scheduled communications
*/
public function process_scheduled_communications() {
$due_schedules = $this->schedule_manager->get_due_schedules();
if ( class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::info( 'Processing ' . count( $due_schedules ) . ' due communication schedules', 'Scheduler' );
}
foreach ( $due_schedules as $schedule ) {
$this->execute_schedule( $schedule['schedule_id'] );
}
}
/**
* Calculate next run time for a schedule
*
* @param array $schedule Schedule configuration
* @return string MySQL datetime string
*/
public function calculate_next_run_time( $schedule ) {
if ( ! empty( $schedule['event_id'] ) ) {
// Event-based scheduling
$event_date = get_post_meta( $schedule['event_id'], '_EventStartDate', true );
if ( ! $event_date ) {
return null;
}
return $this->trigger_engine->calculate_trigger_time( $event_date, $schedule );
} else {
// Immediate or custom date scheduling
if ( $schedule['trigger_type'] === 'custom_date' && ! empty( $schedule['custom_date'] ) ) {
return $schedule['custom_date'];
} elseif ( $schedule['trigger_type'] === 'on_registration' ) {
// This will be triggered immediately on registration
return null;
}
}
return null;
}
/**
* Execute a specific schedule
*
* @param int $schedule_id Schedule ID
* @return bool Success status
*/
public function execute_schedule( $schedule_id ) {
$schedule = $this->schedule_manager->get_schedule( $schedule_id );
if ( ! $schedule ) {
return false;
}
try {
// Get recipients
$recipients = $this->trigger_engine->get_schedule_recipients( $schedule );
if ( empty( $recipients ) ) {
$this->logger->log_schedule_execution( $schedule_id, 'skipped', array(
'reason' => 'No recipients found'
) );
return true;
}
// Execute communication
$result = $this->trigger_engine->execute_communication( $schedule, $recipients );
// Update schedule run tracking
$this->schedule_manager->update_schedule_run_tracking( $schedule_id );
// Log execution
$this->logger->log_schedule_execution( $schedule_id, 'sent', array(
'recipient_count' => count( $recipients ),
'success' => $result
) );
return $result;
} catch ( Exception $e ) {
$this->logger->log_schedule_execution( $schedule_id, 'failed', array(
'error' => $e->getMessage()
) );
if ( class_exists( 'HVAC_Logger' ) ) {
HVAC_Logger::error( "Schedule execution failed: " . $e->getMessage(), 'Scheduler' );
}
return false;
}
}
/**
* Handle event saved/updated
*
* @param int $event_id Event ID
*/
public function on_event_saved( $event_id ) {
$schedules = $this->schedule_manager->get_schedules_by_event( $event_id );
foreach ( $schedules as $schedule ) {
// Recalculate next run time if event date changed
$new_next_run = $this->calculate_next_run_time( $schedule );
if ( $new_next_run !== $schedule['next_run'] ) {
$this->schedule_manager->update_schedule( $schedule['schedule_id'], array(
'next_run' => $new_next_run
) );
}
}
}
/**
* Handle attendee registration
*
* @param int $attendee_id Attendee ID
* @param int $event_id Event ID
*/
public function on_attendee_registered( $attendee_id, $event_id ) {
// Process immediate registration triggers
$this->trigger_engine->process_registration_triggers( $attendee_id, $event_id );
}
/**
* Check for event date changes
*/
public function check_event_date_changes() {
// This will be called on wp hook to check for any event date changes
// and update corresponding schedules
if ( ! is_admin() || ! current_user_can( 'edit_posts' ) ) {
return;
}
// Process any date change updates
$this->trigger_engine->process_event_date_changes();
}
/**
* AJAX: Create schedule
*/
public function ajax_create_schedule() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to create schedules.', 'hvac-community-events' ) ) );
}
$schedule_data = $this->sanitize_schedule_data( $_POST );
$schedule_data['trainer_id'] = get_current_user_id();
$result = $this->create_schedule( $schedule_data );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'schedule_id' => $result,
'message' => __( 'Schedule created successfully.', 'hvac-community-events' )
) );
}
/**
* AJAX: Update schedule
*/
public function ajax_update_schedule() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to update schedules.', 'hvac-community-events' ) ) );
}
$schedule_id = intval( $_POST['schedule_id'] );
$schedule_data = $this->sanitize_schedule_data( $_POST );
$result = $this->update_schedule( $schedule_id, $schedule_data );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array( 'message' => __( 'Schedule updated successfully.', 'hvac-community-events' ) ) );
}
/**
* AJAX: Delete schedule
*/
public function ajax_delete_schedule() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to delete schedules.', 'hvac-community-events' ) ) );
}
$schedule_id = intval( $_POST['schedule_id'] );
$result = $this->delete_schedule( $schedule_id );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array( 'message' => __( 'Schedule deleted successfully.', 'hvac-community-events' ) ) );
}
/**
* AJAX: Get schedules
*/
public function ajax_get_schedules() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to view schedules.', 'hvac-community-events' ) ) );
}
$trainer_id = get_current_user_id();
$event_id = isset( $_POST['event_id'] ) ? intval( $_POST['event_id'] ) : null;
$status_filter = isset( $_POST['status'] ) ? sanitize_text_field( $_POST['status'] ) : null;
$schedules = $this->get_trainer_schedules( $trainer_id, $event_id );
if ( $status_filter && $status_filter !== 'all' ) {
$schedules = array_filter( $schedules, function( $schedule ) use ( $status_filter ) {
return $schedule['status'] === $status_filter;
} );
}
wp_send_json_success( array( 'schedules' => array_values( $schedules ) ) );
}
/**
* AJAX: Toggle schedule status
*/
public function ajax_toggle_schedule() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to toggle schedules.', 'hvac-community-events' ) ) );
}
$schedule_id = intval( $_POST['schedule_id'] );
$schedule = $this->schedule_manager->get_schedule( $schedule_id );
if ( ! $schedule ) {
wp_send_json_error( array( 'message' => __( 'Schedule not found.', 'hvac-community-events' ) ) );
}
$new_status = ( $schedule['status'] === 'active' ) ? 'paused' : 'active';
$result = $this->update_schedule( $schedule_id, array( 'status' => $new_status ) );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'status' => $new_status,
'message' => sprintf( __( 'Schedule %s.', 'hvac-community-events' ), $new_status )
) );
}
/**
* AJAX: Preview recipients
*/
public function ajax_preview_recipients() {
check_ajax_referer( 'hvac_scheduler_nonce', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( array( 'message' => __( 'You must be logged in to preview recipients.', 'hvac-community-events' ) ) );
}
$schedule_data = $this->sanitize_schedule_data( $_POST );
$schedule_data['trainer_id'] = get_current_user_id();
$recipients = $this->trigger_engine->get_schedule_recipients( $schedule_data );
wp_send_json_success( array(
'recipients' => $recipients,
'count' => count( $recipients )
) );
}
/**
* Sanitize schedule data from form input
*
* @param array $raw_data Raw POST data
* @return array Sanitized schedule data
*/
private function sanitize_schedule_data( $raw_data ) {
return array(
'schedule_name' => isset( $raw_data['schedule_name'] ) ? sanitize_text_field( $raw_data['schedule_name'] ) : '',
'event_id' => isset( $raw_data['event_id'] ) ? intval( $raw_data['event_id'] ) : null,
'template_id' => isset( $raw_data['template_id'] ) ? intval( $raw_data['template_id'] ) : 0,
'trigger_type' => isset( $raw_data['trigger_type'] ) ? sanitize_text_field( $raw_data['trigger_type'] ) : '',
'trigger_value' => isset( $raw_data['trigger_value'] ) ? intval( $raw_data['trigger_value'] ) : 0,
'trigger_unit' => isset( $raw_data['trigger_unit'] ) ? sanitize_text_field( $raw_data['trigger_unit'] ) : 'days',
'target_audience' => isset( $raw_data['target_audience'] ) ? sanitize_text_field( $raw_data['target_audience'] ) : 'all_attendees',
'custom_recipient_list' => isset( $raw_data['custom_recipient_list'] ) ? sanitize_textarea_field( $raw_data['custom_recipient_list'] ) : '',
'is_recurring' => isset( $raw_data['is_recurring'] ) ? (bool) $raw_data['is_recurring'] : false,
'recurring_interval' => isset( $raw_data['recurring_interval'] ) ? intval( $raw_data['recurring_interval'] ) : null,
'recurring_unit' => isset( $raw_data['recurring_unit'] ) ? sanitize_text_field( $raw_data['recurring_unit'] ) : null,
'max_runs' => isset( $raw_data['max_runs'] ) ? intval( $raw_data['max_runs'] ) : null,
'status' => isset( $raw_data['status'] ) ? sanitize_text_field( $raw_data['status'] ) : 'active'
);
}
/**
* Get default schedule templates
*
* @return array Default schedule configurations
*/
public function get_default_schedule_templates() {
return array(
'event_reminder_24h' => array(
'name' => __( '24-Hour Event Reminder', 'hvac-community-events' ),
'trigger_type' => 'before_event',
'trigger_value' => 1,
'trigger_unit' => 'days',
'template_category' => 'event_reminder',
'target_audience' => 'confirmed_attendees',
'description' => __( 'Send reminder 24 hours before event starts', 'hvac-community-events' )
),
'welcome_on_registration' => array(
'name' => __( 'Welcome Email on Registration', 'hvac-community-events' ),
'trigger_type' => 'on_registration',
'trigger_value' => 0,
'trigger_unit' => 'minutes',
'template_category' => 'pre_event',
'target_audience' => 'all_attendees',
'description' => __( 'Send welcome email immediately when someone registers', 'hvac-community-events' )
),
'post_event_followup' => array(
'name' => __( 'Post-Event Follow-up', 'hvac-community-events' ),
'trigger_type' => 'after_event',
'trigger_value' => 2,
'trigger_unit' => 'days',
'template_category' => 'post_event',
'target_audience' => 'all_attendees',
'description' => __( 'Send follow-up email 2 days after event', 'hvac-community-events' )
),
'certificate_notification' => array(
'name' => __( 'Certificate Ready Notification', 'hvac-community-events' ),
'trigger_type' => 'after_event',
'trigger_value' => 3,
'trigger_unit' => 'days',
'template_category' => 'certificate',
'target_audience' => 'confirmed_attendees',
'description' => __( 'Notify attendees when certificates are ready', 'hvac-community-events' )
)
);
}
}
// Initialize the scheduler
function hvac_communication_scheduler() {
return HVAC_Communication_Scheduler::instance();
}
// Initialize after plugins loaded
add_action( 'plugins_loaded', 'hvac_communication_scheduler' );