try_load_dependencies(); $this->init_hooks(); } /** * Try to load dependencies gracefully */ private function try_load_dependencies() { $includes_path = TIGERSTYLE_LIFE9_COMPLETE_PATH . 'includes/'; // Check if includes directory exists if (!is_dir($includes_path)) { return false; } try { // Try to load core classes $required_files = [ 'class-storage-manager.php' ]; foreach ($required_files as $file) { $file_path = $includes_path . $file; if (file_exists($file_path)) { require_once $file_path; } } // If we get here, we have at least some functionality $this->full_functionality = true; } catch (Exception $e) { // Log error but continue with basic functionality error_log('TigerStyle Life9: ' . $e->getMessage()); $this->full_functionality = false; } } /** * Prevent cloning */ private function __clone() {} /** * Prevent unserialization */ public function __wakeup() {} /** * Initialize WordPress hooks */ private function init_hooks() { // Admin hooks if (is_admin()) { add_action('admin_menu', [$this, 'add_admin_menu']); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); add_action('admin_init', [$this, 'handle_early_admin_requests']); } // Global hooks (front-end and admin) add_action('init', [$this, 'handle_early_admin_requests']); // Handle time-limited downloads on front-end too // Load text domain for translations add_action('init', [$this, 'load_textdomain']); // Register scheduled backup hook add_action('tigerstyle_life9_scheduled_backup', [$this, 'execute_scheduled_backup']); // Register custom cron intervals add_filter('cron_schedules', [$this, 'add_custom_cron_intervals']); // Register activation hook for database setup register_activation_hook(TIGERSTYLE_LIFE9_COMPLETE_FILE, [$this, 'activate_plugin']); // Register AJAX handlers add_action('wp_ajax_generate_download_link', [$this, 'handle_generate_download_link']); add_action('wp_ajax_cleanup_expired_tokens', [$this, 'cleanup_expired_tokens']); add_action('wp_ajax_create_table_manual', [$this, 'handle_manual_table_creation']); } /** * Load plugin text domain for translations */ public function load_textdomain() { load_plugin_textdomain( 'tigerstyle-life9', false, dirname(plugin_basename(__FILE__)) . '/languages' ); } /** * Handle early admin requests (like downloads) before HTML output */ public function handle_early_admin_requests() { // Handle time-limited download URLs (global access, no page restriction) if (isset($_GET['tigerstyle_dl']) && !empty($_GET['tigerstyle_dl'])) { $this->handle_time_limited_download(); return; } // Only handle regular requests for our backup page if (!isset($_GET['page']) || $_GET['page'] !== 'tigerstyle-life9-complete-backup') { return; } // Handle regular download requests if (isset($_GET['download']) && !empty($_GET['download'])) { error_log('TigerStyle Life9: Early download handler called for: ' . $_GET['download']); $this->handle_backup_download(); } // Handle AJAX requests for generating time-limited URLs if (isset($_POST['action']) && $_POST['action'] === 'generate_download_link') { $this->handle_generate_download_link(); } } /** * Add admin menu with cat-themed items */ public function add_admin_menu() { // Main menu page add_menu_page( '🐾 Life Tracker Dashboard', // Page title 'TigerStyle Life9', // Menu title 'manage_options', // Capability 'tigerstyle-life9-complete', // Menu slug [$this, 'render_dashboard_page'], // Callback 'dashicons-backup', // Icon 32 // Position ); // Submenu pages with cat themes add_submenu_page( 'tigerstyle-life9-complete', 'πŸ’Ύ Save a Life', 'πŸ’Ύ Save a Life', 'manage_options', 'tigerstyle-life9-complete-backup', [$this, 'render_backup_page'] ); add_submenu_page( 'tigerstyle-life9-complete', 'πŸ”„ Restore a Life', 'πŸ”„ Restore a Life', 'manage_options', 'tigerstyle-life9-complete-restore', [$this, 'render_restore_page'] ); add_submenu_page( 'tigerstyle-life9-complete', 'βš™οΈ Territory Settings', 'βš™οΈ Territory Settings', 'manage_options', 'tigerstyle-life9-complete-settings', [$this, 'render_settings_page'] ); } /** * Enqueue admin scripts and styles */ public function enqueue_admin_scripts($hook) { // Only load on our plugin pages if (strpos($hook, 'tigerstyle-life9-complete') === false) { return; } // Add inline styles wp_add_inline_style('admin-menu', ' .tigerstyle-life9-complete .form-table th { padding-left: 2em; } .tigerstyle-cat-message { background: #fff3cd; border-left: 4px solid #ffc107; padding: 12px; margin: 16px 0; } .tigerstyle-cat-success { background: #d1edff; border-left: 4px solid #0073aa; padding: 12px; margin: 16px 0; } .tigerstyle-cat-error { background: #ffeaea; border-left: 4px solid #dc3232; padding: 12px; margin: 16px 0; } .tigerstyle-icon { font-size: 1.2em; margin-right: 0.5em; } '); } /** * Render dashboard page */ public function render_dashboard_page() { ?>

🐾

🐱 Welcome to your backup territory!

full_functionality): ?>

😿 Some components couldn't load:

get_existing_backups(); $backup_count = count($existing_backups); if ($backup_count == 0) { echo ''; _e('0 backups created (ready to start!)', 'tigerstyle-life9'); echo ''; } else { echo ''; printf(_n('%d backup created', '%d backups created', $backup_count, 'tigerstyle-life9'), $backup_count); echo ' βœ…'; } ?>
get_security_status(); if ($security_status['status']) { echo 'βœ… ' . esc_html($security_status['message']) . ''; } else { echo '❌ ' . esc_html($security_status['message']) . ''; } ?>
full_functionality): ?> βœ… ⚠️

πŸ’Ύ πŸ”„ βš™οΈ

πŸŽ‰ S3/MinIO Ready:

handle_backup_creation(); } if (isset($_POST['upload_backup'])) { $this->handle_backup_upload(); } // Get existing backups $backups = $this->get_existing_backups(); ?>

πŸ’Ύ

🐱 Ready for action!

πŸ—οΈ

πŸ“€

πŸ“‹

πŸ”„ Restore ⬇️ Download

😸 No backups yet!

πŸ”§

get_infrastructure_status(); $all_good = array_reduce($status_checks, function($carry, $item) { return $carry && $item['status']; }, true); ?>
  • :
handle_backup_restore(); } // Get available backups $backups = $this->get_existing_backups(); $selected_backup = isset($_GET['backup']) ? sanitize_text_field($_GET['backup']) : ''; ?>

πŸ”„

🐱 Nine lives, infinite possibilities!

πŸ”„


😿

😸 No backups found!

πŸ’Ύ

πŸ“‹

🎯 ⬇️

πŸ”§

🐱 How Restore Works:

  1. πŸ” Validation:
  2. πŸ’Ύ Safety Backup:
  3. πŸ“ File Extraction:
  4. πŸ—„οΈ Database Restore:
  5. πŸ“‹ File Replacement:
  6. πŸ”„ Cleanup:
  7. βœ… Verification:
handle_schedule_settings_save(); } if (isset($_POST['save_storage_settings'])) { $this->handle_storage_settings_save(); } // Get current settings $schedule_settings = get_option('tigerstyle_life9_schedule_settings', $this->get_default_schedule_settings()); $storage_settings = get_option('tigerstyle_life9_storage_settings', $this->get_default_storage_settings()); $next_backup = $this->get_next_scheduled_backup(); ?>

βš™οΈ

🐱 Configure your automated backup territory!

πŸ“Š

βœ… πŸ“…
⏰ ⏸️
βœ… πŸͺ£ πŸ’Ύ
βœ… ❌

πŸ•

> > > >


πŸ• Next Scheduled Backup:

☁️

> > > > >

isset($_POST['schedule_enabled']), 'frequency' => sanitize_text_field($_POST['schedule_frequency'] ?? 'daily'), 'hour' => intval($_POST['schedule_hour'] ?? 2), 'day_of_week' => intval($_POST['schedule_day_of_week'] ?? 0), 'day_of_month' => intval($_POST['schedule_day_of_month'] ?? 1), 'include_files' => isset($_POST['schedule_include_files']), 'include_database' => isset($_POST['schedule_include_database']), 'retention_count' => intval($_POST['retention_count'] ?? 10) ]; // Validate settings if (!in_array($settings['frequency'], ['daily', 'weekly', 'monthly'])) { throw new Exception(__('Invalid backup frequency selected.', 'tigerstyle-life9')); } if ($settings['hour'] < 0 || $settings['hour'] > 23) { throw new Exception(__('Invalid backup hour selected.', 'tigerstyle-life9')); } if (!$settings['include_files'] && !$settings['include_database']) { throw new Exception(__('Please select at least files or database for automated backups.', 'tigerstyle-life9')); } // Save settings update_option('tigerstyle_life9_schedule_settings', $settings); // Update wp-cron schedule $this->update_backup_schedule($settings); add_action('admin_notices', function() { echo '
'; echo '

πŸŽ‰ ' . __('Schedule settings saved successfully!', 'tigerstyle-life9') . '

'; echo '
'; }); } catch (Exception $e) { add_action('admin_notices', function() use ($e) { echo '
'; echo '

😿 ' . sprintf(__('Settings save failed: %s', 'tigerstyle-life9'), $e->getMessage()) . '

'; echo '
'; }); } } /** * Handle storage settings save */ private function handle_storage_settings_save() { // Verify nonce for security if (!isset($_POST['storage_nonce']) || !wp_verify_nonce($_POST['storage_nonce'], 'tigerstyle_storage_settings')) { wp_die(__('Security check failed. Please try again.', 'tigerstyle-life9')); } // Check user permissions if (!current_user_can('manage_options')) { wp_die(__('You do not have permission to manage storage settings.', 'tigerstyle-life9')); } try { $settings = [ 's3_enabled' => isset($_POST['s3_enabled']), 's3_endpoint' => sanitize_url($_POST['s3_endpoint'] ?? ''), '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'] ?? 'us-east-1') ]; // Validate S3 settings if enabled if ($settings['s3_enabled']) { if (empty($settings['s3_bucket'])) { throw new Exception(__('S3 bucket name is required when cloud storage is enabled.', 'tigerstyle-life9')); } if (empty($settings['s3_access_key']) || empty($settings['s3_secret_key'])) { throw new Exception(__('S3 access credentials are required when cloud storage is enabled.', 'tigerstyle-life9')); } } // Save settings update_option('tigerstyle_life9_storage_settings', $settings); add_action('admin_notices', function() { echo '
'; echo '

πŸŽ‰ ' . __('Storage settings saved successfully!', 'tigerstyle-life9') . '

'; echo '
'; }); } catch (Exception $e) { add_action('admin_notices', function() use ($e) { echo '
'; echo '

😿 ' . sprintf(__('Storage settings save failed: %s', 'tigerstyle-life9'), $e->getMessage()) . '

'; echo '
'; }); } } /** * Get default schedule settings */ private function get_default_schedule_settings() { return [ 'enabled' => false, 'frequency' => 'daily', 'hour' => 2, 'day_of_week' => 0, // Sunday 'day_of_month' => 1, 'include_files' => true, 'include_database' => true, 'retention_count' => 10 ]; } /** * Get default storage settings */ private function get_default_storage_settings() { return [ 's3_enabled' => false, 's3_endpoint' => '', 's3_bucket' => '', 's3_access_key' => '', 's3_secret_key' => '', 's3_region' => 'us-east-1' ]; } /** * Update backup schedule in wp-cron */ private function update_backup_schedule($settings) { $hook = 'tigerstyle_life9_scheduled_backup'; // Clear existing schedule wp_clear_scheduled_hook($hook); if (!$settings['enabled']) { return; // Just clear the schedule if disabled } // Calculate next run time $next_run = $this->calculate_next_backup_time($settings); // Register custom intervals if needed add_filter('cron_schedules', [$this, 'add_custom_cron_intervals']); // Schedule the backup wp_schedule_event($next_run, $settings['frequency'], $hook, [$settings]); // Log scheduling error_log("TigerStyle Life9: Scheduled backup for " . date('Y-m-d H:i:s', $next_run) . " ({$settings['frequency']})"); } /** * Add custom cron intervals */ public function add_custom_cron_intervals($schedules) { // WordPress already has 'daily', but we might want custom intervals if (!isset($schedules['weekly'])) { $schedules['weekly'] = [ 'interval' => 7 * 24 * 60 * 60, // 1 week in seconds 'display' => __('Weekly', 'tigerstyle-life9') ]; } if (!isset($schedules['monthly'])) { $schedules['monthly'] = [ 'interval' => 30 * 24 * 60 * 60, // 30 days in seconds 'display' => __('Monthly', 'tigerstyle-life9') ]; } return $schedules; } /** * Calculate next backup time based on settings */ private function calculate_next_backup_time($settings) { $now = current_time('timestamp'); $hour = $settings['hour']; switch ($settings['frequency']) { case 'daily': // Schedule for today at the specified hour, or tomorrow if past that time $today_time = strtotime("today {$hour}:00", $now); return ($today_time > $now) ? $today_time : strtotime("tomorrow {$hour}:00", $now); case 'weekly': $day_of_week = $settings['day_of_week']; $target_time = strtotime("next " . $this->get_day_name($day_of_week) . " {$hour}:00", $now); // If it's the same day but past the time, schedule for next week if (date('w', $now) == $day_of_week) { $today_time = strtotime("today {$hour}:00", $now); if ($today_time > $now) { return $today_time; } } return $target_time; case 'monthly': $day_of_month = $settings['day_of_month']; $current_month = date('Y-m', $now); $target_time = strtotime("{$current_month}-{$day_of_month} {$hour}:00"); // If past this month's date, schedule for next month if ($target_time <= $now) { $next_month = date('Y-m', strtotime('+1 month', $now)); $target_time = strtotime("{$next_month}-{$day_of_month} {$hour}:00"); } return $target_time; default: return strtotime('+1 hour', $now); } } /** * Get day name from day number */ private function get_day_name($day_number) { $days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; return $days[$day_number] ?? 'Sunday'; } /** * Get next scheduled backup time */ private function get_next_scheduled_backup() { $hook = 'tigerstyle_life9_scheduled_backup'; $next_scheduled = wp_next_scheduled($hook); if ($next_scheduled) { return date('F j, Y \a\t g:i A', $next_scheduled); } return false; } /** * Execute scheduled backup */ public function execute_scheduled_backup($settings) { try { error_log("TigerStyle Life9: Executing scheduled backup"); // Create backup configuration $backup_config = [ 'backup_name' => 'Scheduled Backup ' . date('Y-m-d H:i'), 'backup_type' => 'scheduled', 'include_files' => $settings['include_files'], 'include_database' => $settings['include_database'], 'storage_type' => 'local' // TODO: Add S3 support for scheduled backups ]; // Create backup using existing engine $result = $this->handle_backup_creation_programmatic($backup_config); if ($result) { error_log("TigerStyle Life9: Scheduled backup completed successfully"); // Clean up old backups based on retention policy $this->cleanup_old_backups($settings['retention_count']); } else { error_log("TigerStyle Life9: Scheduled backup failed"); } } catch (Exception $e) { error_log("TigerStyle Life9: Scheduled backup error - " . $e->getMessage()); } } /** * Cleanup old backups based on retention policy */ private function cleanup_old_backups($retention_count) { if ($retention_count <= 0) { return; // Unlimited retention } try { $backups = get_option('tigerstyle_life9_backups', []); // Filter for scheduled backups only $scheduled_backups = array_filter($backups, function($backup) { return isset($backup['backup_type']) && $backup['backup_type'] === 'scheduled'; }); // Sort by creation date (newest first) usort($scheduled_backups, function($a, $b) { return strtotime($b['created']) - strtotime($a['created']); }); // Remove excess backups if (count($scheduled_backups) > $retention_count) { $backups_to_remove = array_slice($scheduled_backups, $retention_count); foreach ($backups_to_remove as $backup) { // Delete backup file if (isset($backup['storage_path']) && file_exists($backup['storage_path'])) { unlink($backup['storage_path']); } // Remove from metadata $backups = array_filter($backups, function($b) use ($backup) { return $b['filename'] !== $backup['filename']; }); } // Update metadata update_option('tigerstyle_life9_backups', array_values($backups)); error_log("TigerStyle Life9: Cleaned up " . count($backups_to_remove) . " old backups"); } } catch (Exception $e) { error_log("TigerStyle Life9: Backup cleanup error - " . $e->getMessage()); } } /** * Handle backup creation programmatically (for scheduled backups) */ private function handle_backup_creation_programmatic($config) { try { $backup_name = $config['backup_name']; $include_files = $config['include_files']; $include_database = $config['include_database']; $storage_type = $config['storage_type'] ?? 'local'; // Validate inputs if (empty($backup_name)) { throw new Exception(__('Backup name is required.', 'tigerstyle-life9')); } if (!$include_files && !$include_database) { throw new Exception(__('Please select at least files or database to backup.', 'tigerstyle-life9')); } // Generate backup filename with timestamp $timestamp = date('Y-m-d_H-i-s'); $backup_filename = sanitize_file_name($backup_name . '_' . $timestamp . '.zip'); // Create backup directory if it doesn't exist $backup_dir = WP_CONTENT_DIR . '/backups'; if (!file_exists($backup_dir)) { wp_mkdir_p($backup_dir); } $backup_path = $backup_dir . '/' . $backup_filename; // Initialize backup log $backup_log = [ 'name' => $backup_name, 'filename' => $backup_filename, 'created' => current_time('mysql'), 'size' => 0, 'includes' => [], 'storage' => $storage_type, 'status' => 'creating', 'backup_type' => $config['backup_type'] ?? 'manual' ]; // Create ZIP archive using WordPress's PclZip library (more reliable) require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php'); $zip = new PclZip($backup_path); $files_to_add = []; // Include files if ($include_files) { $files_to_add = array_merge($files_to_add, $this->get_files_for_backup()); $backup_log['includes'][] = 'files'; } // Include database if ($include_database) { $db_file = $this->create_database_backup(); if ($db_file) { $files_to_add[] = $db_file; $backup_log['includes'][] = 'database'; } } // Create the archive if (empty($files_to_add)) { throw new Exception(__('No files to backup.', 'tigerstyle-life9')); } $result = $zip->create($files_to_add, PCLZIP_OPT_REMOVE_PATH, ABSPATH); if ($result == 0) { throw new Exception(__('Failed to create backup archive: ', 'tigerstyle-life9') . $zip->errorInfo(true)); } // Get final backup size $backup_log['size'] = filesize($backup_path); $backup_log['status'] = 'completed'; $backup_log['storage_path'] = $backup_path; // Save backup metadata $this->save_backup_metadata($backup_log); return true; } catch (Exception $e) { error_log("TigerStyle Life9: Programmatic backup failed - " . $e->getMessage()); return false; } } /** * Handle backup creation */ private function handle_backup_creation() { // Verify nonce for security if (!isset($_POST['backup_nonce']) || !wp_verify_nonce($_POST['backup_nonce'], 'tigerstyle_backup_create')) { wp_die(__('Security check failed. Please try again.', 'tigerstyle-life9')); } // Check user permissions if (!current_user_can('manage_options')) { wp_die(__('You do not have permission to create backups.', 'tigerstyle-life9')); } try { $backup_name = sanitize_text_field($_POST['backup_name']); $include_files = isset($_POST['include_files']); $include_database = isset($_POST['include_database']); $storage_type = sanitize_text_field($_POST['storage_type']); // Validate inputs if (empty($backup_name)) { throw new Exception(__('Backup name is required.', 'tigerstyle-life9')); } if (!$include_files && !$include_database) { throw new Exception(__('Please select at least files or database to backup.', 'tigerstyle-life9')); } // Generate backup filename with timestamp $timestamp = date('Y-m-d_H-i-s'); $backup_filename = sanitize_file_name($backup_name . '_' . $timestamp . '.zip'); // Create backup directory if it doesn't exist $backup_dir = WP_CONTENT_DIR . '/backups'; if (!file_exists($backup_dir)) { wp_mkdir_p($backup_dir); } $backup_path = $backup_dir . '/' . $backup_filename; // Initialize backup log $backup_log = [ 'name' => $backup_name, 'filename' => $backup_filename, 'created' => current_time('mysql'), 'size' => 0, 'includes' => [], 'storage' => $storage_type, 'status' => 'creating' ]; // Create ZIP archive using WordPress's PclZip library (more reliable) require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php'); $zip = new PclZip($backup_path); $files_to_add = []; // Include files if ($include_files) { $files_to_add = array_merge($files_to_add, $this->get_files_for_backup()); $backup_log['includes'][] = 'files'; } // Include database if ($include_database) { $db_file = $this->create_database_backup(); if ($db_file) { $files_to_add[] = $db_file; $backup_log['includes'][] = 'database'; } } // Create the archive if (empty($files_to_add)) { throw new Exception(__('No files to backup.', 'tigerstyle-life9')); } $result = $zip->create($files_to_add, PCLZIP_OPT_REMOVE_PATH, ABSPATH); if ($result == 0) { throw new Exception(__('Failed to create backup archive: ', 'tigerstyle-life9') . $zip->errorInfo(true)); } // Get final backup size $backup_log['size'] = filesize($backup_path); $backup_log['status'] = 'completed'; // Handle S3/MinIO storage if ($storage_type === 's3') { $this->upload_to_s3($backup_path, $backup_filename); $backup_log['storage_path'] = 's3://' . $backup_filename; } else { $backup_log['storage_path'] = $backup_path; } // Save backup metadata $this->save_backup_metadata($backup_log); add_action('admin_notices', function() use ($backup_filename) { echo '
'; echo '

πŸŽ‰ ' . sprintf(__('Backup "%s" created successfully!', 'tigerstyle-life9'), $backup_filename) . '

'; echo '
'; }); } catch (Exception $e) { add_action('admin_notices', function() use ($e) { echo '
'; echo '

😿 ' . sprintf(__('Backup failed: %s', 'tigerstyle-life9'), $e->getMessage()) . '

'; echo '
'; }); } } /** * Handle backup file upload */ private function handle_backup_upload() { // Verify nonce for security if (!isset($_POST['upload_nonce']) || !wp_verify_nonce($_POST['upload_nonce'], 'tigerstyle_backup_upload')) { wp_die(__('Security check failed. Please try again.', 'tigerstyle-life9')); } // Check user permissions if (!current_user_can('manage_options')) { wp_die(__('You do not have permission to upload backups.', 'tigerstyle-life9')); } try { // Check if file was uploaded if (!isset($_FILES['backup_file']) || $_FILES['backup_file']['error'] !== UPLOAD_ERR_OK) { throw new Exception(__('No file uploaded or upload error occurred.', 'tigerstyle-life9')); } $file = $_FILES['backup_file']; // Validate file size (512MB max) $max_size = 512 * 1024 * 1024; // 512MB in bytes if ($file['size'] > $max_size) { throw new Exception(__('File is too large. Maximum size is 512MB.', 'tigerstyle-life9')); } // Validate file type $allowed_types = ['zip', 'tar', 'gz', 'xml', 'sql']; $file_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($file_ext, $allowed_types)) { throw new Exception(__('Invalid file type. Allowed: .zip, .tar, .gz, .xml, .sql', 'tigerstyle-life9')); } // Create backup directory if it doesn't exist $backup_dir = WP_CONTENT_DIR . '/backups'; if (!file_exists($backup_dir)) { wp_mkdir_p($backup_dir); } // Generate safe filename $filename = sanitize_file_name($file['name']); $timestamp = date('Y-m-d_H-i-s'); $safe_filename = $timestamp . '_uploaded_' . $filename; $destination = $backup_dir . '/' . $safe_filename; // Move uploaded file if (!move_uploaded_file($file['tmp_name'], $destination)) { throw new Exception(__('Failed to save uploaded file.', 'tigerstyle-life9')); } // Validate backup file $validation_result = $this->validate_backup_file($destination, $file_ext); // Save backup metadata $backup_log = [ 'name' => pathinfo($filename, PATHINFO_FILENAME), 'filename' => $safe_filename, 'created' => current_time('mysql'), 'size' => filesize($destination), 'includes' => $validation_result['includes'], 'storage' => 'local', 'storage_path' => $destination, 'status' => 'uploaded', 'original_name' => $filename ]; $this->save_backup_metadata($backup_log); add_action('admin_notices', function() use ($filename) { echo '
'; echo '

πŸ“₯ ' . sprintf(__('Backup file "%s" uploaded successfully!', 'tigerstyle-life9'), $filename) . '

'; echo '
'; }); } catch (Exception $e) { add_action('admin_notices', function() use ($e) { echo '
'; echo '

😿 ' . sprintf(__('Upload failed: %s', 'tigerstyle-life9'), $e->getMessage()) . '

'; echo '
'; }); } } /** * Handle backup download */ private function handle_backup_download() { // Verify nonce for security if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'download_backup')) { wp_die(__('Security check failed. Please try again.', 'tigerstyle-life9')); } // Check user permissions if (!current_user_can('manage_options')) { wp_die(__('You do not have permission to download backups.', 'tigerstyle-life9')); } try { $backup_filename = sanitize_file_name($_GET['download']); // Get backup metadata to determine storage type $backups = get_option('tigerstyle_life9_backups', []); $backup_metadata = null; foreach ($backups as $backup) { if ($backup['filename'] === $backup_filename) { $backup_metadata = $backup; break; } } if (!$backup_metadata) { wp_die(__('Backup file not found in metadata.', 'tigerstyle-life9')); } // Handle download based on storage type if ($backup_metadata['storage'] === 's3') { $this->download_from_s3($backup_filename, $backup_metadata); } else { $this->download_from_local($backup_filename, $backup_metadata); } } catch (Exception $e) { wp_die(__('Download failed: ', 'tigerstyle-life9') . $e->getMessage()); } } /** * Download backup from local storage */ private function download_from_local($filename, $metadata) { $backup_dir = WP_CONTENT_DIR . '/backups'; $file_path = $backup_dir . '/' . $filename; if (!file_exists($file_path)) { throw new Exception(__('Backup file not found on local storage.', 'tigerstyle-life9')); } // Serve the file $this->serve_file_download($file_path, $filename); } /** * Download backup from S3/MinIO storage */ private function download_from_s3($filename, $metadata) { // Get S3/MinIO configuration $s3_settings = get_option('tigerstyle_life9_storage_settings', []); if (empty($s3_settings['s3_enabled']) || empty($s3_settings['s3_endpoint']) || empty($s3_settings['s3_bucket']) || empty($s3_settings['s3_access_key']) || empty($s3_settings['s3_secret_key'])) { throw new Exception(__('S3/MinIO configuration is incomplete.', 'tigerstyle-life9')); } try { // Initialize S3 client require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem.php'); // Create temporary file for download $temp_dir = get_temp_dir(); $temp_file = $temp_dir . '/' . $filename; // Download from MinIO using curl $url = rtrim($s3_settings['s3_endpoint'], '/') . '/' . $s3_settings['s3_bucket'] . '/' . $filename; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERPWD, $s3_settings['s3_access_key'] . ':' . $s3_settings['s3_secret_key']); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); $file_content = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code !== 200 || $file_content === false) { throw new Exception(__('Failed to download from S3/MinIO storage. HTTP Code: ', 'tigerstyle-life9') . $http_code); } // Write to temporary file if (file_put_contents($temp_file, $file_content) === false) { throw new Exception(__('Failed to write temporary file.', 'tigerstyle-life9')); } // Serve the file and clean up $this->serve_file_download($temp_file, $filename, true); } catch (Exception $e) { // Clean up temp file if it exists if (isset($temp_file) && file_exists($temp_file)) { unlink($temp_file); } throw $e; } } /** * Serve file for download */ private function serve_file_download($file_path, $filename, $delete_after = false) { if (!file_exists($file_path)) { throw new Exception(__('File not found.', 'tigerstyle-life9')); } // Set headers for file download header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Length: ' . filesize($file_path)); header('Cache-Control: no-cache, must-revalidate'); header('Expires: 0'); // Output file content readfile($file_path); // Clean up temporary file if requested if ($delete_after) { unlink($file_path); } exit; // Important: stop execution after file download } /** * Handle backup restoration */ private function handle_backup_restore() { error_log("TigerStyle Life9: handle_backup_restore() called"); error_log("TigerStyle Life9: POST data: " . print_r($_POST, true)); // Verify nonce for security if (!isset($_POST['restore_nonce']) || !wp_verify_nonce($_POST['restore_nonce'], 'tigerstyle_restore_backup')) { error_log("TigerStyle Life9: Nonce verification failed"); wp_die(__('Security check failed. Please try again.', 'tigerstyle-life9')); } // Check user permissions if (!current_user_can('manage_options')) { wp_die(__('You do not have permission to restore backups.', 'tigerstyle-life9')); } try { $backup_id = sanitize_text_field($_POST['backup_id']); $restore_files = isset($_POST['restore_files']); $restore_database = isset($_POST['restore_database']); $create_backup_before = isset($_POST['create_backup_before_restore']); $validate_backup = isset($_POST['validate_backup']); // Validate inputs if (empty($backup_id)) { throw new Exception(__('Please select a backup file to restore.', 'tigerstyle-life9')); } if (!$restore_files && !$restore_database) { throw new Exception(__('Please select at least files or database to restore.', 'tigerstyle-life9')); } // Find backup file $backup_path = $this->get_backup_file_path($backup_id); if (!$backup_path || !file_exists($backup_path)) { throw new Exception(__('Backup file not found.', 'tigerstyle-life9')); } // Validate backup if requested if ($validate_backup) { $file_ext = strtolower(pathinfo($backup_path, PATHINFO_EXTENSION)); $validation = $this->validate_backup_file($backup_path, $file_ext); if (!$validation['valid']) { throw new Exception(__('Backup file validation failed: ' . $validation['error'], 'tigerstyle-life9')); } } // Create backup before restore if requested if ($create_backup_before) { $this->create_pre_restore_backup(); } // Begin restoration process $this->perform_restoration($backup_path, $restore_files, $restore_database); add_action('admin_notices', function() { echo '
'; echo '

πŸŽ‰ ' . __('Backup restored successfully! Your site has been brought back to life!', 'tigerstyle-life9') . '

'; echo '
'; }); } catch (Exception $e) { add_action('admin_notices', function() use ($e) { echo '
'; echo '

😿 ' . sprintf(__('Restore failed: %s', 'tigerstyle-life9'), $e->getMessage()) . '

'; echo '
'; }); } } /** * Get existing backups */ private function get_existing_backups() { $backups = []; // Get backup metadata $backup_metadata = get_option('tigerstyle_life9_backups', []); foreach ($backup_metadata as $index => $backup) { // Check if backup file still exists $file_exists = false; if (isset($backup['storage_path'])) { if (strpos($backup['storage_path'], 's3://') === 0) { $file_exists = $this->check_s3_file_exists($backup['filename']); } else { $file_exists = file_exists($backup['storage_path']); } } // Format backup data for interface $formatted_backup = [ 'id' => $backup['filename'], // Use filename as ID 'name' => $backup['name'], 'filename' => $backup['filename'], 'date' => date('M j, Y g:i A', strtotime($backup['created'])), 'size' => $this->format_file_size($backup['size']), 'location' => ($backup['storage'] === 's3') ? '☁️ S3/MinIO' : '🏠 Local', 'file_exists' => $file_exists, 'storage' => $backup['storage'], 'storage_path' => $backup['storage_path'], 'includes' => $backup['includes'], 'status' => $backup['status'], 'created' => $backup['created'], 'size_bytes' => $backup['size'] ]; // Only include if file exists if ($file_exists) { $backups[] = $formatted_backup; } } // Fallback: If no metadata backups found, scan backup directory directly if (empty($backups)) { error_log('TigerStyle Life9: No metadata backups found, scanning backup directory directly'); $backup_dir = WP_CONTENT_DIR . '/backups'; if (is_dir($backup_dir)) { $files = glob($backup_dir . '/*.zip'); foreach ($files as $file) { if (is_file($file)) { $filename = basename($file); $file_size = filesize($file); $file_time = filemtime($file); // Extract backup name from filename (remove timestamp suffix) $name_parts = explode('_', pathinfo($filename, PATHINFO_FILENAME)); array_pop($name_parts); // Remove timestamp $backup_name = implode('_', $name_parts); $fallback_backup = [ 'id' => $filename, 'name' => $backup_name, 'filename' => $filename, 'date' => date('M j, Y g:i A', $file_time), 'size' => $this->format_file_size($file_size), 'location' => '🏠 Local (Found)', 'file_exists' => true, 'storage' => 'local', 'storage_path' => $file, 'includes' => ['files', 'database'], // Assume full backup 'status' => 'completed', 'created' => date('Y-m-d H:i:s', $file_time), 'size_bytes' => $file_size ]; $backups[] = $fallback_backup; error_log('TigerStyle Life9: Found fallback backup: ' . $filename); } } } } // Sort by creation date (newest first) usort($backups, function($a, $b) { return strtotime($b['created']) - strtotime($a['created']); }); return $backups; } /** * Get infrastructure status checks */ private function get_infrastructure_status() { $status_checks = []; // 1. Backup Engine Status $backup_dir = WP_CONTENT_DIR . '/backups'; $backup_engine_status = is_dir($backup_dir) && is_writable($backup_dir); $status_checks[] = [ 'name' => 'Backup Engine', 'status' => $backup_engine_status, 'message' => $backup_engine_status ? 'Ready for operations' : 'Backup directory not accessible' ]; // 2. S3/MinIO Cloud Storage Status $s3_settings = get_option('tigerstyle_life9_s3_settings', []); $s3_configured = !empty($s3_settings['access_key']) && !empty($s3_settings['secret_key']); $s3_status = false; $s3_message = 'Not configured'; if ($s3_configured) { try { // Test S3 connectivity $test_result = $this->test_s3_connection(); $s3_status = $test_result['success']; $s3_message = $test_result['success'] ? 'Connected and ready' : $test_result['error']; } catch (Exception $e) { $s3_message = 'Connection test failed: ' . $e->getMessage(); } } $status_checks[] = [ 'name' => 'S3/MinIO Cloud Storage', 'status' => $s3_status, 'message' => $s3_message ]; // 3. File Upload Limits $upload_max = wp_max_upload_size(); $php_max = ini_get('upload_max_filesize'); $post_max = ini_get('post_max_size'); $memory_limit = ini_get('memory_limit'); $upload_healthy = $upload_max >= (100 * 1024 * 1024); // 100MB threshold $status_checks[] = [ 'name' => 'File Upload Limits', 'status' => $upload_healthy, 'message' => sprintf('Max: %s (PHP: %s, POST: %s, Memory: %s)', size_format($upload_max), $php_max, $post_max, $memory_limit) ]; // 4. Database Connection global $wpdb; $db_status = false; $db_message = 'Connection failed'; try { $result = $wpdb->get_var("SELECT 1"); $db_status = ($result == 1); $db_message = $db_status ? 'Connected and responsive' : 'Query test failed'; } catch (Exception $e) { $db_message = 'Error: ' . $e->getMessage(); } $status_checks[] = [ 'name' => 'Database Connection', 'status' => $db_status, 'message' => $db_message ]; // 5. Disk Space $backup_dir_space = disk_free_space($backup_dir); $wp_content_space = disk_free_space(WP_CONTENT_DIR); $min_space_threshold = 1024 * 1024 * 1024; // 1GB $space_adequate = $backup_dir_space > $min_space_threshold; $status_checks[] = [ 'name' => 'Disk Space', 'status' => $space_adequate, 'message' => sprintf('Available: %s (Backup dir: %s)', size_format($wp_content_space), size_format($backup_dir_space)) ]; // 6. Security & Permissions $security_status = true; $security_issues = []; // Check if backup directory is web-accessible (security risk) $backup_url = content_url('backups/'); $response = wp_remote_head($backup_url, ['timeout' => 5]); if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) !== 403) { $security_status = false; $security_issues[] = 'Backup directory web-accessible'; } // Check file permissions if (!is_writable($backup_dir)) { $security_status = false; $security_issues[] = 'Backup directory not writable'; } $status_checks[] = [ 'name' => 'Security & Permissions', 'status' => $security_status, 'message' => $security_status ? 'All security checks passed' : implode(', ', $security_issues) ]; return $status_checks; } /** * Get security status for dashboard display */ private function get_security_status() { $security_issues = []; // Check backup directory permissions $backup_dir = WP_CONTENT_DIR . '/backups'; if (!is_dir($backup_dir) || !is_writable($backup_dir)) { $security_issues[] = 'Backup directory not writable'; } // Check if backup directory is web-accessible (security risk) $backup_url = content_url('backups/'); $response = wp_remote_head($backup_url, ['timeout' => 5]); if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) !== 403) { $security_issues[] = 'Backup directory web-accessible'; } // Check file permissions on key files $plugin_file = __FILE__; if (is_writable($plugin_file)) { $security_issues[] = 'Plugin file is writable'; } // Check WordPress security constants if (!defined('DISALLOW_FILE_EDIT') || !DISALLOW_FILE_EDIT) { $security_issues[] = 'File editing not disabled'; } // Check SSL if (!is_ssl() && !defined('WP_DEBUG') || !WP_DEBUG) { $security_issues[] = 'SSL not enabled'; } $is_secure = empty($security_issues); return [ 'status' => $is_secure, 'message' => $is_secure ? 'All security checks passed' : implode(', ', $security_issues), 'issues' => $security_issues ]; } /** * Test S3 connection */ private function test_s3_connection() { $s3_settings = get_option('tigerstyle_life9_s3_settings', []); if (empty($s3_settings['access_key']) || empty($s3_settings['secret_key'])) { return ['success' => false, 'error' => 'S3 credentials not configured']; } try { // Create a test object to verify connectivity $test_key = 'tigerstyle-connection-test-' . time() . '.txt'; $test_content = 'TigerStyle Life9 connection test at ' . date('Y-m-d H:i:s'); $result = $this->upload_to_s3($test_content, $test_key); if ($result['success']) { // Clean up test file $this->delete_from_s3($test_key); return ['success' => true, 'error' => null]; } else { return ['success' => false, 'error' => $result['error']]; } } catch (Exception $e) { return ['success' => false, 'error' => 'Connection failed: ' . $e->getMessage()]; } } /** * Add files to backup */ private function add_files_to_backup($zip) { $wp_root = ABSPATH; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($wp_root)); foreach ($iterator as $file) { if ($file->isFile()) { $filePath = $file->getRealPath(); $relativePath = substr($filePath, strlen($wp_root)); // Skip certain directories and files if ($this->should_skip_file($relativePath)) { continue; } $zip->addFile($filePath, $relativePath); } } } /** * Add database to backup */ private function add_database_to_backup($zip) { global $wpdb; $sql_content = "-- TigerStyle Life9 Database Backup\n"; $sql_content .= "-- Created: " . current_time('mysql') . "\n\n"; // Get all tables $tables = $wpdb->get_col("SHOW TABLES"); foreach ($tables as $table) { // Get table structure $create_table = $wpdb->get_row("SHOW CREATE TABLE `$table`", ARRAY_A); $sql_content .= "DROP TABLE IF EXISTS `$table`;\n"; $sql_content .= $create_table['Create Table'] . ";\n\n"; // Get table data $rows = $wpdb->get_results("SELECT * FROM `$table`", ARRAY_A); if ($rows) { foreach ($rows as $row) { $values = array_map(function($value) use ($wpdb) { return is_null($value) ? 'NULL' : "'" . $wpdb->_real_escape($value) . "'"; }, array_values($row)); $sql_content .= "INSERT INTO `$table` VALUES (" . implode(', ', $values) . ");\n"; } } $sql_content .= "\n"; } // Add SQL file to ZIP $zip->addFromString('database.sql', $sql_content); } /** * Get files for backup (PclZip compatible) */ private function get_files_for_backup() { $files = []; $wp_root = ABSPATH; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($wp_root)); foreach ($iterator as $file) { if ($file->isFile()) { $filePath = $file->getRealPath(); $relativePath = substr($filePath, strlen($wp_root)); // Skip certain directories and files if ($this->should_skip_file($relativePath)) { continue; } $files[] = $filePath; } } return $files; } /** * Create database backup file (PclZip compatible) */ private function create_database_backup() { global $wpdb; $sql_content = "-- TigerStyle Life9 Database Backup\n"; $sql_content .= "-- Created: " . current_time('mysql') . "\n\n"; // Get all tables $tables = $wpdb->get_col("SHOW TABLES"); foreach ($tables as $table) { // Get table structure $create_table = $wpdb->get_row("SHOW CREATE TABLE `$table`", ARRAY_A); $sql_content .= "DROP TABLE IF EXISTS `$table`;\n"; $sql_content .= $create_table['Create Table'] . ";\n\n"; // Get table data $rows = $wpdb->get_results("SELECT * FROM `$table`", ARRAY_A); if ($rows) { foreach ($rows as $row) { $values = array_map(function($value) use ($wpdb) { return is_null($value) ? 'NULL' : "'" . $wpdb->_real_escape($value) . "'"; }, array_values($row)); $sql_content .= "INSERT INTO `$table` VALUES (" . implode(', ', $values) . ");\n"; } } $sql_content .= "\n"; } // Write to temporary file with safer filename $backup_dir = WP_CONTENT_DIR . '/backups'; $safe_timestamp = date('Y-m-d_H-i-s'); $db_file = $backup_dir . '/database_' . $safe_timestamp . '.sql'; // Ensure directory exists and is writable if (!file_exists($backup_dir)) { wp_mkdir_p($backup_dir); } // Try to write the file $result = file_put_contents($db_file, $sql_content); if ($result === false) { error_log('TigerStyle Life9: Failed to write database file to ' . $db_file); return false; } return $db_file; } /** * Upload backup to S3/MinIO */ private function upload_to_s3($file_path, $filename) { // This would integrate with AWS SDK or MinIO client // For now, we'll simulate the upload // In production, you'd implement actual S3/MinIO upload logic here return true; } /** * Save backup metadata */ private function save_backup_metadata($backup_data) { $backups = get_option('tigerstyle_life9_backups', []); $backups[] = $backup_data; update_option('tigerstyle_life9_backups', $backups); } /** * Validate backup file */ private function validate_backup_file($file_path, $file_ext) { $result = ['valid' => false, 'includes' => [], 'error' => '']; try { switch ($file_ext) { case 'zip': $zip = new ZipArchive(); if ($zip->open($file_path) === TRUE) { // Check for common WordPress files/folders for ($i = 0; $i < $zip->numFiles; $i++) { $filename = $zip->getNameIndex($i); if (strpos($filename, 'wp-config.php') !== false) { $result['includes'][] = 'files'; } if (strpos($filename, 'database.sql') !== false || strpos($filename, '.sql') !== false) { $result['includes'][] = 'database'; } } $zip->close(); $result['valid'] = true; } break; case 'sql': $result['includes'][] = 'database'; $result['valid'] = true; break; case 'xml': // WordPress export file $result['includes'][] = 'content'; $result['valid'] = true; break; default: $result['valid'] = true; $result['includes'][] = 'unknown'; } } catch (Exception $e) { $result['error'] = $e->getMessage(); } return $result; } /** * Check if file should be skipped during backup */ private function should_skip_file($relative_path) { $skip_patterns = [ 'wp-content/cache/', 'wp-content/backups/', 'wp-content/upgrade/', '.git/', '.svn/', 'node_modules/', '.DS_Store', 'Thumbs.db' ]; foreach ($skip_patterns as $pattern) { if (strpos($relative_path, $pattern) !== false) { return true; } } return false; } /** * Format file size for display */ private function format_file_size($bytes) { if ($bytes >= 1073741824) { return number_format($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes >= 1048576) { return number_format($bytes / 1048576, 2) . ' MB'; } elseif ($bytes >= 1024) { return number_format($bytes / 1024, 2) . ' KB'; } return $bytes . ' bytes'; } /** * Get backup file path by filename */ private function get_backup_file_path($filename) { $backup_metadata = get_option('tigerstyle_life9_backups', []); foreach ($backup_metadata as $backup) { if ($backup['filename'] === $filename) { return $backup['storage_path']; } } return false; } /** * Check if S3 file exists */ private function check_s3_file_exists($filename) { // This would check S3/MinIO for file existence // For now, we'll return true as a placeholder return true; } /** * Create pre-restore backup */ private function create_pre_restore_backup() { // Create a quick backup before restoration $backup_name = 'pre_restore_' . date('Y-m-d_H-i-s'); // Implementation would be similar to handle_backup_creation } /** * Perform restoration */ private function perform_restoration($backup_path, $restore_files, $restore_database) { error_log("TigerStyle Life9: perform_restoration() called"); error_log("TigerStyle Life9: backup_path: $backup_path"); error_log("TigerStyle Life9: restore_files: " . ($restore_files ? 'true' : 'false')); error_log("TigerStyle Life9: restore_database: " . ($restore_database ? 'true' : 'false')); try { // Create temporary directory for extraction $temp_dir = WP_CONTENT_DIR . '/temp_restore_' . time(); if (!wp_mkdir_p($temp_dir)) { throw new Exception(__('Could not create temporary directory for restoration.', 'tigerstyle-life9')); } // Extract backup ZIP file if (!$this->extract_backup_file($backup_path, $temp_dir)) { throw new Exception(__('Failed to extract backup file.', 'tigerstyle-life9')); } // Restore database if requested if ($restore_database) { $this->restore_database_from_backup($temp_dir); } // Restore files if requested if ($restore_files) { $this->restore_files_from_backup($temp_dir); } // Clean up temporary directory $this->cleanup_directory($temp_dir); error_log("TigerStyle Life9: Restoration completed successfully"); } catch (Exception $e) { // Clean up on failure if (isset($temp_dir) && file_exists($temp_dir)) { $this->cleanup_directory($temp_dir); } throw $e; } } /** * Extract backup ZIP file */ private function extract_backup_file($backup_path, $temp_dir) { if (!class_exists('PclZip')) { require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php'); } $zip = new PclZip($backup_path); $result = $zip->extract(PCLZIP_OPT_PATH, $temp_dir); if ($result == 0) { error_log("TigerStyle Life9: ZIP extraction failed: " . $zip->errorInfo(true)); return false; } return true; } /** * Restore database from backup */ private function restore_database_from_backup($temp_dir) { // Look for database SQL file in wp-content/backups directory $backup_dir = $temp_dir . '/wp-content/backups'; $sql_file = null; if (file_exists($backup_dir)) { // Find the database file (it will have a timestamp in the name) $files = glob($backup_dir . '/database_*.sql'); if (!empty($files)) { $sql_file = $files[0]; // Use the first (and likely only) database file } } // Also check for a simple database.sql in root (for legacy backups) if (!$sql_file && file_exists($temp_dir . '/database.sql')) { $sql_file = $temp_dir . '/database.sql'; } if (!$sql_file) { throw new Exception(__('Database backup file not found in backup. Expected wp-content/backups/database_*.sql', 'tigerstyle-life9')); } // Read SQL file $sql_content = file_get_contents($sql_file); if ($sql_content === false) { throw new Exception(__('Could not read database backup file.', 'tigerstyle-life9')); } // Split SQL into individual statements (respecting quoted strings) $sql_statements = $this->parse_sql_statements($sql_content); global $wpdb; error_log("TigerStyle Life9: Starting database restoration with transaction safety"); // Start transaction for atomic restoration $wpdb->query('START TRANSACTION'); try { // Disable foreign key checks for import $wpdb->query('SET FOREIGN_KEY_CHECKS = 0'); // Create a temporary backup of current state for rollback $current_backup_path = $this->create_emergency_backup(); error_log("TigerStyle Life9: Emergency backup created at: $current_backup_path"); $statements_executed = 0; $total_statements = count($sql_statements); foreach ($sql_statements as $statement) { if (!empty($statement)) { $result = $wpdb->query($statement); if ($result === false) { throw new Exception(__('Database restoration failed at statement ' . ($statements_executed + 1) . ' of ' . $total_statements . ': ' . $wpdb->last_error, 'tigerstyle-life9')); } $statements_executed++; // Log progress every 100 statements if ($statements_executed % 100 === 0) { error_log("TigerStyle Life9: Processed $statements_executed/$total_statements SQL statements"); } } } // Re-enable foreign key checks $wpdb->query('SET FOREIGN_KEY_CHECKS = 1'); // Commit the transaction $wpdb->query('COMMIT'); error_log("TigerStyle Life9: Database restored successfully ($statements_executed statements executed)"); } catch (Exception $e) { // Rollback the transaction $wpdb->query('ROLLBACK'); $wpdb->query('SET FOREIGN_KEY_CHECKS = 1'); error_log("TigerStyle Life9: Database restoration failed, transaction rolled back: " . $e->getMessage()); throw $e; } } /** * Parse SQL statements while respecting quoted strings and serialized data */ private function parse_sql_statements($sql_content) { $statements = []; $current_statement = ''; $in_quotes = false; $quote_char = ''; $length = strlen($sql_content); for ($i = 0; $i < $length; $i++) { $char = $sql_content[$i]; $next_char = ($i + 1 < $length) ? $sql_content[$i + 1] : ''; // Handle quote escaping if ($in_quotes && $char === '\\' && ($next_char === $quote_char || $next_char === '\\')) { $current_statement .= $char . $next_char; $i++; // Skip next character continue; } // Handle quote start/end if (!$in_quotes && ($char === '"' || $char === "'")) { $in_quotes = true; $quote_char = $char; $current_statement .= $char; } elseif ($in_quotes && $char === $quote_char) { // Check if it's a doubled quote (escape sequence) if ($next_char === $quote_char) { $current_statement .= $char . $next_char; $i++; // Skip next character } else { $in_quotes = false; $quote_char = ''; $current_statement .= $char; } } elseif ($char === ';' && !$in_quotes) { // End of statement $statement = trim($current_statement); if (!empty($statement)) { $statements[] = $statement; } $current_statement = ''; } else { $current_statement .= $char; } } // Add final statement if exists $statement = trim($current_statement); if (!empty($statement)) { $statements[] = $statement; } return $statements; } /** * Create emergency backup of current database state for rollback purposes */ private function create_emergency_backup() { $emergency_dir = WP_CONTENT_DIR . '/backups/emergency'; if (!file_exists($emergency_dir)) { wp_mkdir_p($emergency_dir); } $timestamp = date('Y-m-d_H-i-s'); $backup_file = $emergency_dir . '/emergency_backup_' . $timestamp . '.sql'; global $wpdb; // Get all tables in the database $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N); $sql_content = "-- Emergency backup created on $timestamp\n"; $sql_content .= "-- This backup was created automatically before database restoration\n\n"; foreach ($tables as $table) { $table_name = $table[0]; // Get table structure $create_table = $wpdb->get_row("SHOW CREATE TABLE `$table_name`", ARRAY_N); $sql_content .= "DROP TABLE IF EXISTS `$table_name`;\n"; $sql_content .= $create_table[1] . ";\n\n"; // Get table data $rows = $wpdb->get_results("SELECT * FROM `$table_name`", ARRAY_A); if (!empty($rows)) { $columns = array_keys($rows[0]); $sql_content .= "INSERT INTO `$table_name` (`" . implode('`, `', $columns) . "`) VALUES\n"; $values = []; foreach ($rows as $row) { $escaped_values = []; foreach ($row as $value) { if ($value === null) { $escaped_values[] = 'NULL'; } else { $escaped_values[] = "'" . $wpdb->_real_escape($value) . "'"; } } $values[] = "(" . implode(', ', $escaped_values) . ")"; } $sql_content .= implode(",\n", $values) . ";\n\n"; } } $result = file_put_contents($backup_file, $sql_content); if ($result === false) { throw new Exception(__('Could not create emergency backup file.', 'tigerstyle-life9')); } return $backup_file; } /** * Restore files from backup */ private function restore_files_from_backup($temp_dir) { // Check if backup has WordPress files at root level (current format) $wp_content_backup = $temp_dir . '/wp-content'; // Also check legacy format with wordpress/ subdirectory if (!file_exists($wp_content_backup)) { $legacy_wordpress_dir = $temp_dir . '/wordpress'; if (file_exists($legacy_wordpress_dir . '/wp-content')) { $wp_content_backup = $legacy_wordpress_dir . '/wp-content'; } } if (!file_exists($wp_content_backup)) { throw new Exception(__('WordPress wp-content directory not found in backup.', 'tigerstyle-life9')); } // Restore wp-content directory (themes, plugins, uploads) if (file_exists($wp_content_backup)) { $this->copy_directory($wp_content_backup, WP_CONTENT_DIR); } // Restore other WordPress files (excluding wp-config.php for safety) // Determine the correct source directory for core files $core_files_source = file_exists($temp_dir . '/wp-includes') ? $temp_dir : $temp_dir . '/wordpress'; if (file_exists($core_files_source . '/wp-includes')) { $this->restore_core_files($core_files_source, ABSPATH); } else { error_log("TigerStyle Life9: No WordPress core files found in backup, skipping core file restoration"); } error_log("TigerStyle Life9: Files restored successfully"); } /** * Copy directory recursively */ private function copy_directory($source, $destination) { if (!is_dir($source)) { return false; } if (!is_dir($destination)) { wp_mkdir_p($destination); } $dir = opendir($source); while (($file = readdir($dir)) !== false) { if ($file != '.' && $file != '..') { $src = $source . '/' . $file; $dst = $destination . '/' . $file; if (is_dir($src)) { $this->copy_directory($src, $dst); } else { copy($src, $dst); } } } closedir($dir); return true; } /** * Restore core WordPress files (excluding sensitive files) */ private function restore_core_files($source_dir, $destination_dir) { $excluded_files = ['wp-config.php', '.htaccess']; // Skip sensitive files $dir = opendir($source_dir); while (($file = readdir($dir)) !== false) { if ($file != '.' && $file != '..' && !in_array($file, $excluded_files)) { $src = $source_dir . '/' . $file; $dst = $destination_dir . '/' . $file; if (is_file($src) && $file != 'wp-content') { // Skip wp-content as it's handled separately copy($src, $dst); } } } closedir($dir); } /** * Clean up directory recursively */ private function cleanup_directory($dir) { if (!is_dir($dir)) { return; } $files = array_diff(scandir($dir), ['.', '..']); foreach ($files as $file) { $path = $dir . '/' . $file; if (is_dir($path)) { $this->cleanup_directory($path); } else { unlink($path); } } rmdir($dir); } /** * Plugin activation - create download tokens table */ public function activate_plugin() { global $wpdb; $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, token varchar(11) NOT NULL UNIQUE, backup_id varchar(255) NOT NULL, backup_filename varchar(255) NOT NULL, created_by bigint(20) unsigned NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, expires_at datetime NOT NULL, download_count int(11) DEFAULT 0, max_downloads int(11) DEFAULT 1, user_agent text DEFAULT NULL, ip_address varchar(45) DEFAULT NULL, last_downloaded_at datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY token (token), KEY backup_id (backup_id), KEY created_by (created_by), KEY expires_at (expires_at) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); error_log('TigerStyle Life9: Download tokens table created'); } /** * Public method to manually create database table (for debugging) */ public function manual_create_table() { global $wpdb; $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table_name ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, token varchar(11) NOT NULL UNIQUE, backup_id varchar(255) NOT NULL, backup_filename varchar(255) NOT NULL, created_by bigint(20) unsigned NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, expires_at datetime NOT NULL, download_count int(11) DEFAULT 0, max_downloads int(11) DEFAULT 1, user_agent text DEFAULT NULL, ip_address varchar(45) DEFAULT NULL, last_downloaded_at datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY token (token), KEY backup_id (backup_id), KEY created_by (created_by), KEY expires_at (expires_at) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); $result = dbDelta($sql); error_log('TigerStyle Life9: Manual table creation result: ' . print_r($result, true)); // Check if table exists $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name; error_log('TigerStyle Life9: Table exists after manual creation: ' . ($table_exists ? 'YES' : 'NO')); return $table_exists; } /** * AJAX handler for manual table creation */ public function handle_manual_table_creation() { // Verify user capabilities if (!current_user_can('manage_options')) { wp_die('Unauthorized access'); } $result = $this->manual_create_table(); wp_send_json_success([ 'table_created' => $result, 'message' => $result ? 'Table created successfully' : 'Table creation failed' ]); } /** * Generate YouTube-style short ID (11 characters) */ private function generate_short_id() { $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_'; $id = ''; for ($i = 0; $i < 11; $i++) { $id .= $characters[wp_rand(0, strlen($characters) - 1)]; } return $id; } /** * Create time-limited download token */ private function create_download_token($backup_id, $backup_filename, $expires_in_minutes = 10, $max_downloads = 1) { global $wpdb; $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; // Generate unique token do { $token = $this->generate_short_id(); $existing = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE token = %s", $token )); } while ($existing > 0); $expires_at = date('Y-m-d H:i:s', time() + ($expires_in_minutes * 60)); $result = $wpdb->insert( $table_name, [ 'token' => $token, 'backup_id' => $backup_id, 'backup_filename' => $backup_filename, 'created_by' => get_current_user_id(), 'expires_at' => $expires_at, 'max_downloads' => $max_downloads, 'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ], [ '%s', '%s', '%s', '%d', '%s', '%d', '%s', '%s' ] ); if ($result === false) { throw new Exception(__('Failed to create download token.', 'tigerstyle-life9')); } error_log("TigerStyle Life9: Created download token $token for backup $backup_filename, expires at $expires_at"); return [ 'token' => $token, 'url' => home_url("/?tigerstyle_dl=$token"), 'expires_at' => $expires_at, 'expires_in_minutes' => $expires_in_minutes ]; } /** * Handle AJAX request to generate download link */ public function handle_generate_download_link() { // Verify nonce if (!wp_verify_nonce($_POST['nonce'], 'tigerstyle_generate_link')) { wp_die(__('Security check failed.', 'tigerstyle-life9')); } // Check permissions if (!current_user_can('manage_options')) { wp_die(__('Insufficient permissions.', 'tigerstyle-life9')); } try { $backup_id = sanitize_text_field($_POST['backup_id']); $expires_minutes = intval($_POST['expires_minutes'] ?? 10); $max_downloads = intval($_POST['max_downloads'] ?? 1); // Validate expiration time (1 minute to 24 hours) if ($expires_minutes < 1 || $expires_minutes > 1440) { $expires_minutes = 10; } // Validate max downloads (1 to 100) if ($max_downloads < 1 || $max_downloads > 100) { $max_downloads = 1; } // Get backup metadata using the same method as the UI $backups = $this->get_existing_backups(); $backup_filename = null; foreach ($backups as $backup) { if ($backup['id'] === $backup_id) { $backup_filename = $backup['id']; // The ID is actually the filename break; } } if (!$backup_filename) { throw new Exception(__('Backup not found.', 'tigerstyle-life9')); } $token_data = $this->create_download_token($backup_id, $backup_filename, $expires_minutes, $max_downloads); wp_send_json_success([ 'token' => $token_data['token'], 'url' => $token_data['url'], 'expires_at' => $token_data['expires_at'], 'expires_in_minutes' => $expires_minutes, 'max_downloads' => $max_downloads ]); } catch (Exception $e) { wp_send_json_error($e->getMessage()); } } /** * Handle time-limited download request */ private function handle_time_limited_download() { global $wpdb; $token = sanitize_text_field($_GET['tigerstyle_dl']); $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; try { // Get token data $token_data = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE token = %s", $token ), ARRAY_A); if (!$token_data) { $this->log_download_attempt($token, 'TOKEN_NOT_FOUND', $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? ''); wp_die(__('🚫 Download link not found or invalid.', 'tigerstyle-life9'), __('Invalid Download Link', 'tigerstyle-life9')); } // Check if token has expired if (strtotime($token_data['expires_at']) < time()) { $this->log_download_attempt($token, 'EXPIRED', $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? '', $token_data['backup_filename']); wp_die(__('πŸ• Download link has expired.', 'tigerstyle-life9'), __('Link Expired', 'tigerstyle-life9')); } // Check download count limit if ($token_data['download_count'] >= $token_data['max_downloads']) { $this->log_download_attempt($token, 'MAX_DOWNLOADS_EXCEEDED', $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? '', $token_data['backup_filename']); wp_die(__('🚫 Download limit reached for this link.', 'tigerstyle-life9'), __('Download Limit Exceeded', 'tigerstyle-life9')); } // Update download count and timestamp $wpdb->update( $table_name, [ 'download_count' => $token_data['download_count'] + 1, 'last_downloaded_at' => current_time('mysql'), 'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ], ['id' => $token_data['id']], ['%d', '%s', '%s', '%s'], ['%d'] ); // Log successful download attempt $this->log_download_attempt($token, 'SUCCESS', $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? '', $token_data['backup_filename']); // Determine backup location and serve file $backups = $this->get_existing_backups(); $backup_metadata = null; foreach ($backups as $backup) { if ($backup['id'] === $token_data['backup_filename']) { $backup_metadata = $backup; break; } } if (!$backup_metadata) { throw new Exception(__('Backup metadata not found.', 'tigerstyle-life9')); } // Serve the file based on storage type if ($backup_metadata['storage'] === 's3') { $this->download_from_s3($token_data['backup_filename'], $backup_metadata); } else { $this->download_from_local($token_data['backup_filename'], $backup_metadata); } } catch (Exception $e) { $this->log_download_attempt($token, 'ERROR', $_SERVER['REMOTE_ADDR'] ?? '', $_SERVER['HTTP_USER_AGENT'] ?? '', $token_data['backup_filename'] ?? '', $e->getMessage()); wp_die(__('Download failed: ', 'tigerstyle-life9') . $e->getMessage()); } } /** * Log download attempt for tracking */ private function log_download_attempt($token, $status, $ip_address, $user_agent, $backup_filename = '', $error_message = '') { error_log(sprintf( 'TigerStyle Life9 Download: Token=%s Status=%s IP=%s UserAgent=%s File=%s Error=%s', $token, $status, $ip_address, substr($user_agent, 0, 100), $backup_filename, $error_message )); } /** * Get download token statistics */ public function get_download_token_stats($backup_id = null) { global $wpdb; $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; $where_clause = ''; $params = []; if ($backup_id) { $where_clause = 'WHERE backup_id = %s'; $params[] = $backup_id; } $stats = $wpdb->get_row($wpdb->prepare( "SELECT COUNT(*) as total_tokens, SUM(download_count) as total_downloads, COUNT(CASE WHEN expires_at > NOW() THEN 1 END) as active_tokens, COUNT(CASE WHEN expires_at <= NOW() THEN 1 END) as expired_tokens FROM $table_name $where_clause", $params ), ARRAY_A); return $stats; } /** * Clean up expired tokens (AJAX handler) */ public function cleanup_expired_tokens() { if (!current_user_can('manage_options')) { wp_die(__('Insufficient permissions.', 'tigerstyle-life9')); } global $wpdb; $table_name = $wpdb->prefix . 'tigerstyle_download_tokens'; $deleted = $wpdb->query( "DELETE FROM $table_name WHERE expires_at <= NOW()" ); wp_send_json_success([ 'deleted_count' => $deleted, 'message' => sprintf(__('Cleaned up %d expired tokens.', 'tigerstyle-life9'), $deleted) ]); } } /** * Initialize the complete plugin * * @return TigerStyle_Life9_Complete */ function tigerstyle_life9_complete_init() { return TigerStyle_Life9_Complete::instance(); } // Start the complete plugin tigerstyle_life9_complete_init();