🚨 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>
		
			
				
	
	
		
			603 lines
		
	
	
		
			No EOL
		
	
	
		
			21 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			603 lines
		
	
	
		
			No EOL
		
	
	
		
			21 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * HVAC Community Events - Communication Schedule Manager
 | |
|  *
 | |
|  * Handles CRUD operations for communication schedules.
 | |
|  * Manages database interactions and schedule validation.
 | |
|  *
 | |
|  * @package HVAC_Community_Events
 | |
|  * @subpackage Communication
 | |
|  * @version 1.0.0
 | |
|  */
 | |
| 
 | |
| // Exit if accessed directly
 | |
| if ( ! defined( 'ABSPATH' ) ) {
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Class HVAC_Communication_Schedule_Manager
 | |
|  *
 | |
|  * Manages communication schedule database operations.
 | |
|  */
 | |
| class HVAC_Communication_Schedule_Manager {
 | |
| 
 | |
|     /**
 | |
|      * Database table names
 | |
|      */
 | |
|     private $schedules_table;
 | |
|     private $logs_table;
 | |
|     private $tracking_table;
 | |
| 
 | |
|     /**
 | |
|      * Constructor
 | |
|      */
 | |
|     public function __construct() {
 | |
|         global $wpdb;
 | |
|         
 | |
|         $this->schedules_table = $wpdb->prefix . 'hvac_communication_schedules';
 | |
|         $this->logs_table = $wpdb->prefix . 'hvac_communication_logs';
 | |
|         $this->tracking_table = $wpdb->prefix . 'hvac_event_communication_tracking';
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Save a communication schedule
 | |
|      *
 | |
|      * @param array $schedule_data Schedule configuration
 | |
|      * @return int|false Schedule ID on success, false on failure
 | |
|      */
 | |
|     public function save_schedule( $schedule_data ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $data = array(
 | |
|             'trainer_id' => intval( $schedule_data['trainer_id'] ),
 | |
|             'event_id' => ! empty( $schedule_data['event_id'] ) ? intval( $schedule_data['event_id'] ) : null,
 | |
|             'template_id' => intval( $schedule_data['template_id'] ),
 | |
|             'schedule_type' => isset( $schedule_data['schedule_type'] ) ? $schedule_data['schedule_type'] : 'time_based',
 | |
|             'trigger_type' => sanitize_text_field( $schedule_data['trigger_type'] ),
 | |
|             'trigger_value' => intval( $schedule_data['trigger_value'] ),
 | |
|             'trigger_unit' => sanitize_text_field( $schedule_data['trigger_unit'] ),
 | |
|             'status' => isset( $schedule_data['status'] ) ? sanitize_text_field( $schedule_data['status'] ) : 'active',
 | |
|             'target_audience' => sanitize_text_field( $schedule_data['target_audience'] ),
 | |
|             'custom_recipient_list' => ! empty( $schedule_data['custom_recipient_list'] ) ? 
 | |
|                 sanitize_textarea_field( $schedule_data['custom_recipient_list'] ) : null,
 | |
|             'conditions' => ! empty( $schedule_data['conditions'] ) ? 
 | |
|                 wp_json_encode( $schedule_data['conditions'] ) : null,
 | |
|             'next_run' => ! empty( $schedule_data['next_run'] ) ? 
 | |
|                 sanitize_text_field( $schedule_data['next_run'] ) : null,
 | |
|             'is_recurring' => isset( $schedule_data['is_recurring'] ) ? 
 | |
|                 (int) $schedule_data['is_recurring'] : 0,
 | |
|             'recurring_interval' => ! empty( $schedule_data['recurring_interval'] ) ? 
 | |
|                 intval( $schedule_data['recurring_interval'] ) : null,
 | |
|             'recurring_unit' => ! empty( $schedule_data['recurring_unit'] ) ? 
 | |
|                 sanitize_text_field( $schedule_data['recurring_unit'] ) : null,
 | |
|             'max_runs' => ! empty( $schedule_data['max_runs'] ) ? 
 | |
|                 intval( $schedule_data['max_runs'] ) : null
 | |
|         );
 | |
| 
 | |
|         $formats = array(
 | |
|             '%d', // trainer_id
 | |
|             '%d', // event_id
 | |
|             '%d', // template_id
 | |
|             '%s', // schedule_type
 | |
|             '%s', // trigger_type
 | |
|             '%d', // trigger_value
 | |
|             '%s', // trigger_unit
 | |
|             '%s', // status
 | |
|             '%s', // target_audience
 | |
|             '%s', // custom_recipient_list
 | |
|             '%s', // conditions
 | |
|             '%s', // next_run
 | |
|             '%d', // is_recurring
 | |
|             '%d', // recurring_interval
 | |
|             '%s', // recurring_unit
 | |
|             '%d'  // max_runs
 | |
|         );
 | |
| 
 | |
|         $result = $wpdb->insert( $this->schedules_table, $data, $formats );
 | |
| 
 | |
|         if ( $result === false ) {
 | |
|             if ( class_exists( 'HVAC_Logger' ) ) {
 | |
|                 HVAC_Logger::error( 'Failed to save communication schedule: ' . $wpdb->last_error, 'Schedule Manager' );
 | |
|             }
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         return $wpdb->insert_id;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Update a communication schedule
 | |
|      *
 | |
|      * @param int $schedule_id Schedule ID
 | |
|      * @param array $schedule_data Updated schedule data
 | |
|      * @return bool Success status
 | |
|      */
 | |
|     public function update_schedule( $schedule_id, $schedule_data ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $data = array();
 | |
|         $formats = array();
 | |
| 
 | |
|         // Only update provided fields
 | |
|         $allowed_fields = array(
 | |
|             'event_id' => '%d',
 | |
|             'template_id' => '%d',
 | |
|             'schedule_type' => '%s',
 | |
|             'trigger_type' => '%s',
 | |
|             'trigger_value' => '%d',
 | |
|             'trigger_unit' => '%s',
 | |
|             'status' => '%s',
 | |
|             'target_audience' => '%s',
 | |
|             'custom_recipient_list' => '%s',
 | |
|             'conditions' => '%s',
 | |
|             'next_run' => '%s',
 | |
|             'is_recurring' => '%d',
 | |
|             'recurring_interval' => '%d',
 | |
|             'recurring_unit' => '%s',
 | |
|             'max_runs' => '%d'
 | |
|         );
 | |
| 
 | |
|         foreach ( $allowed_fields as $field => $format ) {
 | |
|             if ( array_key_exists( $field, $schedule_data ) ) {
 | |
|                 if ( $field === 'conditions' && ! empty( $schedule_data[$field] ) ) {
 | |
|                     $data[$field] = wp_json_encode( $schedule_data[$field] );
 | |
|                 } elseif ( in_array( $format, array( '%d' ) ) ) {
 | |
|                     $data[$field] = intval( $schedule_data[$field] );
 | |
|                 } else {
 | |
|                     $data[$field] = sanitize_text_field( $schedule_data[$field] );
 | |
|                 }
 | |
|                 $formats[] = $format;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Add modified timestamp
 | |
|         $data['modified_date'] = current_time( 'mysql' );
 | |
|         $formats[] = '%s';
 | |
| 
 | |
|         $result = $wpdb->update(
 | |
|             $this->schedules_table,
 | |
|             $data,
 | |
|             array( 'schedule_id' => intval( $schedule_id ) ),
 | |
|             $formats,
 | |
|             array( '%d' )
 | |
|         );
 | |
| 
 | |
|         return $result !== false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get a communication schedule by ID
 | |
|      *
 | |
|      * @param int $schedule_id Schedule ID
 | |
|      * @return array|null Schedule data or null if not found
 | |
|      */
 | |
|     public function get_schedule( $schedule_id ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $schedule = $wpdb->get_row( $wpdb->prepare(
 | |
|             "SELECT * FROM {$this->schedules_table} WHERE schedule_id = %d",
 | |
|             intval( $schedule_id )
 | |
|         ), ARRAY_A );
 | |
| 
 | |
|         if ( $schedule && ! empty( $schedule['conditions'] ) ) {
 | |
|             $schedule['conditions'] = json_decode( $schedule['conditions'], true );
 | |
|         }
 | |
| 
 | |
|         return $schedule;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get schedules by trainer
 | |
|      *
 | |
|      * @param int $trainer_id Trainer user ID
 | |
|      * @param int $event_id Optional specific event ID
 | |
|      * @return array Array of schedules
 | |
|      */
 | |
|     public function get_schedules_by_trainer( $trainer_id, $event_id = null ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $where_clause = "WHERE trainer_id = %d";
 | |
|         $params = array( intval( $trainer_id ) );
 | |
| 
 | |
|         if ( $event_id ) {
 | |
|             $where_clause .= " AND event_id = %d";
 | |
|             $params[] = intval( $event_id );
 | |
|         }
 | |
| 
 | |
|         $schedules = $wpdb->get_results( $wpdb->prepare(
 | |
|             "SELECT s.*, 
 | |
|                     t.post_title as template_name,
 | |
|                     e.post_title as event_name,
 | |
|                     e.post_status as event_status
 | |
|              FROM {$this->schedules_table} s
 | |
|              LEFT JOIN {$wpdb->posts} t ON s.template_id = t.ID
 | |
|              LEFT JOIN {$wpdb->posts} e ON s.event_id = e.ID
 | |
|              {$where_clause}
 | |
|              ORDER BY s.created_date DESC",
 | |
|             $params
 | |
|         ), ARRAY_A );
 | |
| 
 | |
|         // Process conditions field
 | |
|         foreach ( $schedules as &$schedule ) {
 | |
|             if ( ! empty( $schedule['conditions'] ) ) {
 | |
|                 $schedule['conditions'] = json_decode( $schedule['conditions'], true );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $schedules;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get schedules by event
 | |
|      *
 | |
|      * @param int $event_id Event ID
 | |
|      * @return array Array of schedules
 | |
|      */
 | |
|     public function get_schedules_by_event( $event_id ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $schedules = $wpdb->get_results( $wpdb->prepare(
 | |
|             "SELECT s.*, 
 | |
|                     t.post_title as template_name
 | |
|              FROM {$this->schedules_table} s
 | |
|              LEFT JOIN {$wpdb->posts} t ON s.template_id = t.ID
 | |
|              WHERE s.event_id = %d
 | |
|              ORDER BY s.trigger_type, s.trigger_value",
 | |
|             intval( $event_id )
 | |
|         ), ARRAY_A );
 | |
| 
 | |
|         // Process conditions field
 | |
|         foreach ( $schedules as &$schedule ) {
 | |
|             if ( ! empty( $schedule['conditions'] ) ) {
 | |
|                 $schedule['conditions'] = json_decode( $schedule['conditions'], true );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $schedules;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get active schedules
 | |
|      *
 | |
|      * @return array Array of active schedules
 | |
|      */
 | |
|     public function get_active_schedules() {
 | |
|         global $wpdb;
 | |
| 
 | |
|         return $wpdb->get_results(
 | |
|             "SELECT * FROM {$this->schedules_table} 
 | |
|              WHERE status = 'active'
 | |
|              ORDER BY next_run ASC",
 | |
|             ARRAY_A
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get due schedules
 | |
|      *
 | |
|      * @return array Array of schedules that are due for execution
 | |
|      */
 | |
|     public function get_due_schedules() {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $current_time = current_time( 'mysql' );
 | |
| 
 | |
|         $schedules = $wpdb->get_results( $wpdb->prepare(
 | |
|             "SELECT * FROM {$this->schedules_table} 
 | |
|              WHERE status = 'active' 
 | |
|              AND next_run IS NOT NULL 
 | |
|              AND next_run <= %s
 | |
|              AND (max_runs IS NULL OR run_count < max_runs)
 | |
|              ORDER BY next_run ASC",
 | |
|             $current_time
 | |
|         ), ARRAY_A );
 | |
| 
 | |
|         return $schedules;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Delete a communication schedule
 | |
|      *
 | |
|      * @param int $schedule_id Schedule ID
 | |
|      * @return bool Success status
 | |
|      */
 | |
|     public function delete_schedule( $schedule_id ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         // Delete associated logs first (foreign key constraint)
 | |
|         $wpdb->delete(
 | |
|             $this->logs_table,
 | |
|             array( 'schedule_id' => intval( $schedule_id ) ),
 | |
|             array( '%d' )
 | |
|         );
 | |
| 
 | |
|         // Delete the schedule
 | |
|         $result = $wpdb->delete(
 | |
|             $this->schedules_table,
 | |
|             array( 'schedule_id' => intval( $schedule_id ) ),
 | |
|             array( '%d' )
 | |
|         );
 | |
| 
 | |
|         return $result !== false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Update schedule run tracking
 | |
|      *
 | |
|      * @param int $schedule_id Schedule ID
 | |
|      * @return bool Success status
 | |
|      */
 | |
|     public function update_schedule_run_tracking( $schedule_id ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $schedule = $this->get_schedule( $schedule_id );
 | |
|         if ( ! $schedule ) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         $data = array(
 | |
|             'last_run' => current_time( 'mysql' ),
 | |
|             'run_count' => intval( $schedule['run_count'] ) + 1
 | |
|         );
 | |
| 
 | |
|         // Calculate next run if recurring
 | |
|         if ( $schedule['is_recurring'] ) {
 | |
|             $next_run = $this->calculate_next_recurring_run( $schedule );
 | |
|             if ( $next_run ) {
 | |
|                 $data['next_run'] = $next_run;
 | |
|             }
 | |
|         } else {
 | |
|             // Mark as completed if not recurring
 | |
|             $data['status'] = 'completed';
 | |
|             $data['next_run'] = null;
 | |
|         }
 | |
| 
 | |
|         return $this->update_schedule( $schedule_id, $data );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Calculate next recurring run time
 | |
|      *
 | |
|      * @param array $schedule Schedule data
 | |
|      * @return string|null Next run time or null
 | |
|      */
 | |
|     private function calculate_next_recurring_run( $schedule ) {
 | |
|         if ( ! $schedule['is_recurring'] || ! $schedule['recurring_interval'] ) {
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         $interval = $schedule['recurring_interval'];
 | |
|         $unit = $schedule['recurring_unit'];
 | |
| 
 | |
|         $current_time = current_time( 'timestamp' );
 | |
| 
 | |
|         switch ( $unit ) {
 | |
|             case 'days':
 | |
|                 $next_time = $current_time + ( $interval * DAY_IN_SECONDS );
 | |
|                 break;
 | |
|             case 'weeks':
 | |
|                 $next_time = $current_time + ( $interval * WEEK_IN_SECONDS );
 | |
|                 break;
 | |
|             case 'months':
 | |
|                 $next_time = strtotime( "+{$interval} months", $current_time );
 | |
|                 break;
 | |
|             default:
 | |
|                 return null;
 | |
|         }
 | |
| 
 | |
|         return date( 'Y-m-d H:i:s', $next_time );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Validate schedule data
 | |
|      *
 | |
|      * @param array $schedule_data Schedule data to validate
 | |
|      * @return bool|WP_Error True if valid, WP_Error if invalid
 | |
|      */
 | |
|     public function validate_schedule_data( $schedule_data ) {
 | |
|         // Required fields
 | |
|         $required_fields = array( 'trainer_id', 'template_id', 'trigger_type', 'target_audience' );
 | |
|         
 | |
|         foreach ( $required_fields as $field ) {
 | |
|             if ( empty( $schedule_data[$field] ) ) {
 | |
|                 return new WP_Error( 'missing_field', sprintf( __( 'Required field missing: %s', 'hvac-community-events' ), $field ) );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Validate trainer exists and has permission
 | |
|         $trainer = get_user_by( 'id', $schedule_data['trainer_id'] );
 | |
|         if ( ! $trainer || ! in_array( 'hvac_trainer', $trainer->roles ) ) {
 | |
|             return new WP_Error( 'invalid_trainer', __( 'Invalid trainer specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Validate template exists and belongs to trainer
 | |
|         $template = get_post( $schedule_data['template_id'] );
 | |
|         if ( ! $template || $template->post_type !== 'hvac_email_template' ) {
 | |
|             return new WP_Error( 'invalid_template', __( 'Invalid template specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         if ( $template->post_author != $schedule_data['trainer_id'] && ! current_user_can( 'edit_others_posts' ) ) {
 | |
|             return new WP_Error( 'template_permission', __( 'You do not have permission to use this template.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Validate event if specified
 | |
|         if ( ! empty( $schedule_data['event_id'] ) ) {
 | |
|             $event = get_post( $schedule_data['event_id'] );
 | |
|             if ( ! $event || $event->post_type !== 'tribe_events' ) {
 | |
|                 return new WP_Error( 'invalid_event', __( 'Invalid event specified.', 'hvac-community-events' ) );
 | |
|             }
 | |
| 
 | |
|             // Check if trainer owns the event
 | |
|             if ( $event->post_author != $schedule_data['trainer_id'] && ! current_user_can( 'edit_others_posts' ) ) {
 | |
|                 return new WP_Error( 'event_permission', __( 'You do not have permission to schedule communications for this event.', 'hvac-community-events' ) );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Validate trigger settings
 | |
|         $valid_trigger_types = array( 'before_event', 'after_event', 'on_registration', 'custom_date' );
 | |
|         if ( ! in_array( $schedule_data['trigger_type'], $valid_trigger_types ) ) {
 | |
|             return new WP_Error( 'invalid_trigger_type', __( 'Invalid trigger type specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         $valid_trigger_units = array( 'minutes', 'hours', 'days', 'weeks' );
 | |
|         if ( ! in_array( $schedule_data['trigger_unit'], $valid_trigger_units ) ) {
 | |
|             return new WP_Error( 'invalid_trigger_unit', __( 'Invalid trigger unit specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Validate audience settings
 | |
|         $valid_audiences = array( 'all_attendees', 'confirmed_attendees', 'pending_attendees', 'custom_list' );
 | |
|         if ( ! in_array( $schedule_data['target_audience'], $valid_audiences ) ) {
 | |
|             return new WP_Error( 'invalid_audience', __( 'Invalid target audience specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Validate custom recipient list if specified
 | |
|         if ( $schedule_data['target_audience'] === 'custom_list' && empty( $schedule_data['custom_recipient_list'] ) ) {
 | |
|             return new WP_Error( 'missing_recipients', __( 'Custom recipient list is required when target audience is set to custom list.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Validate recurring settings
 | |
|         if ( ! empty( $schedule_data['is_recurring'] ) ) {
 | |
|             if ( empty( $schedule_data['recurring_interval'] ) || empty( $schedule_data['recurring_unit'] ) ) {
 | |
|                 return new WP_Error( 'invalid_recurring', __( 'Recurring interval and unit are required for recurring schedules.', 'hvac-community-events' ) );
 | |
|             }
 | |
| 
 | |
|             $valid_recurring_units = array( 'days', 'weeks', 'months' );
 | |
|             if ( ! in_array( $schedule_data['recurring_unit'], $valid_recurring_units ) ) {
 | |
|                 return new WP_Error( 'invalid_recurring_unit', __( 'Invalid recurring unit specified.', 'hvac-community-events' ) );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check for schedule conflicts
 | |
|      *
 | |
|      * @param array $schedule_data Schedule data to check
 | |
|      * @return bool|WP_Error True if no conflicts, WP_Error if conflicts found
 | |
|      */
 | |
|     public function check_schedule_conflicts( $schedule_data ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         // Check for duplicate schedules with same trigger settings
 | |
|         $existing = $wpdb->get_var( $wpdb->prepare(
 | |
|             "SELECT COUNT(*) FROM {$this->schedules_table} 
 | |
|              WHERE trainer_id = %d 
 | |
|              AND event_id = %d 
 | |
|              AND template_id = %d 
 | |
|              AND trigger_type = %s 
 | |
|              AND trigger_value = %d 
 | |
|              AND trigger_unit = %s 
 | |
|              AND status = 'active'",
 | |
|             $schedule_data['trainer_id'],
 | |
|             $schedule_data['event_id'] ?? 0,
 | |
|             $schedule_data['template_id'],
 | |
|             $schedule_data['trigger_type'],
 | |
|             $schedule_data['trigger_value'],
 | |
|             $schedule_data['trigger_unit']
 | |
|         ) );
 | |
| 
 | |
|         if ( $existing > 0 ) {
 | |
|             return new WP_Error( 'duplicate_schedule', __( 'A schedule with identical settings already exists.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check if user can edit schedule
 | |
|      *
 | |
|      * @param int $schedule_id Schedule ID
 | |
|      * @return bool Whether user can edit the schedule
 | |
|      */
 | |
|     public function user_can_edit_schedule( $schedule_id ) {
 | |
|         $schedule = $this->get_schedule( $schedule_id );
 | |
|         
 | |
|         if ( ! $schedule ) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         $current_user_id = get_current_user_id();
 | |
|         
 | |
|         // Owner can edit
 | |
|         if ( $schedule['trainer_id'] == $current_user_id ) {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         // Admins can edit others' schedules
 | |
|         if ( current_user_can( 'edit_others_posts' ) ) {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get available templates for scheduling
 | |
|      *
 | |
|      * @param int $trainer_id Trainer user ID
 | |
|      * @return array Array of available templates
 | |
|      */
 | |
|     public function get_available_templates( $trainer_id ) {
 | |
|         $templates_manager = new HVAC_Communication_Templates();
 | |
|         return $templates_manager->get_user_templates( $trainer_id );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Validate template compatibility with schedule type
 | |
|      *
 | |
|      * @param int $template_id Template ID
 | |
|      * @param string $schedule_type Schedule type
 | |
|      * @return bool|WP_Error True if compatible, WP_Error if not
 | |
|      */
 | |
|     public function validate_template_compatibility( $template_id, $schedule_type ) {
 | |
|         $template = get_post( $template_id );
 | |
|         
 | |
|         if ( ! $template || $template->post_type !== 'hvac_email_template' ) {
 | |
|             return new WP_Error( 'invalid_template', __( 'Invalid template specified.', 'hvac-community-events' ) );
 | |
|         }
 | |
| 
 | |
|         // Check for required placeholders based on schedule type
 | |
|         $required_placeholders = array( '{attendee_name}', '{event_title}' );
 | |
|         
 | |
|         if ( $schedule_type === 'event_based' ) {
 | |
|             $required_placeholders[] = '{event_date}';
 | |
|             $required_placeholders[] = '{event_time}';
 | |
|         }
 | |
| 
 | |
|         foreach ( $required_placeholders as $placeholder ) {
 | |
|             if ( strpos( $template->post_content, $placeholder ) === false ) {
 | |
|                 return new WP_Error( 'missing_placeholder', 
 | |
|                     sprintf( __( 'Template missing required placeholder: %s', 'hvac-community-events' ), $placeholder ) 
 | |
|                 );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get schedule statistics for a trainer
 | |
|      *
 | |
|      * @param int $trainer_id Trainer user ID
 | |
|      * @return array Statistics array
 | |
|      */
 | |
|     public function get_trainer_schedule_stats( $trainer_id ) {
 | |
|         global $wpdb;
 | |
| 
 | |
|         $stats = $wpdb->get_row( $wpdb->prepare(
 | |
|             "SELECT 
 | |
|                 COUNT(*) as total_schedules,
 | |
|                 COUNT(CASE WHEN status = 'active' THEN 1 END) as active_schedules,
 | |
|                 COUNT(CASE WHEN status = 'paused' THEN 1 END) as paused_schedules,
 | |
|                 COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_schedules,
 | |
|                 SUM(run_count) as total_executions
 | |
|              FROM {$this->schedules_table} 
 | |
|              WHERE trainer_id = %d",
 | |
|             $trainer_id
 | |
|         ), ARRAY_A );
 | |
| 
 | |
|         return $stats;
 | |
|     }
 | |
| } |