5 * MINUTE_IN_SECONDS, 'display' => __('Every 5 Minutes', 'hvac-community-events') ); $schedules['every_15_minutes'] = array( 'interval' => 15 * MINUTE_IN_SECONDS, 'display' => __('Every 15 Minutes', 'hvac-community-events') ); $schedules['every_30_minutes'] = array( 'interval' => 30 * MINUTE_IN_SECONDS, 'display' => __('Every 30 Minutes', 'hvac-community-events') ); $schedules['every_6_hours'] = array( 'interval' => 6 * HOUR_IN_SECONDS, 'display' => __('Every 6 Hours', 'hvac-community-events') ); return $schedules; } /** * Schedule the sync cron event * * @param string|null $interval Optional interval override * @return bool True if scheduled successfully */ public function schedule_sync($interval = null) { // Unschedule any existing event first $this->unschedule_sync(); // Get interval from option if not provided if (null === $interval) { $interval = get_option(self::OPTION_INTERVAL, 'every_5_minutes'); } // Schedule the event $result = wp_schedule_event(time(), $interval, self::CRON_HOOK); if ($result !== false) { if (class_exists('HVAC_Logger')) { HVAC_Logger::info("Scheduled Zoho sync with interval: {$interval}", 'ZohoScheduledSync'); } return true; } return false; } /** * Unschedule the sync cron event * * @return bool True if unscheduled successfully */ public function unschedule_sync() { $timestamp = wp_next_scheduled(self::CRON_HOOK); if ($timestamp) { wp_unschedule_event($timestamp, self::CRON_HOOK); if (class_exists('HVAC_Logger')) { HVAC_Logger::info('Unscheduled Zoho sync', 'ZohoScheduledSync'); } return true; } return false; } /** * Check if sync is currently scheduled * * @return bool */ public function is_scheduled() { return wp_next_scheduled(self::CRON_HOOK) !== false; } /** * Get next scheduled sync time * * @return int|false Timestamp or false if not scheduled */ public function get_next_scheduled() { return wp_next_scheduled(self::CRON_HOOK); } /** * Handle first-time option creation * * @param string $option Option name * @param mixed $value Option value */ public function on_setting_added($option, $value) { if ($value === '1' || $value === 1 || $value === true) { $this->schedule_sync(); } } /** * Handle setting enabled/disabled change * * @param mixed $old_value Old option value * @param mixed $new_value New option value */ public function on_setting_change($old_value, $new_value) { if ($new_value === '1' || $new_value === 1 || $new_value === true) { $this->schedule_sync(); } else { $this->unschedule_sync(); } } /** * Handle interval change * * @param mixed $old_value Old interval value * @param mixed $new_value New interval value */ public function on_interval_change($old_value, $new_value) { // Only reschedule if currently enabled if (get_option(self::OPTION_ENABLED) === '1') { $this->schedule_sync($new_value); } } /** * Run the scheduled sync * * This is the main cron callback that syncs all data types. */ public function run_scheduled_sync() { $start_time = time(); if (class_exists('HVAC_Logger')) { HVAC_Logger::info('Starting scheduled Zoho sync', 'ZohoScheduledSync'); } // Get last sync time for incremental sync $last_sync = $this->get_last_sync_time(); // Initialize results $results = array( 'started_at' => date('Y-m-d H:i:s', $start_time), 'last_sync_from' => $last_sync ? date('Y-m-d H:i:s', $last_sync) : 'Never', 'events' => null, 'users' => null, 'attendees' => null, 'rsvps' => null, 'purchases' => null, 'errors' => array(), ); try { // Load dependencies require_once HVAC_PLUGIN_DIR . 'includes/zoho/class-zoho-sync.php'; $sync = new HVAC_Zoho_Sync(); // Sync each type with since_timestamp for incremental sync // Events $results['events'] = $this->sync_all_batches($sync, 'sync_events', $last_sync); // Users/Trainers $results['users'] = $this->sync_all_batches($sync, 'sync_users', $last_sync); // Attendees $results['attendees'] = $this->sync_all_batches($sync, 'sync_attendees', $last_sync); // RSVPs $results['rsvps'] = $this->sync_all_batches($sync, 'sync_rsvps', $last_sync); // Purchases/Orders $results['purchases'] = $this->sync_all_batches($sync, 'sync_purchases', $last_sync); } catch (Exception $e) { $results['errors'][] = $e->getMessage(); if (class_exists('HVAC_Logger')) { HVAC_Logger::error('Scheduled sync error: ' . $e->getMessage(), 'ZohoScheduledSync'); } } // Update last sync time $this->set_last_sync_time($start_time); // Calculate totals $total_synced = 0; $total_failed = 0; foreach (array('events', 'users', 'attendees', 'rsvps', 'purchases') as $type) { if (isset($results[$type]['synced'])) { $total_synced += $results[$type]['synced']; } if (isset($results[$type]['failed'])) { $total_failed += $results[$type]['failed']; } } $results['completed_at'] = date('Y-m-d H:i:s'); $results['duration_seconds'] = time() - $start_time; $results['total_synced'] = $total_synced; $results['total_failed'] = $total_failed; // Save last result update_option(self::OPTION_LAST_RESULT, $results); if (class_exists('HVAC_Logger')) { HVAC_Logger::info("Scheduled sync completed: {$total_synced} synced, {$total_failed} failed", 'ZohoScheduledSync'); } return $results; } /** * Sync all batches for a given sync method * * @param HVAC_Zoho_Sync $sync Sync instance * @param string $method Method name (e.g., 'sync_events') * @param int|null $since_timestamp Optional timestamp for incremental sync * @return array Aggregated results */ private function sync_all_batches($sync, $method, $since_timestamp = null) { $offset = 0; $limit = 50; $aggregated = array( 'total' => 0, 'synced' => 0, 'failed' => 0, 'errors' => array(), ); do { // Call the sync method with since_timestamp support $result = $sync->$method($offset, $limit, $since_timestamp); // Aggregate results $aggregated['total'] = $result['total'] ?? 0; $aggregated['synced'] += $result['synced'] ?? 0; $aggregated['failed'] += $result['failed'] ?? 0; if (!empty($result['errors'])) { $aggregated['errors'] = array_merge($aggregated['errors'], $result['errors']); } // Check for more batches $has_more = $result['has_more'] ?? false; $offset = $result['next_offset'] ?? ($offset + $limit); } while ($has_more); return $aggregated; } /** * Get the last sync timestamp * * @return int|null Timestamp or null if never synced */ public function get_last_sync_time() { $time = get_option(self::OPTION_LAST_SYNC); return $time ? (int) $time : null; } /** * Set the last sync timestamp * * @param int $timestamp Timestamp */ public function set_last_sync_time($timestamp) { update_option(self::OPTION_LAST_SYNC, $timestamp); } /** * Get the last sync result * * @return array|null Result array or null */ public function get_last_sync_result() { return get_option(self::OPTION_LAST_RESULT); } /** * Get sync status summary * * @return array Status information */ public function get_status() { $is_enabled = get_option(self::OPTION_ENABLED) === '1'; $interval = get_option(self::OPTION_INTERVAL, 'every_5_minutes'); $last_sync = $this->get_last_sync_time(); $next_sync = $this->get_next_scheduled(); $last_result = $this->get_last_sync_result(); return array( 'enabled' => $is_enabled, 'interval' => $interval, 'is_scheduled' => $this->is_scheduled(), 'last_sync_time' => $last_sync, 'last_sync_formatted' => $last_sync ? date('Y-m-d H:i:s', $last_sync) : 'Never', 'next_sync_time' => $next_sync, 'next_sync_formatted' => $next_sync ? date('Y-m-d H:i:s', $next_sync) : 'Not scheduled', 'last_result' => $last_result, ); } /** * Run sync manually (for "Run Now" button) * * @return array Sync results */ public function run_now() { return $this->run_scheduled_sync(); } }