Files
Instagram-Gallery-Sync-Pro/includes/class-cron.php

405 lines
11 KiB
PHP

<?php
/**
* Cron Handler Class
*
* Manages scheduled synchronization tasks.
*
* @package Instagram_Gallery_Sync_Pro
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Class IGSP_Cron
*/
class IGSP_Cron
{
/**
* Cron hook name
*
* @var string
*/
const SYNC_HOOK = 'igsp_sync_instagram';
/**
* Cleanup hook name
*
* @var string
*/
const CLEANUP_HOOK = 'igsp_cleanup_logs';
/**
* Lock transient name
*
* @var string
*/
const LOCK_KEY = 'igsp_sync_lock';
/**
* Logger instance
*
* @var IGSP_Logger
*/
private $logger;
/**
* Constructor
*/
public function __construct()
{
$this->logger = new IGSP_Logger();
}
/**
* Initialize cron handlers
*
* @return void
*/
public function init()
{
// Register custom intervals
add_filter('cron_schedules', array($this, 'add_custom_intervals'));
// Register cron hooks
add_action(self::SYNC_HOOK, array($this, 'run_sync'));
add_action(self::CLEANUP_HOOK, array($this, 'run_cleanup'));
}
/**
* Add custom cron intervals
*
* @param array $schedules Existing schedules
* @return array
*/
public function add_custom_intervals($schedules)
{
$schedules['igsp_30min'] = array(
'interval' => 30 * MINUTE_IN_SECONDS,
'display' => __('Every 30 Minutes', 'instagram-gallery-sync-pro'),
);
$schedules['igsp_6hours'] = array(
'interval' => 6 * HOUR_IN_SECONDS,
'display' => __('Every 6 Hours', 'instagram-gallery-sync-pro'),
);
return $schedules;
}
/**
* Get available sync intervals
*
* @return array
*/
public function get_intervals()
{
return array(
'igsp_30min' => __('Every 30 Minutes', 'instagram-gallery-sync-pro'),
'hourly' => __('Hourly', 'instagram-gallery-sync-pro'),
'igsp_6hours' => __('Every 6 Hours', 'instagram-gallery-sync-pro'),
'daily' => __('Daily', 'instagram-gallery-sync-pro'),
'weekly' => __('Weekly', 'instagram-gallery-sync-pro'),
);
}
/**
* Schedule sync job
*
* @return void
*/
public function schedule_sync()
{
// Get interval setting
$interval = get_option('igsp_sync_interval', 'daily');
// Clear existing schedule
$this->unschedule_sync();
// Schedule new sync
if (!wp_next_scheduled(self::SYNC_HOOK)) {
wp_schedule_event(time(), $interval, self::SYNC_HOOK);
$this->logger->info(sprintf(__('Scheduled sync with interval: %s', 'instagram-gallery-sync-pro'), $interval));
}
// Schedule daily cleanup if not set
if (!wp_next_scheduled(self::CLEANUP_HOOK)) {
wp_schedule_event(time(), 'daily', self::CLEANUP_HOOK);
}
}
/**
* Unschedule sync job
*
* @return void
*/
public function unschedule_sync()
{
$timestamp = wp_next_scheduled(self::SYNC_HOOK);
if ($timestamp) {
wp_unschedule_event($timestamp, self::SYNC_HOOK);
}
wp_clear_scheduled_hook(self::SYNC_HOOK);
}
/**
* Check if sync is scheduled
*
* @return bool
*/
public function is_scheduled()
{
return (bool) wp_next_scheduled(self::SYNC_HOOK);
}
/**
* Get next scheduled sync time
*
* @return int|false Timestamp or false
*/
public function get_next_sync()
{
return wp_next_scheduled(self::SYNC_HOOK);
}
/**
* Run the sync process
*
* @param bool $manual Whether this is a manual sync
* @return array Result data
*/
public function run_sync($manual = false)
{
// Check if already running
if ($this->is_locked()) {
$this->logger->warning(__('Sync already in progress, skipping.', 'instagram-gallery-sync-pro'));
return array(
'success' => false,
'message' => __('Sync already in progress.', 'instagram-gallery-sync-pro'),
);
}
// Acquire lock
$this->acquire_lock();
$result = array(
'success' => false,
'message' => '',
'posts_added' => 0,
'posts_updated' => 0,
'posts_skipped' => 0,
'errors' => array(),
);
try {
// Get username
$username = get_option('igsp_username', '');
if (empty($username)) {
throw new Exception(__('No Instagram username configured.', 'instagram-gallery-sync-pro'));
}
$this->logger->info(sprintf(__('Starting sync for @%s', 'instagram-gallery-sync-pro'), $username));
// Initialize components
$scraper = new IGSP_Scraper();
$database = new IGSP_Database();
$image_handler = new IGSP_Image_Handler();
// Fetch posts from Instagram
$posts = $scraper->fetch_profile_data($username);
if (is_wp_error($posts)) {
throw new Exception($posts->get_error_message());
}
if (empty($posts)) {
throw new Exception(__('No posts found.', 'instagram-gallery-sync-pro'));
}
// Get existing IDs
$existing_ids = $database->get_existing_instagram_ids();
$save_locally = get_option('igsp_save_locally', 'yes') === 'yes';
// Process each post
foreach ($posts as $post) {
// Validate post data
if (!$scraper->validate_post_data($post)) {
$result['posts_skipped']++;
continue;
}
$is_new = !in_array($post['instagram_id'], $existing_ids, true);
// Prepare post data
$post_data = array(
'instagram_id' => $post['instagram_id'],
'username' => $post['username'],
'post_url' => $post['post_url'],
'caption' => $post['caption'],
'likes_count' => $post['likes_count'],
'comments_count' => $post['comments_count'],
'posted_at' => $post['posted_at'],
'image_width' => $post['image_width'],
'image_height' => $post['image_height'],
);
// Download image if saving locally and is new post
if ($save_locally && $is_new && !empty($post['image_url'])) {
$image_result = $image_handler->download_image($post['image_url'], $post['instagram_id']);
if (!is_wp_error($image_result)) {
$post_data['image_local_path'] = $image_result['local_path'];
$post_data['image_thumbnail_path'] = $image_result['thumbnail_path'];
$post_data['file_size'] = $image_result['file_size'];
$post_data['image_width'] = $image_result['width'];
$post_data['image_height'] = $image_result['height'];
} else {
$result['errors'][] = sprintf(
__('Failed to download image for post %s: %s', 'instagram-gallery-sync-pro'),
$post['instagram_id'],
$image_result->get_error_message()
);
}
}
// Save to database
$saved = $database->insert_post($post_data);
if ($saved) {
if ($is_new) {
$result['posts_added']++;
} else {
$result['posts_updated']++;
}
} else {
$result['errors'][] = sprintf(
__('Failed to save post %s to database.', 'instagram-gallery-sync-pro'),
$post['instagram_id']
);
}
}
// Update last sync time
update_option('igsp_last_sync', current_time('mysql'));
// Delete old posts if auto-delete is enabled
$auto_delete_days = (int) get_option('igsp_auto_delete_days', 0);
if ($auto_delete_days > 0) {
$deleted = $database->delete_old_posts($auto_delete_days);
if ($deleted > 0) {
$this->logger->info(sprintf(__('Auto-deleted %d old posts.', 'instagram-gallery-sync-pro'), $deleted));
}
}
// Clear cache
igsp()->clear_transients();
$result['success'] = true;
$result['message'] = sprintf(
__('Sync completed. Added: %d, Updated: %d, Skipped: %d', 'instagram-gallery-sync-pro'),
$result['posts_added'],
$result['posts_updated'],
$result['posts_skipped']
);
$this->logger->success($result['message'], array(
'added' => $result['posts_added'],
'updated' => $result['posts_updated'],
'skipped' => $result['posts_skipped'],
));
} catch (Exception $e) {
$result['message'] = $e->getMessage();
$result['errors'][] = $e->getMessage();
$this->logger->error(__('Sync failed: ', 'instagram-gallery-sync-pro') . $e->getMessage());
}
// Release lock
$this->release_lock();
return $result;
}
/**
* Run cleanup tasks
*
* @return void
*/
public function run_cleanup()
{
$logger = new IGSP_Logger();
// Cleanup old logs (keep last 7 days)
$logger->delete_logs_older_than(7);
// Other cleanup tasks can be added here
}
/**
* Check if sync is locked
*
* @return bool
*/
private function is_locked()
{
return (bool) get_transient(self::LOCK_KEY);
}
/**
* Acquire sync lock
*
* @return void
*/
private function acquire_lock()
{
set_transient(self::LOCK_KEY, true, 10 * MINUTE_IN_SECONDS);
}
/**
* Release sync lock
*
* @return void
*/
private function release_lock()
{
delete_transient(self::LOCK_KEY);
}
/**
* Manually trigger sync
*
* @return array
*/
public function manual_sync()
{
return $this->run_sync(true);
}
/**
* Get sync status for AJAX
*
* @return array
*/
public function get_sync_status()
{
$last_sync = get_option('igsp_last_sync', '');
$next_sync = $this->get_next_sync();
$is_running = $this->is_locked();
$database = new IGSP_Database();
return array(
'is_running' => $is_running,
'last_sync' => $last_sync ? mysql2date(get_option('date_format') . ' ' . get_option('time_format'), $last_sync) : __('Never', 'instagram-gallery-sync-pro'),
'next_sync' => $next_sync ? date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $next_sync) : __('Not scheduled', 'instagram-gallery-sync-pro'),
'total_posts' => $database->count_posts(),
);
}
}