405 lines
11 KiB
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(),
|
|
);
|
|
}
|
|
}
|