tigerstyle-heat/includes/modules/class-backup-restore.php
Ryan Malloy 0028738e33 Initial commit: TigerStyle Heat v2.0.0
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.
2026-05-27 13:41:35 -06:00

572 lines
20 KiB
PHP

<?php
/**
* Enterprise Backup & Restore Module for TigerStyle Heat
*
* Provides comprehensive backup and restore functionality including:
* - File system backups with chunked processing
* - Database backups with table-level control
* - S3 and S3-compatible storage integration
* - Advanced compression with multiple formats
* - Complete WordPress reset functionality
* - Enterprise-grade logging and validation
*
* @package TigerStyleSEO
* @subpackage BackupRestore
* @version 1.0.0
* @since 2.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class TigerStyleSEO_Backup_Restore {
/**
* Single instance
*/
private static $instance = null;
/**
* Backup engine
*/
private $backup_engine;
/**
* Restore engine
*/
private $restore_engine;
/**
* Storage manager
*/
private $storage_manager;
/**
* Logger
*/
private $logger;
/**
* Validator
*/
private $validator;
/**
* Get instance
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init();
}
/**
* Initialize module
*/
private function init() {
// Load dependencies
$this->load_dependencies();
// Defer backup system initialization until needed to avoid database errors during startup
add_action('admin_init', array($this, 'init_backup_system'), 10);
// Setup hooks
$this->setup_hooks();
}
/**
* Initialize backup system components
*/
public function init_backup_system() {
// Only initialize if we're in admin and not already initialized
if (!is_admin() || $this->backup_engine) {
return;
}
// Initialize components
$this->backup_engine = TigerStyleSEO_Backup_Engine::instance();
$this->restore_engine = TigerStyleSEO_Restore_Engine::instance();
$this->storage_manager = TigerStyleSEO_Storage_Manager::instance();
$this->logger = TigerStyleSEO_Backup_Logger::instance();
$this->validator = TigerStyleSEO_Backup_Validator::instance();
}
/**
* Load dependencies
*/
private function load_dependencies() {
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-backup-engine.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-restore-engine.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-storage-manager.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-backup-logger.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-backup-validator.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-backup-scheduler.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/class-compression-manager.php';
require_once TIGERSTYLE_HEAT_PLUGIN_DIR . 'includes/backup/ajax-handlers.php';
}
/**
* Setup WordPress hooks
*/
private function setup_hooks() {
// AJAX handlers
add_action('wp_ajax_tigerstyle_create_backup', array($this, 'ajax_create_backup'));
add_action('wp_ajax_tigerstyle_restore_backup', array($this, 'ajax_restore_backup'));
add_action('wp_ajax_tigerstyle_delete_backup', array($this, 'ajax_delete_backup'));
add_action('wp_ajax_tigerstyle_download_backup', array($this, 'ajax_download_backup'));
add_action('wp_ajax_tigerstyle_upload_backup', array($this, 'ajax_upload_backup'));
add_action('wp_ajax_tigerstyle_reset_wordpress', array($this, 'ajax_reset_wordpress'));
add_action('wp_ajax_tigerstyle_reset_database', array($this, 'ajax_reset_database'));
add_action('wp_ajax_tigerstyle_backup_progress', array($this, 'ajax_backup_progress'));
add_action('wp_ajax_tigerstyle_validate_backup', array($this, 'ajax_validate_backup'));
// Scheduled backup hooks
add_action('tigerstyle_backup_scheduled', array($this, 'run_scheduled_backup'));
add_action('tigerstyle_backup_cleanup', array($this, 'cleanup_old_backups'));
// Admin hooks
add_action('admin_post_tigerstyle_backup_settings', array($this, 'save_backup_settings'));
add_action('admin_notices', array($this, 'display_backup_notices'));
// Cron hooks
if (!wp_next_scheduled('tigerstyle_backup_cleanup')) {
wp_schedule_event(time(), 'daily', 'tigerstyle_backup_cleanup');
}
}
/**
* Render admin page
*/
public function render_admin_page() {
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.', 'tigerstyle-heat'));
}
// Handle form submissions
$this->handle_form_submissions();
// Get backup list
$backups = $this->get_backup_list();
$storage_stats = $this->storage_manager->get_storage_stats();
$compression_methods = $this->get_available_compression_methods();
include TIGERSTYLE_HEAT_PLUGIN_DIR . 'admin/pages/backup-restore.php';
}
/**
* Handle form submissions
*/
private function handle_form_submissions() {
if (!isset($_POST['tigerstyle_backup_nonce']) ||
!wp_verify_nonce($_POST['tigerstyle_backup_nonce'], 'tigerstyle_backup_action')) {
return;
}
$action = sanitize_text_field($_POST['backup_action']);
switch ($action) {
case 'create_backup':
$this->handle_create_backup();
break;
case 'restore_backup':
$this->handle_restore_backup();
break;
case 'delete_backup':
$this->handle_delete_backup();
break;
case 'save_settings':
$this->handle_save_settings();
break;
}
}
/**
* Create backup via AJAX
*/
public function ajax_create_backup() {
check_ajax_referer('tigerstyle_backup_action', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$backup_type = sanitize_text_field($_POST['backup_type']);
$compression = sanitize_text_field($_POST['compression']);
$storage_location = sanitize_text_field($_POST['storage_location']);
$include_files = isset($_POST['include_files']) ? (bool)$_POST['include_files'] : true;
$include_database = isset($_POST['include_database']) ? (bool)$_POST['include_database'] : true;
try {
$backup_id = $this->backup_engine->create_backup(array(
'type' => $backup_type,
'compression' => $compression,
'storage_location' => $storage_location,
'include_files' => $include_files,
'include_database' => $include_database,
'description' => sanitize_textarea_field($_POST['description'])
));
wp_send_json_success(array(
'backup_id' => $backup_id,
'message' => __('Backup started successfully', 'tigerstyle-heat')
));
} catch (Exception $e) {
$this->logger->error('Backup creation failed: ' . $e->getMessage());
wp_send_json_error($e->getMessage());
}
}
/**
* Restore backup via AJAX
*/
public function ajax_restore_backup() {
check_ajax_referer('tigerstyle_backup_action', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$backup_id = sanitize_text_field($_POST['backup_id']);
$restore_files = isset($_POST['restore_files']) ? (bool)$_POST['restore_files'] : true;
$restore_database = isset($_POST['restore_database']) ? (bool)$_POST['restore_database'] : true;
try {
// Validate backup first
if (!$this->validator->validate_backup($backup_id)) {
throw new Exception(__('Invalid or corrupted backup file', 'tigerstyle-heat'));
}
$restore_id = $this->restore_engine->restore_backup(array(
'backup_id' => $backup_id,
'restore_files' => $restore_files,
'restore_database' => $restore_database
));
wp_send_json_success(array(
'restore_id' => $restore_id,
'message' => __('Restore started successfully', 'tigerstyle-heat')
));
} catch (Exception $e) {
$this->logger->error('Restore failed: ' . $e->getMessage());
wp_send_json_error($e->getMessage());
}
}
/**
* Reset WordPress (remove default pages/posts)
*/
public function ajax_reset_wordpress() {
check_ajax_referer('tigerstyle_backup_action', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$confirmation = sanitize_text_field($_POST['confirmation']);
if ($confirmation !== 'RESET_WORDPRESS') {
wp_send_json_error(__('Invalid confirmation text', 'tigerstyle-heat'));
}
try {
$this->reset_wordpress_content();
wp_send_json_success(__('WordPress content reset successfully', 'tigerstyle-heat'));
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
}
/**
* Reset database (complete database wipe)
*/
public function ajax_reset_database() {
check_ajax_referer('tigerstyle_backup_action', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
$confirmation_code = sanitize_text_field($_POST['confirmation_code']);
$expected_code = get_transient('tigerstyle_reset_code_' . get_current_user_id());
if (!$expected_code || $confirmation_code !== $expected_code) {
wp_send_json_error(__('Invalid confirmation code', 'tigerstyle-heat'));
}
$final_confirmation = sanitize_text_field($_POST['final_confirmation']);
if ($final_confirmation !== 'YES_DELETE_EVERYTHING') {
wp_send_json_error(__('Final confirmation required', 'tigerstyle-heat'));
}
try {
$this->reset_database_completely();
delete_transient('tigerstyle_reset_code_' . get_current_user_id());
wp_send_json_success(__('Database reset successfully', 'tigerstyle-heat'));
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
}
/**
* Get backup progress
*/
public function ajax_backup_progress() {
check_ajax_referer('tigerstyle_backup_action', 'nonce');
$operation_id = sanitize_text_field($_POST['operation_id']);
$progress = get_transient('tigerstyle_backup_progress_' . $operation_id);
if ($progress === false) {
wp_send_json_error(__('Operation not found', 'tigerstyle-heat'));
}
wp_send_json_success($progress);
}
/**
* Get list of available backups
*/
public function get_backup_list() {
return $this->storage_manager->list_backups();
}
/**
* Get available compression methods
*/
public function get_available_compression_methods() {
$methods = array();
if (class_exists('ZipArchive')) {
$methods['zip'] = __('ZIP Compression', 'tigerstyle-heat');
}
if (function_exists('gzopen')) {
$methods['tar.gz'] = __('TAR.GZ Compression', 'tigerstyle-heat');
}
if (function_exists('bzopen')) {
$methods['tar.bz2'] = __('TAR.BZ2 Compression', 'tigerstyle-heat');
}
if (empty($methods)) {
$methods['none'] = __('No Compression (Fallback)', 'tigerstyle-heat');
}
return $methods;
}
/**
* Reset WordPress content (default pages/posts)
*/
private function reset_wordpress_content() {
global $wpdb;
// Create backup before reset
$backup_id = $this->backup_engine->create_backup(array(
'type' => 'quick',
'description' => 'Auto-backup before WordPress reset',
'include_files' => false,
'include_database' => true
));
// Delete default posts
$default_posts = get_posts(array(
'post_type' => array('post', 'page'),
'posts_per_page' => -1,
'post_status' => 'any'
));
foreach ($default_posts as $post) {
wp_delete_post($post->ID, true);
}
// Delete default comments
$wpdb->query("DELETE FROM {$wpdb->comments}");
$wpdb->query("DELETE FROM {$wpdb->commentmeta}");
// Reset comment count
$wpdb->query("UPDATE {$wpdb->posts} SET comment_count = 0");
// Clear caches
wp_cache_flush();
$this->logger->info('WordPress content reset completed', array(
'backup_id' => $backup_id,
'posts_deleted' => count($default_posts)
));
}
/**
* Reset database completely
*/
private function reset_database_completely() {
global $wpdb;
// Get all tables
$tables = $wpdb->get_col("SHOW TABLES");
// Disable foreign key checks
$wpdb->query("SET FOREIGN_KEY_CHECKS = 0");
try {
// Drop all tables
foreach ($tables as $table) {
$wpdb->query("DROP TABLE IF EXISTS `{$table}`");
}
$this->logger->critical('Complete database reset performed', array(
'tables_dropped' => count($tables),
'user_id' => get_current_user_id()
));
} finally {
// Re-enable foreign key checks
$wpdb->query("SET FOREIGN_KEY_CHECKS = 1");
}
}
/**
* Generate reset confirmation code
*/
public function generate_reset_code() {
$code = wp_generate_password(6, false, false);
$code = strtoupper($code);
set_transient('tigerstyle_reset_code_' . get_current_user_id(), $code, 300); // 5 minutes
return $code;
}
/**
* Display admin notices
*/
public function display_backup_notices() {
// Check for backup failures
$failed_backups = get_option('tigerstyle_failed_backups', array());
if (!empty($failed_backups)) {
echo '<div class="notice notice-error"><p>';
echo __('Some backup operations have failed. Please check the backup logs.', 'tigerstyle-heat');
echo '</p></div>';
}
// Check storage space
$storage_stats = $this->storage_manager->get_storage_stats();
if (isset($storage_stats['usage_percent']) && $storage_stats['usage_percent'] > 90) {
echo '<div class="notice notice-warning"><p>';
echo __('Backup storage is nearly full. Consider cleaning up old backups.', 'tigerstyle-heat');
echo '</p></div>';
}
}
/**
* Run scheduled backup
*/
public function run_scheduled_backup() {
$settings = get_option('tigerstyle_backup_settings', array());
if (!isset($settings['schedule_enabled']) || !$settings['schedule_enabled']) {
return;
}
try {
$this->backup_engine->create_backup(array(
'type' => 'scheduled',
'compression' => $settings['compression'] ?? 'zip',
'storage_location' => $settings['storage_location'] ?? 'local',
'include_files' => $settings['include_files'] ?? true,
'include_database' => $settings['include_database'] ?? true,
'description' => 'Scheduled backup - ' . current_time('mysql')
));
} catch (Exception $e) {
$this->logger->error('Scheduled backup failed: ' . $e->getMessage());
}
}
/**
* Cleanup old backups
*/
public function cleanup_old_backups() {
$settings = get_option('tigerstyle_backup_settings', array());
$retention_days = $settings['retention_days'] ?? 30;
$this->storage_manager->cleanup_old_backups($retention_days);
}
/**
* Get backup settings
*/
public function get_backup_settings() {
$defaults = array(
'compression' => 'zip',
'storage_location' => 'local',
'schedule_enabled' => false,
'schedule_frequency' => 'daily',
'retention_days' => 30,
'include_files' => true,
'include_database' => true,
'chunk_size' => 5, // MB
's3_bucket' => '',
's3_access_key' => '',
's3_secret_key' => '',
's3_region' => 'us-east-1',
's3_endpoint' => '', // For S3-compatible services
'email_notifications' => false,
'notification_email' => get_option('admin_email')
);
return wp_parse_args(get_option('tigerstyle_backup_settings', array()), $defaults);
}
/**
* Save backup settings
*/
public function save_backup_settings() {
if (!isset($_POST['tigerstyle_backup_nonce']) || !wp_verify_nonce($_POST['tigerstyle_backup_nonce'], 'tigerstyle_backup_settings')) {
wp_die(__('Security check failed', 'tigerstyle-heat'));
}
if (!current_user_can('manage_options')) {
wp_die(__('Insufficient permissions', 'tigerstyle-heat'));
}
$settings = array(
'compression' => sanitize_text_field($_POST['compression']),
'storage_location' => sanitize_text_field($_POST['storage_location']),
'schedule_enabled' => isset($_POST['schedule_enabled']),
'schedule_frequency' => sanitize_text_field($_POST['schedule_frequency']),
'retention_days' => absint($_POST['retention_days']),
'include_files' => isset($_POST['include_files']),
'include_database' => isset($_POST['include_database']),
'chunk_size' => absint($_POST['chunk_size']),
's3_bucket' => sanitize_text_field($_POST['s3_bucket']),
's3_access_key' => sanitize_text_field($_POST['s3_access_key']),
's3_secret_key' => sanitize_text_field($_POST['s3_secret_key']),
's3_region' => sanitize_text_field($_POST['s3_region']),
's3_endpoint' => sanitize_url($_POST['s3_endpoint']),
'email_notifications' => isset($_POST['email_notifications']),
'notification_email' => sanitize_email($_POST['notification_email'])
);
update_option('tigerstyle_backup_settings', $settings);
// Update scheduled backup if settings changed
if ($settings['schedule_enabled']) {
wp_clear_scheduled_hook('tigerstyle_backup_scheduled');
wp_schedule_event(time(), $settings['schedule_frequency'], 'tigerstyle_backup_scheduled');
} else {
wp_clear_scheduled_hook('tigerstyle_backup_scheduled');
}
wp_redirect(add_query_arg('message', 'backup_settings_saved', wp_get_referer()));
exit;
}
}