Make your WordPress site irresistible. Natural SEO attraction with: - robots.txt management - sitemap.xml generation - LLMs.txt support - Google integration (Analytics, Search Console, Tag Manager) - Schema.org structured data - Open Graph / Twitter Card meta tags - AMP support - Visual elements gallery - Built-in backup/restore module Includes build.sh and .distignore for WordPress-installable release ZIPs.
548 lines
18 KiB
PHP
548 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* TigerStyle SEO Backup Scheduler
|
|
*
|
|
* Handles scheduled backups, retention policies,
|
|
* and automated backup management.
|
|
*
|
|
* @package TigerStyleSEO
|
|
* @subpackage BackupScheduler
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class TigerStyleSEO_Backup_Scheduler {
|
|
|
|
/**
|
|
* Logger
|
|
*/
|
|
private $logger;
|
|
|
|
/**
|
|
* Settings
|
|
*/
|
|
private $settings;
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
$this->logger = TigerStyleSEO_Backup_Logger::instance();
|
|
$this->settings = get_option('tigerstyle_backup_settings', array());
|
|
|
|
$this->setup_hooks();
|
|
}
|
|
|
|
/**
|
|
* Setup WordPress hooks
|
|
*/
|
|
private function setup_hooks() {
|
|
// Scheduled backup hook
|
|
add_action('tigerstyle_backup_scheduled', array($this, 'run_scheduled_backup'));
|
|
|
|
// Cleanup hook
|
|
add_action('tigerstyle_backup_cleanup', array($this, 'cleanup_old_backups'));
|
|
|
|
// Admin hooks
|
|
add_action('admin_init', array($this, 'maybe_setup_schedules'));
|
|
|
|
// Plugin deactivation cleanup
|
|
register_deactivation_hook(TIGERSTYLE_HEAT_PLUGIN_FILE, array($this, 'clear_schedules'));
|
|
}
|
|
|
|
/**
|
|
* Setup schedules if needed
|
|
*/
|
|
public function maybe_setup_schedules() {
|
|
// Setup cleanup schedule if not exists
|
|
if (!wp_next_scheduled('tigerstyle_backup_cleanup')) {
|
|
wp_schedule_event(time() + 3600, 'daily', 'tigerstyle_backup_cleanup');
|
|
}
|
|
|
|
// Setup backup schedule based on settings
|
|
$this->update_backup_schedule();
|
|
}
|
|
|
|
/**
|
|
* Update backup schedule based on settings
|
|
*/
|
|
public function update_backup_schedule() {
|
|
// Clear existing schedule
|
|
wp_clear_scheduled_hook('tigerstyle_backup_scheduled');
|
|
|
|
// Setup new schedule if enabled
|
|
if (!empty($this->settings['schedule_enabled'])) {
|
|
$frequency = $this->settings['schedule_frequency'] ?? 'daily';
|
|
$start_time = $this->calculate_next_backup_time($frequency);
|
|
|
|
wp_schedule_event($start_time, $frequency, 'tigerstyle_backup_scheduled');
|
|
|
|
$this->logger->info('Backup schedule updated', array(
|
|
'frequency' => $frequency,
|
|
'next_run' => date('Y-m-d H:i:s', $start_time)
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate next backup time
|
|
*/
|
|
private function calculate_next_backup_time($frequency) {
|
|
$current_time = time();
|
|
$preferred_hour = $this->settings['schedule_time'] ?? '02:00';
|
|
|
|
// Parse preferred time
|
|
list($hour, $minute) = explode(':', $preferred_hour);
|
|
$hour = intval($hour);
|
|
$minute = intval($minute);
|
|
|
|
// Calculate next run time
|
|
switch ($frequency) {
|
|
case 'hourly':
|
|
return $current_time + HOUR_IN_SECONDS;
|
|
|
|
case 'daily':
|
|
$next_time = mktime($hour, $minute, 0);
|
|
if ($next_time <= $current_time) {
|
|
$next_time += DAY_IN_SECONDS;
|
|
}
|
|
return $next_time;
|
|
|
|
case 'weekly':
|
|
$preferred_day = $this->settings['schedule_day'] ?? 'sunday';
|
|
$day_number = $this->get_day_number($preferred_day);
|
|
$next_time = strtotime("next {$preferred_day} {$hour}:{$minute}:00");
|
|
return $next_time;
|
|
|
|
case 'monthly':
|
|
$preferred_date = $this->settings['schedule_date'] ?? 1;
|
|
$next_time = mktime($hour, $minute, 0, date('n'), $preferred_date);
|
|
if ($next_time <= $current_time) {
|
|
$next_time = mktime($hour, $minute, 0, date('n') + 1, $preferred_date);
|
|
}
|
|
return $next_time;
|
|
|
|
default:
|
|
return $current_time + DAY_IN_SECONDS;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run scheduled backup
|
|
*/
|
|
public function run_scheduled_backup() {
|
|
if (!$this->should_run_backup()) {
|
|
$this->logger->info('Scheduled backup skipped', array(
|
|
'reason' => 'Conditions not met'
|
|
));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$this->logger->info('Starting scheduled backup');
|
|
|
|
$backup_engine = new TigerStyleSEO_Backup_Engine();
|
|
|
|
$backup_options = array(
|
|
'type' => 'scheduled',
|
|
'compression' => $this->settings['compression'] ?? 'zip',
|
|
'storage_location' => $this->settings['storage_location'] ?? 'local',
|
|
'include_files' => $this->settings['include_files'] ?? true,
|
|
'include_database' => $this->settings['include_database'] ?? true,
|
|
'description' => $this->generate_scheduled_backup_description()
|
|
);
|
|
|
|
$backup_id = $backup_engine->create_backup($backup_options);
|
|
|
|
$this->logger->info('Scheduled backup completed successfully', array(
|
|
'backup_id' => $backup_id
|
|
));
|
|
|
|
// Send notification if enabled
|
|
if (!empty($this->settings['email_notifications'])) {
|
|
$this->send_backup_notification($backup_id, true);
|
|
}
|
|
|
|
// Update last backup time
|
|
update_option('tigerstyle_last_scheduled_backup', time());
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Scheduled backup failed: ' . $e->getMessage());
|
|
|
|
// Send failure notification
|
|
if (!empty($this->settings['email_notifications'])) {
|
|
$this->send_backup_notification(null, false, $e->getMessage());
|
|
}
|
|
|
|
// Record failure
|
|
$this->record_backup_failure($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if backup should run
|
|
*/
|
|
private function should_run_backup() {
|
|
// Check if backups are enabled
|
|
if (empty($this->settings['schedule_enabled'])) {
|
|
return false;
|
|
}
|
|
|
|
// Check system load (don't backup during high load)
|
|
if ($this->is_system_under_high_load()) {
|
|
$this->logger->warning('Backup skipped due to high system load');
|
|
return false;
|
|
}
|
|
|
|
// Check disk space
|
|
if (!$this->has_sufficient_disk_space()) {
|
|
$this->logger->warning('Backup skipped due to insufficient disk space');
|
|
return false;
|
|
}
|
|
|
|
// Check if another backup is running
|
|
if ($this->is_backup_running()) {
|
|
$this->logger->warning('Backup skipped because another backup is running');
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if system is under high load
|
|
*/
|
|
private function is_system_under_high_load() {
|
|
if (!function_exists('sys_getloadavg')) {
|
|
return false;
|
|
}
|
|
|
|
$load = sys_getloadavg();
|
|
$max_load = $this->settings['max_load_average'] ?? 2.0;
|
|
|
|
return $load && $load[0] > $max_load;
|
|
}
|
|
|
|
/**
|
|
* Check if there's sufficient disk space
|
|
*/
|
|
private function has_sufficient_disk_space() {
|
|
$required_space_mb = $this->settings['min_disk_space'] ?? 1024; // 1GB default
|
|
$required_space = $required_space_mb * 1024 * 1024;
|
|
|
|
$available_space = disk_free_space(ABSPATH);
|
|
|
|
return $available_space === false || $available_space > $required_space;
|
|
}
|
|
|
|
/**
|
|
* Check if backup is currently running
|
|
*/
|
|
private function is_backup_running() {
|
|
global $wpdb;
|
|
|
|
// Check for active backup processes
|
|
$active_backups = get_transient('tigerstyle_active_backup_processes');
|
|
|
|
if ($active_backups && count($active_backups) > 0) {
|
|
// Clean up stale processes (older than 2 hours)
|
|
$current_time = time();
|
|
foreach ($active_backups as $key => $timestamp) {
|
|
if ($current_time - $timestamp > 7200) {
|
|
unset($active_backups[$key]);
|
|
}
|
|
}
|
|
|
|
set_transient('tigerstyle_active_backup_processes', $active_backups, 3600);
|
|
|
|
return count($active_backups) > 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Generate scheduled backup description
|
|
*/
|
|
private function generate_scheduled_backup_description() {
|
|
$frequency = $this->settings['schedule_frequency'] ?? 'daily';
|
|
$timestamp = current_time('mysql');
|
|
|
|
return sprintf(
|
|
__('Scheduled %s backup - %s', 'tigerstyle-heat'),
|
|
$frequency,
|
|
$timestamp
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Send backup notification email
|
|
*/
|
|
private function send_backup_notification($backup_id, $success, $error_message = '') {
|
|
if (empty($this->settings['notification_email'])) {
|
|
return;
|
|
}
|
|
|
|
$site_name = get_bloginfo('name');
|
|
$site_url = home_url();
|
|
|
|
if ($success) {
|
|
$subject = sprintf(__('[%s] Scheduled Backup Completed Successfully', 'tigerstyle-heat'), $site_name);
|
|
$message = sprintf(
|
|
__("Your scheduled backup has completed successfully.\n\nBackup ID: %s\nSite: %s\nCompleted: %s\n\nYou can manage your backups from the WordPress admin panel.", 'tigerstyle-heat'),
|
|
$backup_id,
|
|
$site_url,
|
|
current_time('Y-m-d H:i:s')
|
|
);
|
|
} else {
|
|
$subject = sprintf(__('[%s] Scheduled Backup Failed', 'tigerstyle-heat'), $site_name);
|
|
$message = sprintf(
|
|
__("Your scheduled backup has failed.\n\nSite: %s\nFailed: %s\nError: %s\n\nPlease check your backup settings and try again.", 'tigerstyle-heat'),
|
|
$site_url,
|
|
current_time('Y-m-d H:i:s'),
|
|
$error_message
|
|
);
|
|
}
|
|
|
|
wp_mail($this->settings['notification_email'], $subject, $message);
|
|
}
|
|
|
|
/**
|
|
* Record backup failure
|
|
*/
|
|
private function record_backup_failure($error_message) {
|
|
$failures = get_option('tigerstyle_scheduled_backup_failures', array());
|
|
|
|
$failures[] = array(
|
|
'timestamp' => time(),
|
|
'error' => $error_message
|
|
);
|
|
|
|
// Keep only last 10 failures
|
|
$failures = array_slice($failures, -10);
|
|
|
|
update_option('tigerstyle_scheduled_backup_failures', $failures);
|
|
}
|
|
|
|
/**
|
|
* Cleanup old backups
|
|
*/
|
|
public function cleanup_old_backups() {
|
|
$retention_days = $this->settings['retention_days'] ?? 30;
|
|
|
|
if ($retention_days <= 0) {
|
|
return; // Unlimited retention
|
|
}
|
|
|
|
try {
|
|
$storage_manager = new TigerStyleSEO_Storage_Manager();
|
|
$deleted_count = $storage_manager->cleanup_old_backups($retention_days);
|
|
|
|
$this->logger->info('Old backups cleanup completed', array(
|
|
'retention_days' => $retention_days,
|
|
'deleted_count' => $deleted_count
|
|
));
|
|
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Backup cleanup failed: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get next scheduled backup time
|
|
*/
|
|
public function get_next_backup_time() {
|
|
$timestamp = wp_next_scheduled('tigerstyle_backup_scheduled');
|
|
|
|
if (!$timestamp) {
|
|
return null;
|
|
}
|
|
|
|
return array(
|
|
'timestamp' => $timestamp,
|
|
'formatted' => date('Y-m-d H:i:s', $timestamp),
|
|
'human' => human_time_diff($timestamp, current_time('timestamp'))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get backup schedule status
|
|
*/
|
|
public function get_schedule_status() {
|
|
$status = array(
|
|
'enabled' => !empty($this->settings['schedule_enabled']),
|
|
'frequency' => $this->settings['schedule_frequency'] ?? 'daily',
|
|
'next_run' => $this->get_next_backup_time(),
|
|
'last_run' => null,
|
|
'last_success' => null,
|
|
'failure_count' => 0
|
|
);
|
|
|
|
// Get last backup time
|
|
$last_backup_time = get_option('tigerstyle_last_scheduled_backup');
|
|
if ($last_backup_time) {
|
|
$status['last_run'] = array(
|
|
'timestamp' => $last_backup_time,
|
|
'formatted' => date('Y-m-d H:i:s', $last_backup_time),
|
|
'human' => human_time_diff($last_backup_time, current_time('timestamp')) . __(' ago', 'tigerstyle-heat')
|
|
);
|
|
}
|
|
|
|
// Get failure information
|
|
$failures = get_option('tigerstyle_scheduled_backup_failures', array());
|
|
$status['failure_count'] = count($failures);
|
|
|
|
if (!empty($failures)) {
|
|
$last_failure = end($failures);
|
|
$status['last_failure'] = array(
|
|
'timestamp' => $last_failure['timestamp'],
|
|
'formatted' => date('Y-m-d H:i:s', $last_failure['timestamp']),
|
|
'error' => $last_failure['error']
|
|
);
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Enable scheduled backups
|
|
*/
|
|
public function enable_schedule($frequency = 'daily', $time = '02:00') {
|
|
$this->settings['schedule_enabled'] = true;
|
|
$this->settings['schedule_frequency'] = $frequency;
|
|
$this->settings['schedule_time'] = $time;
|
|
|
|
update_option('tigerstyle_backup_settings', $this->settings);
|
|
|
|
$this->update_backup_schedule();
|
|
|
|
$this->logger->info('Backup schedule enabled', array(
|
|
'frequency' => $frequency,
|
|
'time' => $time
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Disable scheduled backups
|
|
*/
|
|
public function disable_schedule() {
|
|
$this->settings['schedule_enabled'] = false;
|
|
update_option('tigerstyle_backup_settings', $this->settings);
|
|
|
|
wp_clear_scheduled_hook('tigerstyle_backup_scheduled');
|
|
|
|
$this->logger->info('Backup schedule disabled');
|
|
}
|
|
|
|
/**
|
|
* Run backup immediately
|
|
*/
|
|
public function run_backup_now() {
|
|
// Mark as manual backup
|
|
$backup_engine = new TigerStyleSEO_Backup_Engine();
|
|
|
|
$backup_options = array(
|
|
'type' => 'manual',
|
|
'compression' => $this->settings['compression'] ?? 'zip',
|
|
'storage_location' => $this->settings['storage_location'] ?? 'local',
|
|
'include_files' => $this->settings['include_files'] ?? true,
|
|
'include_database' => $this->settings['include_database'] ?? true,
|
|
'description' => __('Manual backup - ', 'tigerstyle-heat') . current_time('mysql')
|
|
);
|
|
|
|
return $backup_engine->create_backup($backup_options);
|
|
}
|
|
|
|
/**
|
|
* Get day number for date calculations
|
|
*/
|
|
private function get_day_number($day_name) {
|
|
$days = array(
|
|
'sunday' => 0,
|
|
'monday' => 1,
|
|
'tuesday' => 2,
|
|
'wednesday' => 3,
|
|
'thursday' => 4,
|
|
'friday' => 5,
|
|
'saturday' => 6
|
|
);
|
|
|
|
return $days[strtolower($day_name)] ?? 0;
|
|
}
|
|
|
|
/**
|
|
* Clear all schedules (for plugin deactivation)
|
|
*/
|
|
public function clear_schedules() {
|
|
wp_clear_scheduled_hook('tigerstyle_backup_scheduled');
|
|
wp_clear_scheduled_hook('tigerstyle_backup_cleanup');
|
|
|
|
$this->logger->info('All backup schedules cleared');
|
|
}
|
|
|
|
/**
|
|
* Get backup statistics
|
|
*/
|
|
public function get_backup_statistics($days = 30) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_metadata';
|
|
$since_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$stats = $wpdb->get_row(
|
|
$wpdb->prepare(
|
|
"SELECT
|
|
COUNT(*) as total_backups,
|
|
SUM(CASE WHEN backup_id LIKE 'backup_%scheduled%' THEN 1 ELSE 0 END) as scheduled_backups,
|
|
SUM(CASE WHEN backup_id LIKE 'backup_%manual%' THEN 1 ELSE 0 END) as manual_backups,
|
|
AVG(file_size) as average_size,
|
|
SUM(file_size) as total_size
|
|
FROM {$table_name}
|
|
WHERE created_at >= %s",
|
|
$since_date
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
if ($stats) {
|
|
$stats['average_size_formatted'] = size_format($stats['average_size']);
|
|
$stats['total_size_formatted'] = size_format($stats['total_size']);
|
|
$stats['success_rate'] = $this->calculate_success_rate($days);
|
|
}
|
|
|
|
return $stats ?: array();
|
|
}
|
|
|
|
/**
|
|
* Calculate backup success rate
|
|
*/
|
|
private function calculate_success_rate($days) {
|
|
$failures = get_option('tigerstyle_scheduled_backup_failures', array());
|
|
$recent_failures = array_filter($failures, function($failure) use ($days) {
|
|
return $failure['timestamp'] > strtotime("-{$days} days");
|
|
});
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'tigerstyle_backup_metadata';
|
|
$since_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$total_attempts = $wpdb->get_var(
|
|
$wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$table_name} WHERE created_at >= %s AND backup_id LIKE '%scheduled%'",
|
|
$since_date
|
|
)
|
|
);
|
|
|
|
$total_attempts += count($recent_failures);
|
|
|
|
if ($total_attempts === 0) {
|
|
return 100;
|
|
}
|
|
|
|
$success_rate = (($total_attempts - count($recent_failures)) / $total_attempts) * 100;
|
|
return round($success_rate, 2);
|
|
}
|
|
} |