errorHandler = new ErrorHandler(); $this->parseArguments(); } /** * Parse command line arguments */ private function parseArguments() { global $argv; if (isset($argv)) { $this->force = in_array('--force', $argv); $this->configOnly = in_array('--config-only', $argv); } } /** * Main installation process */ public function install() { try { $this->printHeader(); // Step 1: Validate system requirements $this->validateSystemRequirements(); // Step 2: Check existing installation if (!$this->force) { $this->checkExistingInstallation(); } // Step 3: Create directory structure $this->createDirectoryStructure(); // Step 4: Initialize data files $this->initializeDataFiles(); // Step 5: Scan and create albums from existing files if (!$this->configOnly) { $this->scanAndCreateAlbums(); } // Step 6: Set proper permissions $this->setPermissions(); // Step 7: Validate installation if (!$this->configOnly) { $this->validateInstallation(); } $this->printSuccess(); } catch (Exception $e) { $this->handleError($e); exit(1); } } /** * Print installation header */ private function printHeader() { echo "\n"; echo "=====================================\n"; echo " Photo Gallery Installation Script \n"; echo "=====================================\n\n"; if ($this->force) { echo "āš ļø FORCE MODE: Existing data will be overwritten\n\n"; } if ($this->configOnly) { echo "šŸ“ CONFIG ONLY: Only configuration files will be created\n\n"; } } /** * Validate system requirements */ private function validateSystemRequirements() { echo "šŸ” Validating system requirements...\n"; $requirements = [ 'PHP Version' => [ 'check' => version_compare(PHP_VERSION, '7.4.0', '>='), 'message' => 'PHP 7.4.0 or higher required. Current: '.PHP_VERSION ], 'GD Extension' => [ 'check' => extension_loaded('gd'), 'message' => 'GD extension is required for image processing' ], 'JSON Extension' => [ 'check' => extension_loaded('json'), 'message' => 'JSON extension is required for data storage' ], 'File Upload Support' => [ 'check' => ini_get('file_uploads'), 'message' => 'File uploads must be enabled in PHP configuration' ], 'Write Permissions' => [ 'check' => is_writable('.'), 'message' => 'Current directory must be writable' ] ]; $failed = []; foreach ($requirements as $name => $requirement) { if ($requirement['check']) { echo " āœ“ $name\n"; } else { echo " āœ— $name: {$requirement['message']}\n"; $failed[] = $name; } } if (!empty($failed)) { throw new Exception("System requirements not met: ".implode(', ', $failed)); } echo "āœ… All system requirements satisfied\n\n"; } /** * Check for existing installation */ private function checkExistingInstallation() { echo "šŸ” Checking for existing installation...\n"; $existingFiles = []; $checkFiles = [ 'data/config.json', 'data/albums.json', 'data/tags.json', 'data/counters.json' ]; foreach ($checkFiles as $file) { if (file_exists($file)) { $existingFiles[] = $file; } } if (!empty($existingFiles)) { echo "āš ļø Existing installation detected:\n"; foreach ($existingFiles as $file) { echo " - $file\n"; } echo "\nUse --force to overwrite existing installation\n"; throw new Exception("Installation already exists. Use --force to overwrite."); } echo "āœ… No existing installation found\n\n"; } /** * Create directory structure */ private function createDirectoryStructure() { echo "šŸ“ Creating directory structure...\n"; $directories = [ 'data', 'data/cache', 'logs', 'albums' ]; foreach ($directories as $dir) { if (!is_dir($dir)) { if (!mkdir($dir, 0755, true)) { throw new Exception("Failed to create directory: $dir"); } echo " āœ“ Created: $dir\n"; } else { echo " āœ“ Exists: $dir\n"; } } echo "āœ… Directory structure created\n\n"; } /** * Initialize data files */ private function initializeDataFiles() { echo "šŸ“„ Initializing data files...\n"; try { $this->dataManager = new DataManager(); $this->dataManager->initializeData(); echo " āœ“ albums.json initialized\n"; echo " āœ“ tags.json initialized\n"; echo " āœ“ counters.json initialized\n"; echo " āœ“ config.json initialized\n"; } catch (Exception $e) { throw new Exception("Failed to initialize data files: ".$e->getMessage()); } echo "āœ… Data files initialized\n\n"; } /** * Scan existing albums directory and create/update album entries */ private function scanAndCreateAlbums() { echo "šŸ” Scanning for existing albums and photos...\n"; if (!is_dir('albums')) { echo " āš ļø Albums directory not found, skipping scan\n"; return; } $albumDirs = glob('albums/*', GLOB_ONLYDIR); $createdAlbums = 0; $updatedAlbums = 0; $totalPhotos = 0; // Get current albums data $albumsData = $this->dataManager->getAlbums(); $nextId = empty($albumsData) ? 1 : max(array_keys($albumsData)) + 1; // Create mapping of existing albums by folder name for quick lookup // Map folder names to album IDs based on the order they appear $albumDirNames = []; foreach ($albumDirs as $dir) { $albumDirNames[] = basename($dir); } sort($albumDirNames, SORT_NATURAL); // Sort naturally (1, 2, 10, 11, etc.) $existingAlbumsByFolder = []; $albumIndex = 0; foreach ($albumsData as $id => $album) { if ($albumIndex < count($albumDirNames)) { $folderName = $albumDirNames[$albumIndex]; $existingAlbumsByFolder[$folderName] = $id; $albumIndex++; } } foreach ($albumDirs as $dir) { $albumId = basename($dir); $photos = glob($dir.'/*.{jpg,jpeg,png,gif,webp,bmp,tiff}', GLOB_BRACE); if (empty($photos)) { echo " āš ļø No photos found in $albumId, skipping\n"; continue; } // Create photo data $photoData = []; foreach ($photos as $index => $photo) { $filename = basename($photo); $filesize = filesize($photo); // Get image dimensions $imageInfo = @getimagesize($photo); $width = $imageInfo ? $imageInfo[0] : 1200; $height = $imageInfo ? $imageInfo[1] : 800; $photoData[] = [ 'id' => $index + 1, 'filename' => $filename, 'original_filename' => $filename, 'file_size' => $filesize, 'width' => $width, 'height' => $height, 'uploaded_at' => date('c', filemtime($photo)) ]; } // Check if album already exists if (isset($existingAlbumsByFolder[$albumId])) { // Update existing album - keep existing data, update only photos $existingId = $existingAlbumsByFolder[$albumId]; $existingAlbum = $albumsData[$existingId]; $albumsData[$existingId] = [ 'id' => $existingAlbum['id'], 'title' => $existingAlbum['title'], // Keep existing title 'description' => $existingAlbum['description'], // Keep existing description 'created_at' => $existingAlbum['created_at'], // Keep original creation date 'updated_at' => date('c'), // Update modification date 'photo_count' => count($photoData), 'is_rss_enabled' => $existingAlbum['is_rss_enabled'], // Keep existing RSS setting 'tags' => $existingAlbum['tags'], // Keep existing tags 'photos' => $photoData // Update with current photos ]; echo " āœ“ Updated album '{$existingAlbum['title']}' with ".count($photoData)." photos\n"; $updatedAlbums++; } else { // Create new album $albumsData[$albumId] = [ 'id' => $albumId, 'title' => "Album $albumId", 'description' => "Photo collection $albumId", 'created_at' => date('c'), 'updated_at' => date('c'), 'photo_count' => count($photoData), 'is_rss_enabled' => true, 'tags' => [], 'photos' => $photoData ]; echo " āœ“ Created album 'Album $albumId' with ".count($photoData)." photos\n"; $createdAlbums++; $nextId++; } $totalPhotos += count($photoData); } // Save updated albums data if ($createdAlbums > 0 || $updatedAlbums > 0) { // Use the private method through reflection $reflection = new ReflectionClass($this->dataManager); $method = $reflection->getMethod('saveJsonFile'); $method->setAccessible(true); $method->invoke($this->dataManager, 'albums.json', $albumsData); echo "āœ… Processed albums: $createdAlbums created, $updatedAlbums updated ($totalPhotos total photos)\n\n"; } else { echo " ā„¹ļø No albums created or updated from existing files\n\n"; } } /** * Set proper file permissions */ private function setPermissions() { echo "šŸ” Setting file permissions...\n"; $permissions = [ 'data' => 0755, 'data/cache' => 0755, 'logs' => 0755, 'albums' => 0755, 'data/config.json' => 0644, 'data/albums.json' => 0644, 'data/tags.json' => 0644, 'data/counters.json' => 0644 ]; foreach ($permissions as $path => $permission) { if (file_exists($path)) { if (!chmod($path, $permission)) { echo " āš ļø Warning: Could not set permissions for $path\n"; } else { echo " āœ“ Set permissions for $path\n"; } } } echo "āœ… Permissions configured\n\n"; } /** * Validate installation */ private function validateInstallation() { echo "āœ… Validating installation...\n"; // Test DataManager functionality try { $config = $this->dataManager->getAllConfig(); echo " āœ“ Configuration loaded successfully\n"; $albums = $this->dataManager->getAlbums(); echo " āœ“ Albums data accessible\n"; $tags = $this->dataManager->getTags(); echo " āœ“ Tags data accessible\n"; } catch (Exception $e) { throw new Exception("Installation validation failed: ".$e->getMessage()); } // Test API endpoint if (file_exists('api.php')) { echo " āœ“ API endpoint available\n"; } else { echo " āš ļø Warning: api.php not found\n"; } echo "āœ… Installation validated\n\n"; } /** * Print success message */ private function printSuccess() { echo "šŸŽ‰ Installation completed successfully!\n\n"; echo "Next steps:\n"; echo "1. Configure your web server to serve the gallery\n"; echo "2. Access the gallery through your web browser\n"; echo "3. Start uploading photos and creating albums\n\n"; if ($this->dataManager) { echo "Default configuration:\n"; $config = $this->dataManager->getAllConfig(); $importantSettings = [ 'upload_max_file_size', 'image_webp_quality', 'storage_max_total_size', 'rss_title' ]; foreach ($importantSettings as $setting) { if (isset($config[$setting])) { $value = $config[$setting]; if ($setting === 'upload_max_file_size' || $setting === 'storage_max_total_size') { $value = $this->formatBytes($value); } echo " $setting: $value\n"; } } } echo "\n"; } /** * Handle installation errors */ private function handleError($exception) { echo "\nāŒ Installation failed!\n"; echo "Error: ".$exception->getMessage()."\n"; if ($this->errorHandler) { $this->errorHandler->logError('INSTALLATION_ERROR', $exception->getMessage(), [ 'file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString() ]); } echo "\nTroubleshooting:\n"; echo "1. Check file permissions on the installation directory\n"; echo "2. Ensure PHP has write access to create directories and files\n"; echo "3. Verify all system requirements are met\n"; echo "4. Check the error log for detailed information\n"; } /** * Format bytes for human readable output */ private function formatBytes($bytes) { $units = ['B', 'KB', 'MB', 'GB']; $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1 << (10 * $pow)); return round($bytes, 2).' '.$units[$pow]; } } // Run installation if called directly if (basename(__FILE__) == basename($_SERVER['SCRIPT_NAME'])) { $installer = new PhotoGalleryInstaller(); $installer->install(); }