<?php
/**
* Photo Gallery Installation Script
*
* This script initializes the photo gallery system with proper error handling,
* system requirements validation, and database setup.
*
* Usage: php install.php [--force] [--config-only]
*/
require_once 'classes/DataManager.php';
require_once 'classes/ErrorHandler.php';
class PhotoGalleryInstaller
{
/**
* @var mixed
*/
private $dataManager;
/**
* @var mixed
*/
private $errorHandler;
/**
* @var mixed
*/
private $force = false;
/**
* @var mixed
*/
private $configOnly = false;
public function __construct()
{
$this->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();
}