#!/usr/bin/env php /dev/null */ $dryRun = in_array('--dry-run', $argv, true); echo ($dryRun ? "[DRY RUN] " : "") . "Storage path migration: theses/ | documents/ → {objet}/YYYY/...\n"; echo "─────────────────────────────────────────────────────────────\n\n"; // ── Auto-detect paths ────────────────────────────────────────────────────── // Remote deploy: repo root = /var/www/xamxam, storage in storage/ // Local dev: script in scripts/, storage in ../app/storage/ $repoRoot = file_exists(__DIR__ . '/bootstrap.php') ? __DIR__ : dirname(__DIR__); // DB path: try xamxam.db first, fall back to database.sqlite $dbPath = $repoRoot . '/storage/xamxam.db'; if (!file_exists($dbPath)) { $dbPath = $repoRoot . '/storage/database.sqlite'; } $storageRoot = $repoRoot . '/storage'; echo "Repo root: {$repoRoot}\n"; echo "DB path: {$dbPath}\n"; echo "Storage root: {$storageRoot}\n\n"; if (!file_exists($dbPath)) { die("ERROR: Database not found at {$dbPath}\n"); } // ── Connect to DB ─────────────────────────────────────────────────────────── $pdo = new PDO('sqlite:' . $dbPath); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Fetch all files that need migration $sql = "SELECT tf.id, tf.thesis_id, tf.file_path, tf.file_name, t.objet, t.year FROM thesis_files tf JOIN theses t ON t.id = tf.thesis_id WHERE (tf.file_path LIKE 'theses/%' OR tf.file_path LIKE 'documents/%') AND t.objet IS NOT NULL ORDER BY tf.id ASC"; $stmt = $pdo->query($sql); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); $total = count($rows); echo "Found {$total} file(s) to migrate.\n\n"; if ($total === 0) { echo "Nothing to do.\n"; exit(0); } $moved = 0; $skipped = 0; $errors = []; $folderCache = []; foreach ($rows as $row) { $fileId = (int)$row['id']; $thesisId = (int)$row['thesis_id']; $oldPath = $row['file_path']; $objet = $row['objet']; $year = $row['year']; // Parse old path: theses/2025/FOLDERNAME/file.pdf or documents/2025/FOLDERNAME/file.pdf $parts = explode('/', $oldPath); if (count($parts) < 4) { $skipped++; $errors[] = "SKIP file #{$fileId}: unexpected path format '{$oldPath}'"; continue; } $yearDir = $parts[1]; // '2025' $folderName = $parts[2]; // '2025_SMITH_Titre' $fileNameOnly = implode('/', array_slice($parts, 3)); // rest of path // Construct new path $newPath = $objet . '/' . $yearDir . '/' . $folderName . '/' . $fileNameOnly; // Skip if paths are identical if ($newPath === $oldPath) { $skipped++; echo " #{$fileId} SKIP: already correct '{$oldPath}'\n"; continue; } $oldAbs = $storageRoot . '/' . $oldPath; $newAbs = $storageRoot . '/' . $newPath; // Ensure target directory exists $newDir = dirname($newAbs); if (!isset($folderCache[$newDir])) { if (!is_dir($newDir)) { if (!$dryRun) { mkdir($newDir, 0755, true); } echo " MKDIR {$newDir}\n"; } $folderCache[$newDir] = true; } // Check source exists if (!file_exists($oldAbs)) { // If the target already exists (moved by a previous duplicate row), just update DB if (file_exists($newAbs)) { echo " #{$fileId} {$oldPath}\n → {$newPath} (target exists, DB-only update)\n"; if (!$dryRun) { $pdo->prepare('UPDATE thesis_files SET file_path = ? WHERE id = ?') ->execute([$newPath, $fileId]); } $moved++; continue; } $skipped++; echo " #{$fileId} SKIP: source not found '{$oldAbs}'\n"; continue; } // Check target doesn't exist if (file_exists($newAbs)) { $skipped++; echo " #{$fileId} SKIP: target already exists '{$newAbs}'\n"; continue; } echo " #{$fileId} {$oldPath}\n → {$newPath}\n"; if (!$dryRun) { if (!rename($oldAbs, $newAbs)) { $skipped++; $errors[] = "FAIL file #{$fileId}: rename failed '{$oldAbs}' → '{$newAbs}'"; echo " ✗ rename failed\n"; continue; } $pdo->prepare('UPDATE thesis_files SET file_path = ? WHERE id = ?') ->execute([$newPath, $fileId]); } $moved++; } echo "\n─────────────────────────────────────────────────────────────\n"; echo "Summary: {$moved} moved, {$skipped} skipped, " . count($errors) . " errors\n"; if (!empty($errors)) { echo "\nErrors:\n"; foreach ($errors as $err) { echo " {$err}\n"; } } // Clean up empty directories if (!$dryRun) { $oldRoots = [$storageRoot . '/theses', $storageRoot . '/documents']; foreach ($oldRoots as $oldRoot) { removeEmptyDirs($oldRoot); } echo "\nCleaned up empty directories.\n"; } if ($dryRun) { echo "\nRun without --dry-run to apply changes.\n"; } exit(empty($errors) ? 0 : 1); // ── Helpers ──────────────────────────────────────────────────────────────── function removeEmptyDirs(string $dir): void { if (!is_dir($dir)) { return; } $items = @scandir($dir); if ($items === false) { return; } foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path = $dir . '/' . $item; if (is_dir($path)) { removeEmptyDirs($path); } } @rmdir($dir); }