diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..d68cefe --- /dev/null +++ b/admin.php @@ -0,0 +1,270 @@ + + + + + + + Connexion - ICO + + + + + +
+

Connexion

+ +
+ +
+
+ + +
+
+ + +
+ +
+
+ + + + + + + + + Administration - ICO + + + + + +
+

Administration ICO

+
+ Accéder à la galerie + Changer le mot de passe + Déconnexion +
+
+
+ +
+ + + + +
+ + + +
+ + + + +
+
+ + + + + + + + + Changer le mot de passe - ICO + + + + + +
+

Changer le mot de passe

+ Retour +
+
+ +
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + + prepare('SELECT id, password_hash FROM admins WHERE username = :username'); + $stmt->bindValue(':username', $username, SQLITE3_TEXT); + $result = $stmt->execute(); + + if ($user = $result->fetchArray()) { + if (password_verify($password, $user['password_hash'])) { + $_SESSION['admin_id'] = $user['id']; + header('Location: admin.php'); + exit; + } + } + + showLoginForm('Identifiants incorrects'); + return; + } + + showLoginForm(); +} + +// Gérer le changement de mot de passe +function handlePasswordChange() { + checkAuth(); + + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + header('Location: admin.php'); + return; + } + + $currentPassword = $_POST['current_password'] ?? ''; + $newPassword = $_POST['new_password'] ?? ''; + $confirmPassword = $_POST['confirm_password'] ?? ''; + + // Vérifier que les nouveaux mots de passe correspondent + if ($newPassword !== $confirmPassword) { + $_SESSION['error_message'] = "Les nouveaux mots de passe ne correspondent pas."; + header('Location: admin.php?action=show_change_password'); + return; + } + + // Vérifier que le nouveau mot de passe est assez long + if (strlen($newPassword) < 8) { + $_SESSION['error_message'] = "Le nouveau mot de passe doit faire au moins 8 caractères."; + header('Location: admin.php?action=show_change_password'); + return; + } + + $db = getDB(); + + // Vérifier l'ancien mot de passe + $stmt = $db->prepare('SELECT password_hash FROM admins WHERE id = :id'); + $stmt->bindValue(':id', $_SESSION['admin_id'], SQLITE3_INTEGER); + $result = $stmt->execute(); + $user = $result->fetchArray(); + + if (!password_verify($currentPassword, $user['password_hash'])) { + $_SESSION['error_message'] = "Le mot de passe actuel est incorrect."; + header('Location: admin.php?action=show_change_password'); + return; + } + + // Mettre à jour le mot de passe + $newHash = password_hash($newPassword, PASSWORD_DEFAULT); + $stmt = $db->prepare('UPDATE admins SET password_hash = :hash WHERE id = :id'); + $stmt->bindValue(':hash', $newHash, SQLITE3_TEXT); + $stmt->bindValue(':id', $_SESSION['admin_id'], SQLITE3_INTEGER); + + if ($stmt->execute()) { + $_SESSION['success_message'] = "Mot de passe changé avec succès."; + header('Location: admin.php'); + } else { + $_SESSION['error_message'] = "Une erreur est survenue lors du changement de mot de passe."; + header('Location: admin.php?action=show_change_password'); + } + return; +} + +// Gérer la déconnexion +function handleLogout() { + session_destroy(); + header('Location: admin.php'); + exit; +} + +// Router principal +$action = $_GET['action'] ?? 'home'; + +switch ($action) { + case 'login': + handleLogin(); + break; + + case 'logout': + handleLogout(); + break; + + case 'show_change_password': + showChangePasswordForm(); + break; + + case 'change_password': + handlePasswordChange(); + break; + + default: + showAdminInterface(); + break; +} +?> \ No newline at end of file diff --git a/arbre-img.php b/arbre-img.php new file mode 100644 index 0000000..aa0bf28 --- /dev/null +++ b/arbre-img.php @@ -0,0 +1,247 @@ + 0) { + $_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès."; + } + if (!empty($errors)) { + $_SESSION['error_message'] = implode("\n", $errors); + } + break; + + case 'delete': + $images = $_POST['images'] ?? []; + $deleteCount = 0; + + foreach ($images as $image) { + $imagePath = $currentPath . '/' . basename($image); + if (isSecurePath($imagePath) && file_exists($imagePath)) { + if (unlink($imagePath)) { + $deleteCount++; + } + } + } + + if ($deleteCount > 0) { + $_SESSION['success_message'] = "$deleteCount image(s) supprimée(s)."; + } + break; + } + } + header('Location: arbre-img.php?path=' . urlencode($currentPath)); + exit; +} + +// Récupérer les images du dossier courant +$images = []; +$tempImages = []; +foreach (new DirectoryIterator($currentPath) as $file) { + if ($file->isDot()) continue; + if ($file->isFile()) { + $extension = strtolower($file->getExtension()); + if (in_array($extension, ALLOWED_EXTENSIONS)) { + $tempImages[] = [ + 'name' => $file->getFilename(), + 'time' => $file->getCTime() + ]; + } + } +} + +// Trier par date de création décroissante +usort($tempImages, function($a, $b) { + return $b['time'] - $a['time']; +}); + +// Extraire uniquement les noms de fichiers +$images = array_map(function($img) { + return $img['name']; +}, $tempImages); +?> + + + + + + + Gestion des images - ICO + + + + + +
+

Gestion des images

+
+ + + + Retour + +
+
+ +
+ +
+ + + + +
+ + + +
+

Glissez-déposez vos images ici ou cliquez sur "Ajouter des images"

+
+ + +
+
+ +
+ +
+ +
+ + <?php echo htmlspecialchars($image); ?> +
+ +
+
+ +
+
+
+ + + + diff --git a/arbre.php b/arbre.php new file mode 100644 index 0000000..6414ef1 --- /dev/null +++ b/arbre.php @@ -0,0 +1,242 @@ +'; + foreach (new DirectoryIterator($path) as $item) { + if ($item->isDot()) continue; + if ($item->isDir()) { + $fullPath = $item->getPathname(); + $info = getAlbumInfo($fullPath); + $isCurrentPath = realpath($fullPath) === $currentPath; + $hasSubfolders = hasSubfolders($fullPath); + + $output .= '
  • '; + $output .= '
    '; + $output .= ''; + $output .= '📁 ' . htmlspecialchars($info['title']); + $output .= ''; + $output .= '
    '; + if (!$hasSubfolders) { + $output .= '🖼️'; + } + $output .= ''; + $output .= ''; + if ($fullPath !== './liste_albums') { + $output .= ''; + } + $output .= '
    '; + $output .= generateTree($fullPath, $currentPath); + $output .= '
  • '; + } + } + $output .= ''; + return $output; +} +?> + + + + + + + Arborescence - ICO + + + + + +
    +

    Gestion de l'arborescence

    +
    + + Retour +
    +
    + +
    + +
    + + + + +
    + + + +
    + +
    +
    + + + + + + + + + + + + + \ No newline at end of file diff --git a/styles-admin.css b/styles-admin.css new file mode 100644 index 0000000..ad4386a --- /dev/null +++ b/styles-admin.css @@ -0,0 +1,559 @@ +/* Reset et styles de base pour l'admin */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', system-ui, sans-serif; + background-color: #121212; + color: #ffffff; + min-height: 100vh; + overflow-x: hidden; +} + +/* Page d'administration */ +.admin-page { + background-color: #121212; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem; +} + +/* Page de connexion */ +.admin-login { + width: 90%; + max-width: 400px; + margin: auto; + background-color: #1e1e1e; + padding: 2rem; + border-radius: 1rem; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); +} + +.admin-login h1 { + font-size: 2rem; + margin-bottom: 2rem; + text-align: center; +} + +/* En-tête de l'administration */ +.admin-header { + width: 100%; + max-width: 1200px; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; + padding-bottom: 1rem; + border-bottom: 1px solid #2a2a2a; +} + +.admin-header h1 { + font-size: 2rem; + margin: 0; +} + +/* Zone de contenu principale */ +.admin-content { + width: 100%; + max-width: 1200px; + background-color: #1e1e1e; + padding: 2rem; + border-radius: 1rem; + min-height: calc(100vh - 12rem); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.2); +} + +/* Boutons d'action */ +.admin-actions { + display: flex; + gap: 1rem; +} + +.action-button { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.8rem 1.5rem; + background-color: #2196f3; + color: white; + border: none; + border-radius: 2rem; + cursor: pointer; + font-size: 1rem; + text-decoration: none; + transition: all 0.3s ease; +} + +.action-button:hover { + background-color: #1976d2; + transform: translateY(-2px); +} + +.action-button-success { + background-color: #28a745; +} + +.action-button-success:hover { + background-color: #218838; +} + +.action-button-danger { + background-color: #dc3545; +} + +.action-button-danger:hover { + background-color: #c82333; +} + +.action-button-secondary { + background-color: #6c757d; +} + +.action-button-secondary:hover { + background-color: #5a6268; +} + +/* Menu administration */ +.admin-menu { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 2rem; + padding: 1rem; +} + +.admin-menu-item { + aspect-ratio: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + background: #1e1e1e; + border: 2px solid #333; + border-radius: 1.5rem; + text-decoration: none; + color: white; + padding: 2rem; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.admin-menu-item:hover { + transform: translateY(-5px); + border-color: #2196f3; + box-shadow: 0 5px 20px rgba(33, 150, 243, 0.15); +} + +.menu-icon { + margin-bottom: 1rem; +} + +.menu-icon svg { + width: 64px; + height: 64px; + stroke: #fff; + transition: all 0.3s ease; +} + +.admin-menu-item:hover .menu-icon svg { + stroke: #2196f3; + transform: scale(1.1); +} + +.menu-content h2 { + font-size: 1.2rem; + font-weight: 500; + margin: 0; +} + +.menu-content p { + position: absolute; + left: 0; + right: 0; + bottom: -100%; + padding: 1rem; + background: rgba(33, 150, 243, 0.95); + color: white; + font-size: 0.9rem; + transition: 0.3s ease; +} + +.admin-menu-item:hover .menu-content p { + bottom: 0; +} + +/* Formulaires */ +.form-group { + margin-bottom: 1.5rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + color: #e0e0e0; +} + +.form-group input { + width: 100%; + padding: 0.8rem; + border: none; + border-radius: 0.5rem; + background-color: #2a2a2a; + color: white; + font-size: 1rem; +} + +.form-group input:focus { + outline: 2px solid #2196f3; + background-color: #333; +} + +.form-container { + max-width: 500px; + margin: 0 auto; +} + +.form-actions { + margin-top: 2rem; + display: flex; + justify-content: flex-end; + gap: 1rem; +} + +.form-textarea { + width: 100%; + padding: 0.8rem; + border: none; + border-radius: 0.5rem; + background-color: #2a2a2a; + color: white; + font-size: 1rem; + min-height: 100px; + resize: vertical; +} + +.form-textarea:focus { + outline: 2px solid #2196f3; + background-color: #333; +} + +/* Messages */ +.message { + padding: 1rem; + border-radius: 0.5rem; + margin-bottom: 1.5rem; + text-align: center; +} + +.success-message { + background-color: #28a745; + color: white; +} + +.error-message { + background-color: #dc3545; + color: white; +} + +/* Styles pour l'arborescence */ +.tree-container { + padding: 1rem; + background: #2a2a2a; + border-radius: 0.5rem; +} + +.tree-list { + list-style: none; + padding-left: 1.5rem; +} + +.tree-item { + margin: 0.5rem 0; +} + +.tree-item-content { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem; + border-radius: 0.25rem; + background: #1e1e1e; +} + +.tree-item.active > .tree-item-content { + background: #2196f3; +} + +.tree-link { + color: white; + text-decoration: none; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.folder-icon { + font-size: 1.2rem; +} + +.tree-actions { + display: flex; + gap: 0.5rem; +} + +.tree-button { + background: none; + border: none; + color: white; + cursor: pointer; + padding: 0.25rem; + border-radius: 0.25rem; + font-size: 1rem; +} + +.tree-button:hover { + background: rgba(255, 255, 255, 0.1); +} + +.tree-button-danger:hover { + background: rgba(220, 53, 69, 0.2); +} + +.image-actions .tree-button { + background-color: rgba(0, 0, 0, 0.7); + padding: 0.5rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.image-actions .tree-button:hover { + background-color: rgba(220, 53, 69, 0.9); + transform: scale(1.1); +} + +.tree-button-danger { + background-color: rgba(220, 53, 69, 0.7); +} + +/* Styles pour le toggle des sous-dossiers */ +.tree-item-content { + position: relative; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + border-radius: 0.25rem; + background: #1e1e1e; + margin: 0.25rem 0; +} + +.toggle-btn { + background: none; + border: none; + padding: 0.25rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: #fff; + transition: transform 0.3s ease; +} + +.toggle-btn.active { + transform: rotate(90deg); +} + +.toggle-spacer { + width: 24px; +} + +.toggle-icon { + width: 16px; + height: 16px; + transition: transform 0.3s ease; +} + +.subtree { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out; + opacity: 0; +} + +.subtree.active { + max-height: 1000px; + opacity: 1; + transition: max-height 0.3s ease-in, opacity 0.3s ease-in; +} + +/* Ajuster les marges et le padding pour une meilleure hiérarchie visuelle */ +.tree-list { + list-style: none; + padding-left: 1.5rem; + margin: 0; +} + +.tree-list:first-child { + padding-left: 0; +} + +/* Animation pour le hover des boutons */ +.toggle-btn:hover { + color: #2196f3; + background: rgba(33, 150, 243, 0.1); + border-radius: 4px; +} + +/* Styles pour les modales */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + z-index: 1000; +} + +.modal-content { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: #1e1e1e; + padding: 2rem; + border-radius: 1rem; + min-width: 300px; + max-width: 500px; + width: 90%; +} + +.modal h2 { + margin-bottom: 1.5rem; +} + +/* Styles pour les listes d'images */ +.images-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 1rem; + padding: 1rem; +} + +.image-item { + position: relative; + aspect-ratio: 1; + border-radius: 0.5rem; + overflow: hidden; + background-color: #2a2a2a; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.image-item:hover { + transform: translateY(-5px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + z-index: 1; +} + +.image-item img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.image-item:hover img { + transform: scale(1.1); +} + +.image-checkbox { + position: absolute; + top: 0.5rem; + left: 0.5rem; + z-index: 2; + width: 24px; + height: 24px; + cursor: pointer; + accent-color: #2196f3; +} + +.image-checkbox:hover { + transform: scale(1.1); +} + +.image-actions { + position: absolute; + top: 0.5rem; + right: 0.5rem; + z-index: 2; +} + +.upload-zone { + border: 2px dashed #2196f3; + border-radius: 1rem; + padding: 2rem; + text-align: center; + margin: 1rem; + background-color: rgba(33, 150, 243, 0.1); +} + +.upload-zone.drag-over { + background-color: rgba(33, 150, 243, 0.2); + border-color: #1976d2; +} + +#imageUploadForm { + display: none; +} + +/* Media Queries */ +@media (max-width: 768px) { + .admin-page { + padding: 1rem; + } + + .admin-header { + flex-direction: column; + gap: 1rem; + text-align: center; + } + + .admin-actions { + flex-direction: column; + width: 100%; + } + + .action-button { + width: 100%; + justify-content: center; + } + + .admin-menu { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 1rem; + } +} + +/* Accessibilité */ +a:focus-visible, +button:focus-visible, +input:focus-visible { + outline: 2px solid #2196f3; + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} \ No newline at end of file