Compare commits
No commits in common. "main" and "1.0.2" have entirely different histories.
@ -38,9 +38,3 @@ Options -Indexes
|
|||||||
Header set X-Frame-Options "SAMEORIGIN"
|
Header set X-Frame-Options "SAMEORIGIN"
|
||||||
Header set X-XSS-Protection "1; mode=block"
|
Header set X-XSS-Protection "1; mode=block"
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# Bloquer l'accès direct aux images privées
|
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteRule ^liste_albums_prives/.+\.(jpg|jpeg|png|gif)$ - [F]
|
|
||||||
</IfModule>
|
|
@ -11,4 +11,3 @@ Il s'agit d'un outil de galeries d'illustrations simple et léger utilisant prin
|
|||||||
|
|
||||||
### Crédits
|
### Crédits
|
||||||
Le code d'**ICO** a été réalisé en grande partie grâce à l'aide de [Claude.ia](https://claude.ai/).
|
Le code d'**ICO** a été réalisé en grande partie grâce à l'aide de [Claude.ia](https://claude.ai/).
|
||||||
Le logo vient de [Freepik.com](https://fr.freepik.com/).
|
|
60
admin.php
60
admin.php
@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'fonctions.php';
|
require_once 'fonctions.php';
|
||||||
session_start();
|
session_start();
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Vérifier si un utilisateur est connecté
|
// Vérifier si un utilisateur est connecté
|
||||||
function checkAuth() {
|
function checkAuth() {
|
||||||
@ -149,68 +148,15 @@ function showAdminInterface() {
|
|||||||
</a>
|
</a>
|
||||||
<a href="personnalisation.php" class="admin-menu-item">
|
<a href="personnalisation.php" class="admin-menu-item">
|
||||||
<div class="menu-icon">
|
<div class="menu-icon">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<circle cx="12" cy="12" r="3"></circle>
|
<path d="M12 20s8-3 8-10V4l-8-2-8 2v6c0 7 8 10 8 10z"/>
|
||||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-content">
|
<div class="menu-content">
|
||||||
<h2>Options de personnalisation</h2>
|
<h2>Personnalisation</h2>
|
||||||
<p>Personnalisez le titre et la description de votre galerie.</p>
|
<p>Personnalisez le titre et la description de votre galerie.</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<?php if ($_SESSION['admin_id'] == $firstId): ?>
|
|
||||||
<a href="logs.php" class="admin-menu-item">
|
|
||||||
<div class="menu-icon">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
||||||
<polyline points="14 2 14 8 20 8"></polyline>
|
|
||||||
<line x1="16" y1="13" x2="8" y2="13"></line>
|
|
||||||
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
||||||
<polyline points="10 9 9 9 8 9"></polyline>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="menu-content">
|
|
||||||
<h2>Logs système</h2>
|
|
||||||
<p>Consultez l'historique des actions des administrateurs.</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php
|
|
||||||
$updateStatus = checkUpdate();
|
|
||||||
$updateAvailable = $updateStatus && $updateStatus['available'];
|
|
||||||
$menuItemClass = 'admin-menu-item' . ($updateAvailable ? ' update-available' : ' disabled');
|
|
||||||
?>
|
|
||||||
<?php if ($updateAvailable): ?>
|
|
||||||
<a href="https://git.crystalyx.net/camelia-studio/ICO/releases/tag/<?php echo htmlspecialchars($updateStatus['latest']); ?>"
|
|
||||||
class="<?php echo $menuItemClass; ?>"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer">
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="<?php echo $menuItemClass; ?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<div class="menu-icon">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" rx="4" />
|
|
||||||
<path
|
|
||||||
d="M7 14l5-5 5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="menu-content">
|
|
||||||
<h2>Mise à jour</h2>
|
|
||||||
<?php if ($updateAvailable): ?>
|
|
||||||
<div class="update-status">
|
|
||||||
Version actuelle : <?php echo htmlspecialchars($updateStatus['current']); ?>
|
|
||||||
Dernière version : <?php echo htmlspecialchars($updateStatus['latest']); ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php if ($updateAvailable): ?>
|
|
||||||
</a>
|
|
||||||
<?php else: ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php include 'footer.php'; ?>
|
<?php include 'footer.php'; ?>
|
||||||
|
23
albums.php
23
albums.php
@ -17,22 +17,17 @@ $currentAlbumInfo = getAlbumInfo($currentPath);
|
|||||||
|
|
||||||
// Récupérer tous les sous-dossiers
|
// Récupérer tous les sous-dossiers
|
||||||
$albums = [];
|
$albums = [];
|
||||||
$tempAlbums = [];
|
|
||||||
foreach (new DirectoryIterator($currentPath) as $item) {
|
foreach (new DirectoryIterator($currentPath) as $item) {
|
||||||
if ($item->isDot()) continue;
|
if ($item->isDot()) continue;
|
||||||
if ($item->isDir()) {
|
if ($item->isDir()) {
|
||||||
$albumPath = $item->getPathname();
|
$albumPath = $item->getPathname();
|
||||||
|
|
||||||
// Ajout de la vérification de sécurité
|
|
||||||
if (!isSecurePath($albumPath)) continue;
|
|
||||||
|
|
||||||
$info = getAlbumInfo($albumPath);
|
$info = getAlbumInfo($albumPath);
|
||||||
|
|
||||||
$images = hasSubfolders($albumPath) ?
|
$images = hasSubfolders($albumPath) ?
|
||||||
getImagesRecursively($albumPath) :
|
getImagesRecursively($albumPath) :
|
||||||
getLatestImages($albumPath);
|
getLatestImages($albumPath);
|
||||||
|
|
||||||
$tempAlbums[] = [
|
$albums[] = [
|
||||||
'path' => str_replace('\\', '/', $albumPath),
|
'path' => str_replace('\\', '/', $albumPath),
|
||||||
'title' => $info['title'],
|
'title' => $info['title'],
|
||||||
'description' => $info['description'],
|
'description' => $info['description'],
|
||||||
@ -44,13 +39,6 @@ foreach (new DirectoryIterator($currentPath) as $item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tri alphabétique des albums par titre
|
|
||||||
usort($tempAlbums, function($a, $b) {
|
|
||||||
return strcasecmp($a['title'], $b['title']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$albums = $tempAlbums;
|
|
||||||
|
|
||||||
// Déterminer le chemin parent pour le bouton retour
|
// Déterminer le chemin parent pour le bouton retour
|
||||||
$parentPath = dirname($currentPath);
|
$parentPath = dirname($currentPath);
|
||||||
if (!isSecurePath($parentPath)) {
|
if (!isSecurePath($parentPath)) {
|
||||||
@ -92,14 +80,7 @@ $config = getSiteConfig();
|
|||||||
<div class="empty-album"></div>
|
<div class="empty-album"></div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($album['images'] as $index => $image): ?>
|
<?php foreach ($album['images'] as $index => $image): ?>
|
||||||
<div class="album-image">
|
<div class="album-image" style="background-image: url('<?php echo htmlspecialchars($image); ?>')"></div>
|
||||||
<div class="image-background <?php echo is_array($image) && $image['is_mature'] ? 'mature-preview' : ''; ?>"
|
|
||||||
style="background-image: url('<?php echo htmlspecialchars(is_array($image) ? $image['url'] : $image); ?>')">
|
|
||||||
</div>
|
|
||||||
<?php if (is_array($image) && $image['is_mature']): ?>
|
|
||||||
<div class="mature-preview-indicator">🔞</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php for ($i = count($album['images']); $i < 4; $i++): ?>
|
<?php for ($i = count($album['images']); $i < 4; $i++): ?>
|
||||||
<div class="empty-image"></div>
|
<div class="empty-image"></div>
|
||||||
|
@ -7,7 +7,6 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Récupérer le chemin courant
|
// Récupérer le chemin courant
|
||||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums_prives';
|
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums_prives';
|
||||||
@ -25,7 +24,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
switch ($_POST['action']) {
|
switch ($_POST['action']) {
|
||||||
case 'upload':
|
case 'upload':
|
||||||
$uploadedFiles = $_FILES['images'] ?? [];
|
$uploadedFiles = $_FILES['images'] ?? [];
|
||||||
$successCount = 0; // Initialiser le compteur
|
$successCount = 0;
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
// Gérer les uploads multiples
|
// Gérer les uploads multiples
|
||||||
@ -51,7 +50,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (move_uploaded_file($tmpName, $destination)) {
|
if (move_uploaded_file($tmpName, $destination)) {
|
||||||
$successCount++; // Incrémenter le compteur en cas de succès
|
$successCount++;
|
||||||
} else {
|
} else {
|
||||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||||
}
|
}
|
||||||
@ -61,16 +60,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loguer l'action une fois que tous les uploads sont terminés
|
|
||||||
if ($successCount > 0) {
|
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'UPLOAD_PRIVATE_IMAGES', // Notez le changement ici pour les images privées
|
|
||||||
"Téléversement de $successCount image(s) privée(s)", // Message adapté pour les images privées
|
|
||||||
$currentPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($successCount > 0) {
|
if ($successCount > 0) {
|
||||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||||
}
|
}
|
||||||
@ -107,34 +96,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$deleteCount = 0; // Initialiser le compteur
|
$deleteCount = 0;
|
||||||
$errors = [];
|
|
||||||
|
|
||||||
foreach ($images as $image) {
|
foreach ($images as $image) {
|
||||||
$imagePath = $currentPath . '/' . basename($image);
|
$imagePath = $currentPath . '/' . basename($image);
|
||||||
if (isSecurePrivatePath($imagePath) && file_exists($imagePath)) { // Notez l'utilisation de isSecurePrivatePath
|
if (isSecurePrivatePath($imagePath) && file_exists($imagePath)) {
|
||||||
if (unlink($imagePath)) {
|
if (unlink($imagePath)) {
|
||||||
$deleteCount++; // Incrémenter le compteur en cas de succès
|
$deleteCount++;
|
||||||
} else {
|
|
||||||
$errors[] = "Erreur lors de la suppression de " . basename($image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loguer l'action une fois que toutes les suppressions sont terminées
|
|
||||||
if ($deleteCount > 0) {
|
if ($deleteCount > 0) {
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_PRIVATE_IMAGES', // Notez le changement ici pour les images privées
|
|
||||||
"Suppression de $deleteCount image(s) privée(s)", // Message adapté pour les images privées
|
|
||||||
$currentPath
|
|
||||||
);
|
|
||||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($errors)) {
|
|
||||||
$_SESSION['error_message'] = implode("\n", $errors);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,9 +170,6 @@ $config = getSiteConfig();
|
|||||||
<button onclick="deleteSelected()" id="deleteSelectedBtn" class="action-button action-button-danger">
|
<button onclick="deleteSelected()" id="deleteSelectedBtn" class="action-button action-button-danger">
|
||||||
Supprimer la sélection
|
Supprimer la sélection
|
||||||
</button>
|
</button>
|
||||||
<button onclick="toggleSelectAll()" id="selectAllBtn" class="action-button">
|
|
||||||
Tout sélectionner
|
|
||||||
</button>
|
|
||||||
<a href="arbre-prive.php?path=<?php echo urlencode($currentPath); ?>" class="action-button action-button-secondary">
|
<a href="arbre-prive.php?path=<?php echo urlencode($currentPath); ?>" class="action-button action-button-secondary">
|
||||||
Retour
|
Retour
|
||||||
</a>
|
</a>
|
||||||
@ -228,10 +200,7 @@ $config = getSiteConfig();
|
|||||||
<div class="images-grid">
|
<div class="images-grid">
|
||||||
<?php foreach($images as $image):
|
<?php foreach($images as $image):
|
||||||
$imagePath = str_replace('\\', '/', substr($currentPath, strpos($currentPath, '/liste_albums_prives/') + strlen('/liste_albums_prives/')));
|
$imagePath = str_replace('\\', '/', substr($currentPath, strpos($currentPath, '/liste_albums_prives/') + strlen('/liste_albums_prives/')));
|
||||||
$imageUrl = getBaseUrl() . '/images.php?path=' . urlencode($currentPath . '/' . $image);
|
$imageUrl = getBaseUrl() . '/liste_albums_prives/' . ($imagePath ? $imagePath . '/' : '') . $image;
|
||||||
if (isset($_SESSION['admin_id'])) {
|
|
||||||
$imageUrl .= '&admin_session=' . session_id();
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<div class="image-item">
|
<div class="image-item">
|
||||||
<input type="checkbox" name="images[]" value="<?php echo htmlspecialchars($image); ?>"
|
<input type="checkbox" name="images[]" value="<?php echo htmlspecialchars($image); ?>"
|
||||||
@ -322,40 +291,6 @@ $config = getSiteConfig();
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fonction pour basculer la sélection de toutes les images
|
|
||||||
function toggleSelectAll() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
|
||||||
const allChecked = document.querySelectorAll('.image-checkbox:checked').length === checkboxes.length;
|
|
||||||
|
|
||||||
checkboxes.forEach(checkbox => {
|
|
||||||
checkbox.checked = !allChecked;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateActionButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modifier la fonction updateActionButtons existante
|
|
||||||
function updateActionButtons() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
|
||||||
const selectedCheckboxes = document.querySelectorAll('.image-checkbox:checked');
|
|
||||||
const count = selectedCheckboxes.length;
|
|
||||||
|
|
||||||
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
|
||||||
const selectAllBtn = document.getElementById('selectAllBtn');
|
|
||||||
|
|
||||||
if (deleteBtn) {
|
|
||||||
deleteBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectAllBtn) {
|
|
||||||
selectAllBtn.textContent = checkboxes.length === selectedCheckboxes.length ?
|
|
||||||
'Tout désélectionner' : 'Tout sélectionner';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ajouter l'initialisation au chargement de la page
|
|
||||||
document.addEventListener('DOMContentLoaded', updateActionButtons);
|
|
||||||
</script>
|
</script>
|
||||||
<button class="scroll-top" title="Retour en haut">↑</button>
|
<button class="scroll-top" title="Retour en haut">↑</button>
|
||||||
<script>
|
<script>
|
||||||
@ -366,61 +301,6 @@ $config = getSiteConfig();
|
|||||||
scrollBtn.addEventListener('click', () => {
|
scrollBtn.addEventListener('click', () => {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fonction pour basculer la sélection de toutes les images
|
|
||||||
function toggleSelectAll() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
|
||||||
const allChecked = document.querySelectorAll('.image-checkbox:checked').length === checkboxes.length;
|
|
||||||
|
|
||||||
checkboxes.forEach(checkbox => {
|
|
||||||
checkbox.checked = !allChecked;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateActionButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fonction de suppression d'une seule image
|
|
||||||
function deleteImage(imageName) {
|
|
||||||
if (confirm('Êtes-vous sûr de vouloir supprimer cette image ?')) {
|
|
||||||
const form = document.getElementById('imagesForm');
|
|
||||||
form.innerHTML = `
|
|
||||||
<input type="hidden" name="action" value="delete">
|
|
||||||
<input type="hidden" name="images[]" value="${imageName}">
|
|
||||||
`;
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fonction de suppression multiple
|
|
||||||
function deleteSelected() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
|
||||||
if (checkboxes.length > 0 && confirm('Êtes-vous sûr de vouloir supprimer les images sélectionnées ?')) {
|
|
||||||
document.getElementById('formAction').value = 'delete';
|
|
||||||
document.getElementById('imagesForm').submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fonction de mise à jour de l'état des boutons d'action
|
|
||||||
function updateActionButtons() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
|
||||||
const selectedCheckboxes = document.querySelectorAll('.image-checkbox:checked');
|
|
||||||
const count = selectedCheckboxes.length;
|
|
||||||
|
|
||||||
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
|
||||||
const selectAllBtn = document.getElementById('selectAllBtn');
|
|
||||||
|
|
||||||
if (deleteBtn) {
|
|
||||||
deleteBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectAllBtn) {
|
|
||||||
selectAllBtn.textContent = checkboxes.length === selectedCheckboxes.length ?
|
|
||||||
'Tout désélectionner' : 'Tout sélectionner';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialisation des boutons au chargement
|
|
||||||
document.addEventListener('DOMContentLoaded', updateActionButtons);
|
|
||||||
</script>
|
</script>
|
||||||
<?php include 'footer.php'; ?>
|
<?php include 'footer.php'; ?>
|
||||||
</body>
|
</body>
|
||||||
|
@ -7,7 +7,6 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Récupérer le chemin courant
|
// Récupérer le chemin courant
|
||||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums';
|
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums';
|
||||||
@ -25,7 +24,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
switch ($_POST['action']) {
|
switch ($_POST['action']) {
|
||||||
case 'upload':
|
case 'upload':
|
||||||
$uploadedFiles = $_FILES['images'] ?? [];
|
$uploadedFiles = $_FILES['images'] ?? [];
|
||||||
$successCount = 0; // Initialiser le compteur
|
$successCount = 0;
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
// Gérer les uploads multiples
|
// Gérer les uploads multiples
|
||||||
@ -51,7 +50,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (move_uploaded_file($tmpName, $destination)) {
|
if (move_uploaded_file($tmpName, $destination)) {
|
||||||
$successCount++; // Incrémenter le compteur en cas de succès
|
$successCount++;
|
||||||
} else {
|
} else {
|
||||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||||
}
|
}
|
||||||
@ -61,16 +60,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loguer l'action une fois que tous les uploads sont terminés
|
|
||||||
if ($successCount > 0) {
|
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'UPLOAD_IMAGES',
|
|
||||||
"Téléversement de $successCount image(s)",
|
|
||||||
$currentPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($successCount > 0) {
|
if ($successCount > 0) {
|
||||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||||
}
|
}
|
||||||
@ -107,47 +96,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$deleteCount = 0; // Initialiser le compteur
|
$deleteCount = 0;
|
||||||
$errors = [];
|
|
||||||
|
|
||||||
foreach ($images as $image) {
|
foreach ($images as $image) {
|
||||||
$imagePath = $currentPath . '/' . basename($image);
|
$imagePath = $currentPath . '/' . basename($image);
|
||||||
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
||||||
if (unlink($imagePath)) {
|
if (unlink($imagePath)) {
|
||||||
$deleteCount++; // Incrémenter le compteur en cas de succès
|
$deleteCount++;
|
||||||
} else {
|
|
||||||
$errors[] = "Erreur lors de la suppression de " . basename($image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loguer l'action une fois que toutes les suppressions sont terminées
|
|
||||||
if ($deleteCount > 0) {
|
if ($deleteCount > 0) {
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_IMAGES',
|
|
||||||
"Suppression de $deleteCount image(s)",
|
|
||||||
$currentPath
|
|
||||||
);
|
|
||||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($errors)) {
|
|
||||||
$_SESSION['error_message'] = implode("\n", $errors);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'move':
|
case 'move':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$destinationPath = $_POST['destination_path'] ?? '';
|
$destinationPath = $_POST['destination_path'] ?? '';
|
||||||
if ($moveCount > 0) {
|
$moveCount = 0;
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'MOVE_IMAGES',
|
|
||||||
"Déplacement de $moveCount image(s) vers " . basename($_POST['destination_path']),
|
|
||||||
$currentPath . ' -> ' . $_POST['destination_path']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
// Vérifier que le dossier de destination existe et est valide
|
// Vérifier que le dossier de destination existe et est valide
|
||||||
@ -253,9 +221,6 @@ $config = getSiteConfig();
|
|||||||
<button onclick="moveSelected()" id="moveSelectedBtn" class="action-button action-button-warning">
|
<button onclick="moveSelected()" id="moveSelectedBtn" class="action-button action-button-warning">
|
||||||
Déplacer la sélection
|
Déplacer la sélection
|
||||||
</button>
|
</button>
|
||||||
<button onclick="toggleSelectAll()" id="selectAllBtn" class="action-button">
|
|
||||||
Tout sélectionner
|
|
||||||
</button>
|
|
||||||
<a href="arbre.php?path=<?php echo urlencode($currentPath); ?>" class="action-button action-button-secondary">
|
<a href="arbre.php?path=<?php echo urlencode($currentPath); ?>" class="action-button action-button-secondary">
|
||||||
Retour
|
Retour
|
||||||
</a>
|
</a>
|
||||||
@ -281,18 +246,6 @@ $config = getSiteConfig();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fonction pour basculer la sélection de toutes les images
|
|
||||||
function toggleSelectAll() {
|
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
|
||||||
const allChecked = document.querySelectorAll('.image-checkbox:checked').length === checkboxes.length;
|
|
||||||
|
|
||||||
checkboxes.forEach(checkbox => {
|
|
||||||
checkbox.checked = !allChecked;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateActionButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fonction de suppression multiple
|
// Fonction de suppression multiple
|
||||||
function deleteSelected() {
|
function deleteSelected() {
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||||
@ -479,22 +432,27 @@ $config = getSiteConfig();
|
|||||||
// Gestion des boutons d'action
|
// Gestion des boutons d'action
|
||||||
console.log("Définition de updateActionButtons");
|
console.log("Définition de updateActionButtons");
|
||||||
function updateActionButtons() {
|
function updateActionButtons() {
|
||||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
console.log("updateActionButtons appelé");
|
||||||
const selectedCheckboxes = document.querySelectorAll('.image-checkbox:checked');
|
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||||
const count = selectedCheckboxes.length;
|
const count = checkboxes.length;
|
||||||
|
console.log("Nombre d'images sélectionnées:", count);
|
||||||
|
|
||||||
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
||||||
const moveBtn = document.getElementById('moveSelectedBtn');
|
const moveBtn = document.getElementById('moveSelectedBtn');
|
||||||
const selectAllBtn = document.getElementById('selectAllBtn');
|
|
||||||
|
|
||||||
if (deleteBtn && moveBtn) {
|
if (!deleteBtn || !moveBtn) {
|
||||||
deleteBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
console.log("Boutons non trouvés");
|
||||||
moveBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectAllBtn) {
|
if (count > 0) {
|
||||||
selectAllBtn.textContent = checkboxes.length === selectedCheckboxes.length ?
|
deleteBtn.style.display = 'inline-flex';
|
||||||
'Tout désélectionner' : 'Tout sélectionner';
|
moveBtn.style.display = 'inline-flex';
|
||||||
|
console.log("Affichage des boutons");
|
||||||
|
} else {
|
||||||
|
deleteBtn.style.display = 'none';
|
||||||
|
moveBtn.style.display = 'none';
|
||||||
|
console.log("Masquage des boutons");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Gérer la génération de lien de partage
|
// Gérer la génération de lien de partage
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'generate_link') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'generate_link') {
|
||||||
@ -24,13 +23,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
|||||||
|
|
||||||
if ($shareKey) {
|
if ($shareKey) {
|
||||||
$shareUrl = getBaseUrl() . '/galeries-privees.php?key=' . urlencode($shareKey);
|
$shareUrl = getBaseUrl() . '/galeries-privees.php?key=' . urlencode($shareKey);
|
||||||
logAdminAction(
|
$_SESSION['success_message'] = "Lien de partage généré avec succès. URL : " . $shareUrl;
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'GENERATE_SHARE_LINK',
|
|
||||||
"Création d'un lien de partage valide " . $duration . " heures",
|
|
||||||
$albumPath
|
|
||||||
);
|
|
||||||
$_SESSION['success_message'] = "Lien de partage généré avec succès.";
|
|
||||||
$_SESSION['share_url'] = $shareUrl;
|
$_SESSION['share_url'] = $shareUrl;
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la génération du lien de partage.";
|
$_SESSION['error_message'] = "Erreur lors de la génération du lien de partage.";
|
||||||
@ -59,19 +52,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||||
if (!file_exists($newPath)) {
|
if (!file_exists($newPath)) {
|
||||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||||
mkdir($newPath, 0775, true);
|
mkdir($newPath, 0755, true);
|
||||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||||
$_SESSION['success_message'] = "Dossier privé créé avec succès.";
|
$_SESSION['success_message'] = "Dossier privé créé avec succès.";
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'CREATE_PRIVATE_FOLDER',
|
|
||||||
"Création du dossier privé : " . $newName,
|
|
||||||
$newPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -85,12 +72,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'EDIT_PRIVATE_FOLDER',
|
|
||||||
"Modification du dossier privé : " . $newName,
|
|
||||||
$path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -111,12 +92,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_PRIVATE_FOLDER',
|
|
||||||
"Suppression du dossier privé",
|
|
||||||
$path
|
|
||||||
);
|
|
||||||
rrmdir($path);
|
rrmdir($path);
|
||||||
$_SESSION['success_message'] = "Dossier privé supprimé avec succès.";
|
$_SESSION['success_message'] = "Dossier privé supprimé avec succès.";
|
||||||
}
|
}
|
||||||
@ -137,7 +112,7 @@ if (!isSecurePrivatePath($currentPath)) {
|
|||||||
|
|
||||||
// Créer le dossier racine s'il n'existe pas
|
// Créer le dossier racine s'il n'existe pas
|
||||||
if (!file_exists('./liste_albums_prives')) {
|
if (!file_exists('./liste_albums_prives')) {
|
||||||
mkdir('./liste_albums_prives', 0775, true);
|
mkdir('./liste_albums_prives', 0755, true);
|
||||||
// Créer le fichier infos.txt pour le dossier racine
|
// Créer le fichier infos.txt pour le dossier racine
|
||||||
$infoContent = "Albums privés\nVos albums photos privés\n18-\n";
|
$infoContent = "Albums privés\nVos albums photos privés\n18-\n";
|
||||||
file_put_contents('./liste_albums_prives/infos.txt', $infoContent);
|
file_put_contents('./liste_albums_prives/infos.txt', $infoContent);
|
||||||
@ -165,22 +140,11 @@ function generatePrivateTree($path, $currentPath) {
|
|||||||
$output .= '</div></div>';
|
$output .= '</div></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer et trier les sous-dossiers
|
// Parcourir tous les sous-dossiers
|
||||||
$dirs = array();
|
|
||||||
foreach (new DirectoryIterator($path) as $item) {
|
foreach (new DirectoryIterator($path) as $item) {
|
||||||
if ($item->isDot()) continue;
|
if ($item->isDot()) continue;
|
||||||
if ($item->isDir()) {
|
if ($item->isDir()) {
|
||||||
$fullPath = $item->getPathname();
|
$fullPath = $item->getPathname();
|
||||||
$info = getAlbumInfo($fullPath);
|
|
||||||
$dirs[$info['title']] = $fullPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tri alphabétique par titre
|
|
||||||
ksort($dirs, SORT_STRING | SORT_FLAG_CASE);
|
|
||||||
|
|
||||||
// Parcourir les dossiers triés
|
|
||||||
foreach ($dirs as $title => $fullPath) {
|
|
||||||
$info = getAlbumInfo($fullPath);
|
$info = getAlbumInfo($fullPath);
|
||||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||||
$hasSubfolders = hasSubfolders($fullPath);
|
$hasSubfolders = hasSubfolders($fullPath);
|
||||||
@ -197,10 +161,11 @@ function generatePrivateTree($path, $currentPath) {
|
|||||||
$output .= '<div class="tree-actions">';
|
$output .= '<div class="tree-actions">';
|
||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
|
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
|
||||||
|
// Ajout du bouton de génération de lien si le dossier contient des images
|
||||||
if ($hasImages) {
|
if ($hasImages) {
|
||||||
$encodedPath = htmlspecialchars(addslashes($fullPath));
|
$output .= '<button onclick="generateShareLink(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||||
$encodedTitle = htmlspecialchars(addslashes($info['title']));
|
. htmlspecialchars($info['title'])
|
||||||
$output .= '<button onclick="generateShareLink(\'' . $encodedPath . '\', \'' . $encodedTitle . '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
. '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
@ -228,6 +193,7 @@ function generatePrivateTree($path, $currentPath) {
|
|||||||
$output .= generatePrivateTree($fullPath, $currentPath);
|
$output .= generatePrivateTree($fullPath, $currentPath);
|
||||||
$output .= '</li>';
|
$output .= '</li>';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$output .= '</ul>';
|
$output .= '</ul>';
|
||||||
return $output;
|
return $output;
|
||||||
|
38
arbre.php
38
arbre.php
@ -6,7 +6,6 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$action = $_POST['action'] ?? '';
|
$action = $_POST['action'] ?? '';
|
||||||
@ -21,19 +20,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||||
if (!file_exists($newPath)) {
|
if (!file_exists($newPath)) {
|
||||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||||
mkdir($newPath, 0775, true);
|
mkdir($newPath, 0755, true);
|
||||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||||
$_SESSION['success_message'] = "Dossier créé avec succès.";
|
$_SESSION['success_message'] = "Dossier créé avec succès.";
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'CREATE_FOLDER',
|
|
||||||
"Création du dossier : " . $newName,
|
|
||||||
$newPath
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -47,12 +40,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'EDIT_FOLDER',
|
|
||||||
"Modification du dossier : " . $newName,
|
|
||||||
$path
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -73,12 +60,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_FOLDER',
|
|
||||||
"Suppression du dossier",
|
|
||||||
$path
|
|
||||||
);
|
|
||||||
rrmdir($path);
|
rrmdir($path);
|
||||||
$_SESSION['success_message'] = "Dossier supprimé avec succès.";
|
$_SESSION['success_message'] = "Dossier supprimé avec succès.";
|
||||||
}
|
}
|
||||||
@ -130,22 +111,11 @@ function generateTree($path, $currentPath) {
|
|||||||
$output .= '</div></div>';
|
$output .= '</div></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Récupérer et trier les sous-dossiers
|
// Parcourir tous les sous-dossiers
|
||||||
$dirs = array();
|
|
||||||
foreach (new DirectoryIterator($path) as $item) {
|
foreach (new DirectoryIterator($path) as $item) {
|
||||||
if ($item->isDot()) continue;
|
if ($item->isDot()) continue;
|
||||||
if ($item->isDir()) {
|
if ($item->isDir()) {
|
||||||
$fullPath = $item->getPathname();
|
$fullPath = $item->getPathname();
|
||||||
$info = getAlbumInfo($fullPath);
|
|
||||||
$dirs[$info['title']] = $fullPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tri alphabétique par titre
|
|
||||||
ksort($dirs, SORT_STRING | SORT_FLAG_CASE);
|
|
||||||
|
|
||||||
// Parcourir les dossiers triés
|
|
||||||
foreach ($dirs as $title => $fullPath) {
|
|
||||||
$info = getAlbumInfo($fullPath);
|
$info = getAlbumInfo($fullPath);
|
||||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||||
$hasSubfolders = hasSubfolders($fullPath);
|
$hasSubfolders = hasSubfolders($fullPath);
|
||||||
@ -162,6 +132,7 @@ function generateTree($path, $currentPath) {
|
|||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
$output .= '<a href="arbre-img.php?path=' . urlencode($fullPath) . '" class="tree-button" style="text-decoration: none">🖼️</a>';
|
$output .= '<a href="arbre-img.php?path=' . urlencode($fullPath) . '" class="tree-button" style="text-decoration: none">🖼️</a>';
|
||||||
}
|
}
|
||||||
|
// Pour les dossiers avec des images
|
||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||||
. rawurlencode($info['title']) . '\', \''
|
. rawurlencode($info['title']) . '\', \''
|
||||||
@ -171,6 +142,7 @@ function generateTree($path, $currentPath) {
|
|||||||
. (hasImages($fullPath) ? 'true' : 'false')
|
. (hasImages($fullPath) ? 'true' : 'false')
|
||||||
. ')" class="tree-button">✏️</button>';
|
. ')" class="tree-button">✏️</button>';
|
||||||
} else {
|
} else {
|
||||||
|
// Pour les dossiers sans images
|
||||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||||
. rawurlencode($info['title']) . '\', \''
|
. rawurlencode($info['title']) . '\', \''
|
||||||
. rawurlencode($info['description']) . '\', '
|
. rawurlencode($info['description']) . '\', '
|
||||||
@ -187,6 +159,7 @@ function generateTree($path, $currentPath) {
|
|||||||
$output .= generateTree($fullPath, $currentPath);
|
$output .= generateTree($fullPath, $currentPath);
|
||||||
$output .= '</li>';
|
$output .= '</li>';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$output .= '</ul>';
|
$output .= '</ul>';
|
||||||
return $output;
|
return $output;
|
||||||
@ -194,6 +167,7 @@ function generateTree($path, $currentPath) {
|
|||||||
|
|
||||||
$config = getSiteConfig();
|
$config = getSiteConfig();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
|
16
clefs.php
16
clefs.php
@ -2,7 +2,6 @@
|
|||||||
require_once 'fonctions.php';
|
require_once 'fonctions.php';
|
||||||
|
|
||||||
session_start();
|
session_start();
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Variables pour stocker les messages
|
// Variables pour stocker les messages
|
||||||
$successMessage = null;
|
$successMessage = null;
|
||||||
@ -34,12 +33,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$successMessage = "Clé supprimée avec succès.";
|
$successMessage = "Clé supprimée avec succès.";
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_SHARE_KEY',
|
|
||||||
"Suppression d'une clé de partage",
|
|
||||||
"ID: " . $keyId
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$errorMessage = "Erreur lors de la suppression de la clé.";
|
$errorMessage = "Erreur lors de la suppression de la clé.";
|
||||||
}
|
}
|
||||||
@ -50,11 +43,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$deletedCount = cleanExpiredShareKeys();
|
$deletedCount = cleanExpiredShareKeys();
|
||||||
if ($deletedCount > 0) {
|
if ($deletedCount > 0) {
|
||||||
$successMessage = "$deletedCount clé(s) expirée(s) supprimée(s).";
|
$successMessage = "$deletedCount clé(s) expirée(s) supprimée(s).";
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'CLEAN_EXPIRED_KEYS',
|
|
||||||
"Nettoyage de $deletedCount clé(s) de partage expirée(s)"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$successMessage = "Aucune clé expirée à supprimer.";
|
$successMessage = "Aucune clé expirée à supprimer.";
|
||||||
}
|
}
|
||||||
@ -133,7 +121,7 @@ $config = getSiteConfig();
|
|||||||
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label for="status-filter">Statut :</label>
|
<label for="status-filter">Statut :</label>
|
||||||
<select id="status-filter" class="form-select" onchange="updateFilters()">
|
<select id="status-filter" class="form-select" onchange="updateFilters()">
|
||||||
<option value="active" <?php echo $filter === 'active' ? 'selected' : ''; ?>>Clés actives</option>
|
<option value="active" <?php echo $filter === 'active' ? 'selected' : ''; ?>>Clés actives</option>
|
||||||
<option value="expired" <?php echo $filter === 'expired' ? 'selected' : ''; ?>>Clés expirées</option>
|
<option value="expired" <?php echo $filter === 'expired' ? 'selected' : ''; ?>>Clés expirées</option>
|
||||||
@ -142,7 +130,7 @@ $config = getSiteConfig();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label for="album-filter">Album :</label>
|
<label for="album-filter">Album :</label>
|
||||||
<select id="album-filter" class="form-select" onchange="updateFilters()">
|
<select id="album-filter" class="form-select" onchange="updateFilters()">
|
||||||
<option value="">Tous les albums</option>
|
<option value="">Tous les albums</option>
|
||||||
<?php foreach ($albums as $album): ?>
|
<?php foreach ($albums as $album): ?>
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
ICO
|
ICO
|
||||||
ICO est la galerie d'images de l'association Camélia Studio.
|
ICO est la galerie d'images de l'association Camélia Studio.
|
||||||
test-ico
|
|
BIN
favicon.png
BIN
favicon.png
Binary file not shown.
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 68 KiB |
171
fonctions.php
171
fonctions.php
@ -1,44 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
// Configuration
|
// Configuration
|
||||||
function getProjectRootDir() {
|
define('PROJECT_ROOT_DIR', 'test-ico');
|
||||||
$configFile = __DIR__ . '/config.txt';
|
|
||||||
if (file_exists($configFile)) {
|
|
||||||
$content = file_get_contents($configFile);
|
|
||||||
$lines = explode("\n", $content);
|
|
||||||
if (isset($lines[2])) {
|
|
||||||
$path = trim($lines[2]);
|
|
||||||
if (!empty($path)) {
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'test-ico'; // Valeur par défaut
|
|
||||||
}
|
|
||||||
|
|
||||||
define('PROJECT_ROOT_DIR', getProjectRootDir());
|
|
||||||
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif']);
|
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif']);
|
||||||
|
|
||||||
// Configuration de la durée de session
|
|
||||||
ini_set('session.gc_maxlifetime', 86400);
|
|
||||||
session_set_cookie_params(86400);
|
|
||||||
|
|
||||||
// Nouvelle fonction de vérification de session
|
|
||||||
function checkAdminSession() {
|
|
||||||
// Ne pas vérifier si on est déjà sur la page de login
|
|
||||||
if (basename($_SERVER['PHP_SELF']) === 'admin.php' && isset($_GET['action']) && $_GET['action'] === 'login') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$timeout = 86400;
|
|
||||||
if (!isset($_SESSION['admin_id']) ||
|
|
||||||
(isset($_SESSION['last_activity']) && time() - $_SESSION['last_activity'] > $timeout)) {
|
|
||||||
session_destroy();
|
|
||||||
header('Location: admin.php?action=login');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$_SESSION['last_activity'] = time();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtient l'URL de base du site
|
* Obtient l'URL de base du site
|
||||||
* @return string L'URL de base du site
|
* @return string L'URL de base du site
|
||||||
@ -139,22 +103,15 @@ function getImagesRecursively($albumPath, $limit = 4) {
|
|||||||
if ($file->isFile()) {
|
if ($file->isFile()) {
|
||||||
$extension = strtolower($file->getExtension());
|
$extension = strtolower($file->getExtension());
|
||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
// Récupérer les infos du dossier parent de l'image
|
|
||||||
$parentDir = dirname($file->getPathname());
|
|
||||||
$parentInfo = getAlbumInfo($parentDir);
|
|
||||||
|
|
||||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||||
$images[] = [
|
$images[] = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||||
'url' => $baseUrl . '/' . ltrim($relativePath, '/'),
|
|
||||||
'is_mature' => $parentInfo['mature_content']
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($images, function($a, $b) {
|
usort($images, function($a, $b) {
|
||||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a['url']);
|
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
||||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b['url']);
|
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
||||||
return filectime($pathB) - filectime($pathA);
|
return filectime($pathB) - filectime($pathA);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -206,7 +163,7 @@ function isSecurePath($path) {
|
|||||||
|
|
||||||
return $realPath && (
|
return $realPath && (
|
||||||
(strpos($realPath, $rootPath) === 0) ||
|
(strpos($realPath, $rootPath) === 0) ||
|
||||||
($carouselPath && strpos($realPath, $carouselPath) === 0)
|
($carouselPath && $realPath === $carouselPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,34 +343,6 @@ function cleanExpiredShareKeys() {
|
|||||||
return $db->changes();
|
return $db->changes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enregistre une action d'administrateur dans les logs
|
|
||||||
*/
|
|
||||||
function logAdminAction($adminId, $actionType, $description, $targetPath = null) {
|
|
||||||
$db = new SQLite3('database.sqlite');
|
|
||||||
$stmt = $db->prepare('INSERT INTO admin_logs (admin_id, action_type, action_description, target_path)
|
|
||||||
VALUES (:admin_id, :action_type, :description, :target_path)');
|
|
||||||
$stmt->bindValue(':admin_id', $adminId, SQLITE3_INTEGER);
|
|
||||||
$stmt->bindValue(':action_type', $actionType, SQLITE3_TEXT);
|
|
||||||
$stmt->bindValue(':description', $description, SQLITE3_TEXT);
|
|
||||||
$stmt->bindValue(':target_path', $targetPath, SQLITE3_TEXT);
|
|
||||||
return $stmt->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère le nom d'utilisateur d'un admin
|
|
||||||
*/
|
|
||||||
function getAdminUsername($adminId) {
|
|
||||||
$db = new SQLite3('database.sqlite');
|
|
||||||
$stmt = $db->prepare('SELECT username FROM admins WHERE id = :id');
|
|
||||||
$stmt->bindValue(':id', $adminId, SQLITE3_INTEGER);
|
|
||||||
$result = $stmt->execute();
|
|
||||||
if ($row = $result->fetchArray()) {
|
|
||||||
return $row['username'];
|
|
||||||
}
|
|
||||||
return 'Inconnu';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère la version actuelle du projet
|
* Récupère la version actuelle du projet
|
||||||
* @return string La version du projet
|
* @return string La version du projet
|
||||||
@ -430,8 +359,7 @@ function getSiteConfig() {
|
|||||||
$configFile = './config.txt';
|
$configFile = './config.txt';
|
||||||
$config = [
|
$config = [
|
||||||
'site_title' => 'ICO',
|
'site_title' => 'ICO',
|
||||||
'site_description' => 'ICO est la galerie d\'images de l\'association Camélia Studio.',
|
'site_description' => 'ICO est la galerie d\'images de l\'association Camélia Studio.'
|
||||||
'project_path' => PROJECT_ROOT_DIR
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (file_exists($configFile)) {
|
if (file_exists($configFile)) {
|
||||||
@ -439,95 +367,8 @@ function getSiteConfig() {
|
|||||||
$lines = explode("\n", $content);
|
$lines = explode("\n", $content);
|
||||||
if (isset($lines[0])) $config['site_title'] = trim($lines[0]);
|
if (isset($lines[0])) $config['site_title'] = trim($lines[0]);
|
||||||
if (isset($lines[1])) $config['site_description'] = trim($lines[1]);
|
if (isset($lines[1])) $config['site_description'] = trim($lines[1]);
|
||||||
if (isset($lines[2])) $config['project_path'] = trim($lines[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupère la dernière version disponible depuis Gitea
|
|
||||||
* @return array|false Tableau contenant ['version' => 'x.x.x', 'url' => 'url'] ou false en cas d'erreur
|
|
||||||
*/
|
|
||||||
function getLatestVersion() {
|
|
||||||
$ch = curl_init('https://git.crystalyx.net/api/v1/repos/camelia-studio/ICO/tags');
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_USERAGENT, 'ICO Gallery Update Checker');
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$error = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
if (!$response) {
|
|
||||||
error_log("Erreur lors de la vérification des mises à jour : " . $error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tags = json_decode($response, true);
|
|
||||||
if (!$tags || !is_array($tags)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trier les tags par date de création (le plus récent en premier)
|
|
||||||
usort($tags, function($a, $b) {
|
|
||||||
return strtotime($b['created_at']) - strtotime($a['created_at']);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (empty($tags)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer le dernier tag
|
|
||||||
$latestTag = $tags[0];
|
|
||||||
|
|
||||||
// Construction de l'URL directe vers le tag sur Gitea
|
|
||||||
$tagUrl = 'https://git.crystalyx.net/camelia-studio/ICO/releases/tag/' . $latestTag['name'];
|
|
||||||
|
|
||||||
return [
|
|
||||||
'version' => ltrim($latestTag['name'], 'v'), // Enlever le 'v' potentiel du numéro de version
|
|
||||||
'url' => $tagUrl // Utiliser l'URL construite manuellement
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare deux numéros de version
|
|
||||||
* @param string $version1 Premier numéro de version
|
|
||||||
* @param string $version2 Second numéro de version
|
|
||||||
* @return int Retourne 1 si version1 > version2, -1 si version1 < version2, 0 si égales
|
|
||||||
*/
|
|
||||||
function compareVersions($version1, $version2) {
|
|
||||||
$v1 = array_map('intval', explode('.', $version1));
|
|
||||||
$v2 = array_map('intval', explode('.', $version2));
|
|
||||||
|
|
||||||
for ($i = 0; $i < 3; $i++) {
|
|
||||||
$v1[$i] = $v1[$i] ?? 0;
|
|
||||||
$v2[$i] = $v2[$i] ?? 0;
|
|
||||||
|
|
||||||
if ($v1[$i] > $v2[$i]) return 1;
|
|
||||||
if ($v1[$i] < $v2[$i]) return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si une mise à jour est disponible
|
|
||||||
* @return array|false ['available' => bool, 'current' => string, 'latest' => string, 'url' => string] ou false
|
|
||||||
*/
|
|
||||||
function checkUpdate() {
|
|
||||||
$currentVersion = trim(file_get_contents(__DIR__ . '/version.txt'));
|
|
||||||
$latest = getLatestVersion();
|
|
||||||
|
|
||||||
if (!$latest) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'available' => compareVersions($latest['version'], $currentVersion) > 0,
|
|
||||||
'current' => $currentVersion,
|
|
||||||
'latest' => $latest['version'],
|
|
||||||
'url' => $latest['url']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
?>
|
?>
|
@ -34,7 +34,7 @@ if (empty($shareKey)) {
|
|||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
// Obtenir le chemin relatif depuis la racine du projet
|
// Obtenir le chemin relatif depuis la racine du projet
|
||||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||||
$url = $baseUrl . '/images.php?path=' . urlencode($file->getPathname()) . '&key=' . urlencode($shareKey);
|
$url = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||||
// Vérifier que le fichier existe et est accessible
|
// Vérifier que le fichier existe et est accessible
|
||||||
if (file_exists($file->getPathname())) {
|
if (file_exists($file->getPathname())) {
|
||||||
$images[] = $url;
|
$images[] = $url;
|
||||||
@ -148,7 +148,7 @@ $config = getSiteConfig();
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="gallery-item <?php echo $isTop ? 'gallery-item-top' : ''; ?> <?php echo $spanClass; ?>">
|
<div class="gallery-item <?php echo $isTop ? 'gallery-item-top' : ''; ?> <?php echo $spanClass; ?>">
|
||||||
<a href="partage.php?image=<?php echo urlencode($image); ?>&key=<?php echo urlencode($shareKey); ?>" target="_blank">
|
<a href="partage.php?image=<?php echo urlencode($image); ?>" target="_blank">
|
||||||
<img src="<?php echo htmlspecialchars($image); ?>"
|
<img src="<?php echo htmlspecialchars($image); ?>"
|
||||||
alt="Image de la galerie"
|
alt="Image de la galerie"
|
||||||
loading="lazy">
|
loading="lazy">
|
||||||
|
@ -133,7 +133,6 @@ $config = getSiteConfig();
|
|||||||
<script>
|
<script>
|
||||||
// Gestion du contenu mature
|
// Gestion du contenu mature
|
||||||
function acceptMatureContent() {
|
function acceptMatureContent() {
|
||||||
document.body.classList.remove('gallery-page-mature');
|
|
||||||
document.body.classList.remove('content-blurred');
|
document.body.classList.remove('content-blurred');
|
||||||
const warning = document.getElementById('mature-warning');
|
const warning = document.getElementById('mature-warning');
|
||||||
if (warning) {
|
if (warning) {
|
||||||
|
34
images.php
34
images.php
@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
// images.php
|
|
||||||
require_once 'fonctions.php';
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
$path = $_GET['path'] ?? '';
|
|
||||||
$key = $_GET['key'] ?? '';
|
|
||||||
$adminSession = $_GET['admin_session'] ?? '';
|
|
||||||
|
|
||||||
// Vérifier que le chemin est valide et dans un album privé
|
|
||||||
if (!isSecurePrivatePath($path) || !file_exists($path)) {
|
|
||||||
header("HTTP/1.0 404 Not Found");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier l'authentification (admin ou clé de partage valide)
|
|
||||||
if ($adminSession) {
|
|
||||||
session_id($adminSession);
|
|
||||||
session_start();
|
|
||||||
if (!isset($_SESSION['admin_id'])) {
|
|
||||||
header("HTTP/1.0 403 Forbidden");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!$key || !validateShareKey($key)) {
|
|
||||||
header("HTTP/1.0 403 Forbidden");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Servir l'image avec le bon Content-Type
|
|
||||||
$mime = mime_content_type($path);
|
|
||||||
header("Content-Type: $mime");
|
|
||||||
readfile($path);
|
|
@ -8,7 +8,7 @@ function getCarouselImages($limit = 5) {
|
|||||||
// Vérifier si le dossier existe
|
// Vérifier si le dossier existe
|
||||||
if (!is_dir($carouselDir)) {
|
if (!is_dir($carouselDir)) {
|
||||||
// Créer le dossier s'il n'existe pas
|
// Créer le dossier s'il n'existe pas
|
||||||
mkdir($carouselDir, 0775, true);
|
mkdir($carouselDir, 0755, true);
|
||||||
return $images;
|
return $images;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,6 @@ $config = getSiteConfig();
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="admin.php" class="admin-button" title="Administration">⚙️</a>
|
|
||||||
<div class="carousel">
|
<div class="carousel">
|
||||||
<?php foreach($carouselImages as $index => $image): ?>
|
<?php foreach($carouselImages as $index => $image): ?>
|
||||||
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
||||||
|
13
init-db.php
13
init-db.php
@ -47,23 +47,10 @@ if ($count === 0) {
|
|||||||
echo "Admin par défaut créé (username: admin, password: admin). Pensez à changer ces identifiants !";
|
echo "Admin par défaut créé (username: admin, password: admin). Pensez à changer ces identifiants !";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Après la création des tables existantes, ajouter :
|
|
||||||
$db->exec('CREATE TABLE IF NOT EXISTS admin_logs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
admin_id INTEGER NOT NULL,
|
|
||||||
action_type TEXT NOT NULL,
|
|
||||||
action_description TEXT NOT NULL,
|
|
||||||
target_path TEXT,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (admin_id) REFERENCES admins(id)
|
|
||||||
)');
|
|
||||||
|
|
||||||
// Créer les index nécessaires
|
// Créer les index nécessaires
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_expires_at ON share_keys(expires_at)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_expires_at ON share_keys(expires_at)');
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_album_identifier ON share_keys(album_identifier)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_album_identifier ON share_keys(album_identifier)');
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_album_identifiers_identifier ON album_identifiers(identifier)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_album_identifiers_identifier ON album_identifiers(identifier)');
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_admin_logs_admin_id ON admin_logs(admin_id)');
|
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_admin_logs_created_at ON admin_logs(created_at)');
|
|
||||||
|
|
||||||
$db->close();
|
$db->close();
|
||||||
echo "Base de données initialisée avec succès !";
|
echo "Base de données initialisée avec succès !";
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
Liste des albums
|
Titre
|
||||||
Voici tous les albums de premier niveau.
|
Description sur la seconde ligne.
|
@ -1,2 +0,0 @@
|
|||||||
Albums privés
|
|
||||||
Liste des albums privés de premier niveau.
|
|
276
logs.php
276
logs.php
@ -1,276 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once 'fonctions.php';
|
|
||||||
|
|
||||||
session_start();
|
|
||||||
if (!isset($_SESSION['admin_id'])) {
|
|
||||||
header('Location: admin.php?action=login');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vérifier que c'est bien le premier administrateur
|
|
||||||
$db = new SQLite3('database.sqlite');
|
|
||||||
$stmt = $db->prepare('SELECT MIN(id) as first_id FROM admins');
|
|
||||||
$result = $stmt->execute();
|
|
||||||
$firstId = $result->fetchArray()['first_id'];
|
|
||||||
|
|
||||||
if ($_SESSION['admin_id'] != $firstId) {
|
|
||||||
$_SESSION['error_message'] = "Accès non autorisé. Seul le premier administrateur peut consulter les logs.";
|
|
||||||
header('Location: admin.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supprimer les logs de plus d'un mois
|
|
||||||
$db->exec('DELETE FROM admin_logs WHERE created_at < datetime("now", "-1 month")');
|
|
||||||
|
|
||||||
// Tableau de traduction des actions
|
|
||||||
$actionTranslations = [
|
|
||||||
'ADD_USER' => 'Ajouter un utilisateur',
|
|
||||||
'EDIT_USER' => 'Modifier un utilisateur',
|
|
||||||
'DELETE_USER' => 'Supprimer un utilisateur',
|
|
||||||
'CREATE_FOLDER' => 'Créer un dossier',
|
|
||||||
'EDIT_FOLDER' => 'Modifier un dossier',
|
|
||||||
'DELETE_FOLDER' => 'Supprimer un dossier',
|
|
||||||
'CREATE_PRIVATE_FOLDER' => 'Créer un dossier privé',
|
|
||||||
'EDIT_PRIVATE_FOLDER' => 'Modifier un dossier privé',
|
|
||||||
'DELETE_PRIVATE_FOLDER' => 'Supprimer un dossier privé',
|
|
||||||
'UPLOAD_IMAGES' => 'Téléverser des images',
|
|
||||||
'DELETE_IMAGES' => 'Supprimer des images',
|
|
||||||
'MOVE_IMAGES' => 'Déplacer des images',
|
|
||||||
'UPLOAD_PRIVATE_IMAGES' => 'Téléverser des images privées',
|
|
||||||
'DELETE_PRIVATE_IMAGES' => 'Supprimer des images privées',
|
|
||||||
'GENERATE_SHARE_LINK' => 'Générer un lien de partage',
|
|
||||||
'CLEAN_EXPIRED_KEYS' => 'Nettoyer les clés expirées',
|
|
||||||
'DELETE_SHARE_KEY' => 'Supprimer une clé de partage',
|
|
||||||
'UPDATE_SETTINGS' => 'Modifier les paramètres'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
|
|
||||||
$perPage = 50;
|
|
||||||
$offset = ($page - 1) * $perPage;
|
|
||||||
|
|
||||||
// Filtres
|
|
||||||
$actionType = isset($_GET['action_type']) ? $_GET['action_type'] : '';
|
|
||||||
$adminFilter = isset($_GET['admin']) ? intval($_GET['admin']) : 0;
|
|
||||||
$dateRange = isset($_GET['date_range']) ? $_GET['date_range'] : '';
|
|
||||||
|
|
||||||
// Construction de la requête
|
|
||||||
$whereClause = [];
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
if ($actionType) {
|
|
||||||
$whereClause[] = 'action_type = :action_type';
|
|
||||||
$params[':action_type'] = $actionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($adminFilter) {
|
|
||||||
$whereClause[] = 'admin_id = :admin_id';
|
|
||||||
$params[':admin_id'] = $adminFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($dateRange) {
|
|
||||||
switch ($dateRange) {
|
|
||||||
case '24h':
|
|
||||||
$whereClause[] = 'created_at >= datetime("now", "-1 day")';
|
|
||||||
break;
|
|
||||||
case '48h':
|
|
||||||
$whereClause[] = 'created_at >= datetime("now", "-2 days")';
|
|
||||||
break;
|
|
||||||
case '72h':
|
|
||||||
$whereClause[] = 'created_at >= datetime("now", "-3 days")';
|
|
||||||
break;
|
|
||||||
case '1week':
|
|
||||||
$whereClause[] = 'created_at >= datetime("now", "-7 days")';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$whereSQL = !empty($whereClause) ? 'WHERE ' . implode(' AND ', $whereClause) : '';
|
|
||||||
|
|
||||||
// Récupérer le nombre total de logs
|
|
||||||
$countQuery = "SELECT COUNT(*) as total FROM admin_logs $whereSQL";
|
|
||||||
$stmt = $db->prepare($countQuery);
|
|
||||||
foreach ($params as $key => $value) {
|
|
||||||
$stmt->bindValue($key, $value);
|
|
||||||
}
|
|
||||||
$total = $stmt->execute()->fetchArray()['total'];
|
|
||||||
$totalPages = ceil($total / $perPage);
|
|
||||||
|
|
||||||
// Récupérer les logs
|
|
||||||
$query = "SELECT l.*, a.username
|
|
||||||
FROM admin_logs l
|
|
||||||
LEFT JOIN admins a ON l.admin_id = a.id
|
|
||||||
$whereSQL
|
|
||||||
ORDER BY l.created_at DESC
|
|
||||||
LIMIT :limit OFFSET :offset";
|
|
||||||
|
|
||||||
$stmt = $db->prepare($query);
|
|
||||||
$stmt->bindValue(':limit', $perPage, SQLITE3_INTEGER);
|
|
||||||
$stmt->bindValue(':offset', $offset, SQLITE3_INTEGER);
|
|
||||||
foreach ($params as $key => $value) {
|
|
||||||
$stmt->bindValue($key, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
$logs = [];
|
|
||||||
$result = $stmt->execute();
|
|
||||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
|
||||||
$logs[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLogActionClass($actionType) {
|
|
||||||
if (strpos(strtolower($actionType), 'create') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'add') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'upload') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'generate') !== false) {
|
|
||||||
return 'log-action-create';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos(strtolower($actionType), 'edit') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'update') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'modify') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'move') !== false) {
|
|
||||||
return 'log-action-edit';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos(strtolower($actionType), 'delete') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'remove') !== false ||
|
|
||||||
strpos(strtolower($actionType), 'clean') !== false) {
|
|
||||||
return 'log-action-delete';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer la liste des admins pour le filtre
|
|
||||||
$admins = [];
|
|
||||||
$result = $db->query('SELECT id, username FROM admins ORDER BY username');
|
|
||||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
|
||||||
$admins[] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer les types d'actions uniques pour le filtre
|
|
||||||
$actionTypes = [];
|
|
||||||
$result = $db->query('SELECT DISTINCT action_type FROM admin_logs ORDER BY action_type');
|
|
||||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
|
||||||
$actionTypes[] = $row['action_type'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = getSiteConfig();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="fr">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Logs administrateurs - <?php echo htmlspecialchars($config['site_title']); ?></title>
|
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
|
||||||
<link rel="stylesheet" href="styles.css">
|
|
||||||
<link rel="stylesheet" href="styles-admin.css">
|
|
||||||
</head>
|
|
||||||
<body class="admin-page">
|
|
||||||
<div class="admin-header">
|
|
||||||
<h1>Logs administrateurs</h1>
|
|
||||||
<div class="admin-actions">
|
|
||||||
<a href="admin.php" class="action-button action-button-secondary">Retour</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="admin-content">
|
|
||||||
<!-- Filtres -->
|
|
||||||
<div class="filters">
|
|
||||||
<form method="get" class="filter-form">
|
|
||||||
<div class="filter-group">
|
|
||||||
<label for="action_type">Type d'action :</label>
|
|
||||||
<select name="action_type" id="action_type" class="form-select">
|
|
||||||
<option value="">Toutes les actions</option>
|
|
||||||
<?php foreach($actionTypes as $type): ?>
|
|
||||||
<option value="<?php echo htmlspecialchars($type); ?>"
|
|
||||||
<?php echo $actionType === $type ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($actionTranslations[$type] ?? $type); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filter-group">
|
|
||||||
<label for="admin">Administrateur :</label>
|
|
||||||
<select name="admin" id="admin" class="form-select">
|
|
||||||
<option value="">Tous les administrateurs</option>
|
|
||||||
<?php foreach($admins as $admin): ?>
|
|
||||||
<option value="<?php echo $admin['id']; ?>"
|
|
||||||
<?php echo $adminFilter === $admin['id'] ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($admin['username']); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filter-group">
|
|
||||||
<label for="date_range">Période :</label>
|
|
||||||
<select name="date_range" id="date_range" class="form-select">
|
|
||||||
<option value="">Toutes les dates</option>
|
|
||||||
<option value="24h" <?php echo $dateRange === '24h' ? 'selected' : ''; ?>>Dernières 24h</option>
|
|
||||||
<option value="48h" <?php echo $dateRange === '48h' ? 'selected' : ''; ?>>Dernières 48h</option>
|
|
||||||
<option value="72h" <?php echo $dateRange === '72h' ? 'selected' : ''; ?>>Dernières 72h</option>
|
|
||||||
<option value="1week" <?php echo $dateRange === '1week' ? 'selected' : ''; ?>>Dernière semaine</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="action-button">Filtrer</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tableau des logs -->
|
|
||||||
<div class="logs-list">
|
|
||||||
<table class="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Date</th>
|
|
||||||
<th>Administrateur</th>
|
|
||||||
<th>Action</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>Chemin</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach($logs as $log):
|
|
||||||
$actionClass = getLogActionClass($log['action_type']);
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php echo date('d/m/Y H:i:s', strtotime($log['created_at'])); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($log['username']); ?></td>
|
|
||||||
<td class="<?php echo $actionClass; ?>"><?php echo htmlspecialchars($actionTranslations[$log['action_type']] ?? $log['action_type']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($log['action_description']); ?></td>
|
|
||||||
<td><?php echo htmlspecialchars($log['target_path'] ?? ''); ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination -->
|
|
||||||
<?php if ($totalPages > 1): ?>
|
|
||||||
<div class="pagination">
|
|
||||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
|
||||||
<a href="?page=<?php echo $i; ?>&action_type=<?php echo urlencode($actionType); ?>&admin=<?php echo $adminFilter; ?>&date_range=<?php echo urlencode($dateRange); ?>"
|
|
||||||
class="pagination-link <?php echo $page === $i ? 'active' : ''; ?>">
|
|
||||||
<?php echo $i; ?>
|
|
||||||
</a>
|
|
||||||
<?php endfor; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<button class="scroll-top" title="Retour en haut">↑</button>
|
|
||||||
<script>
|
|
||||||
const scrollBtn = document.querySelector('.scroll-top');
|
|
||||||
window.addEventListener('scroll', () => {
|
|
||||||
scrollBtn.style.display = window.scrollY > 500 ? 'flex' : 'none';
|
|
||||||
});
|
|
||||||
scrollBtn.addEventListener('click', () => {
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<?php include 'footer.php'; ?>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
45
partage.php
45
partage.php
@ -4,35 +4,6 @@ require_once 'fonctions.php';
|
|||||||
// Vérifier que nous avons une URL d'image
|
// Vérifier que nous avons une URL d'image
|
||||||
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
||||||
|
|
||||||
if (!$imageUrl) {
|
|
||||||
header('Location: index.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si c'est une image privée
|
|
||||||
if (strpos($imageUrl, 'images.php') !== false) {
|
|
||||||
// On récupère les paramètres de l'URL de l'image
|
|
||||||
parse_str(parse_url($imageUrl, PHP_URL_QUERY), $params);
|
|
||||||
$path = $params['path'] ?? '';
|
|
||||||
$key = $params['key'] ?? '';
|
|
||||||
|
|
||||||
if (strpos($path, 'liste_albums_prives') !== false) {
|
|
||||||
$isPrivateImage = true;
|
|
||||||
if (!isset($_SESSION['admin_id'])) {
|
|
||||||
if (!$key || !validateShareKey($key)) {
|
|
||||||
header('Location: index.php');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} elseif (isset($_SESSION['admin_id'])) {
|
|
||||||
// Pour les admins, on remplace la clé par la session admin
|
|
||||||
$imageUrl = preg_replace('/&key=[^&]*/', '', $imageUrl) . '&admin_session=' . session_id();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer le nom du fichier pour le téléchargement
|
|
||||||
$filename = basename(parse_url($imageUrl, PHP_URL_PATH));
|
|
||||||
|
|
||||||
// Si pas d'image, redirection
|
// Si pas d'image, redirection
|
||||||
if (!$imageUrl) {
|
if (!$imageUrl) {
|
||||||
header('Location: index.php');
|
header('Location: index.php');
|
||||||
@ -54,7 +25,7 @@ $config = getSiteConfig();
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="share-page">
|
<body class="share-page">
|
||||||
<button onclick="var referrer = document.referrer; if (referrer.includes('galeries.php') || referrer.includes('galeries-privees.php')) { window.close(); } else { window.location.href='index.php'; }" class="back-button">Retour</button>
|
<button onclick="window.close();" class="back-button">Retour à la galerie</button>
|
||||||
|
|
||||||
<div class="share-container">
|
<div class="share-container">
|
||||||
<div class="share-image">
|
<div class="share-image">
|
||||||
@ -71,7 +42,6 @@ $config = getSiteConfig();
|
|||||||
Partager
|
Partager
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<?php if (!$isPrivateImage): ?>
|
|
||||||
<button class="action-button" onclick="embedImage()">
|
<button class="action-button" onclick="embedImage()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<polyline points="16 18 22 12 16 6"></polyline>
|
<polyline points="16 18 22 12 16 6"></polyline>
|
||||||
@ -79,7 +49,6 @@ $config = getSiteConfig();
|
|||||||
</svg>
|
</svg>
|
||||||
Intégrer
|
Intégrer
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
||||||
download="<?php echo htmlspecialchars($filename); ?>"
|
download="<?php echo htmlspecialchars($filename); ?>"
|
||||||
@ -91,18 +60,6 @@ $config = getSiteConfig();
|
|||||||
</svg>
|
</svg>
|
||||||
Télécharger
|
Télécharger
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="https://saucenao.com/search.php?url=<?php echo urlencode($imageUrl); ?>"
|
|
||||||
target="_blank"
|
|
||||||
class="action-button">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<circle cx="11" cy="11" r="8"></circle>
|
|
||||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
||||||
<line x1="11" y1="8" x2="11" y2="14"></line>
|
|
||||||
<line x1="8" y1="11" x2="14" y2="11"></line>
|
|
||||||
</svg>
|
|
||||||
Source ?
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -6,28 +6,21 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Gérer les soumissions du formulaire
|
// Gérer les soumissions du formulaire
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$siteTitle = $_POST['site_title'] ?? '';
|
$siteTitle = $_POST['site_title'] ?? '';
|
||||||
$siteDescription = $_POST['site_description'] ?? '';
|
$siteDescription = $_POST['site_description'] ?? '';
|
||||||
$projectPath = $_POST['project_path'] ?? '';
|
|
||||||
|
|
||||||
// Vérifications basiques
|
// Vérifications basiques
|
||||||
if (empty($siteTitle)) {
|
if (empty($siteTitle)) {
|
||||||
$_SESSION['error_message'] = "Le titre du site est requis.";
|
$_SESSION['error_message'] = "Le titre du site est requis.";
|
||||||
} else {
|
} else {
|
||||||
// Sauvegarder la configuration
|
// Sauvegarder la configuration
|
||||||
$configContent = $siteTitle . "\n" . $siteDescription . "\n" . $projectPath;
|
$configContent = $siteTitle . "\n" . $siteDescription;
|
||||||
|
|
||||||
if (file_put_contents('./config.txt', $configContent) !== false) {
|
if (file_put_contents('./config.txt', $configContent) !== false) {
|
||||||
$_SESSION['success_message'] = "Configuration mise à jour avec succès.";
|
$_SESSION['success_message'] = "Configuration mise à jour avec succès.";
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'UPDATE_SETTINGS',
|
|
||||||
"Modification des paramètres du site"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la sauvegarde de la configuration.";
|
$_SESSION['error_message'] = "Erreur lors de la sauvegarde de la configuration.";
|
||||||
}
|
}
|
||||||
@ -77,19 +70,13 @@ $config = getSiteConfig();
|
|||||||
value="<?php echo htmlspecialchars($config['site_title']); ?>">
|
value="<?php echo htmlspecialchars($config['site_title']); ?>">
|
||||||
<small class="form-help">Ce titre apparaîtra dans l'en-tête des pages et la barre de titre du navigateur.</small>
|
<small class="form-help">Ce titre apparaîtra dans l'en-tête des pages et la barre de titre du navigateur.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="site_description">Description du site :</label>
|
<label for="site_description">Description du site :</label>
|
||||||
<textarea id="site_description" name="site_description" rows="4"
|
<textarea id="site_description" name="site_description" rows="4"
|
||||||
class="form-textarea"><?php echo htmlspecialchars($config['site_description']); ?></textarea>
|
class="form-textarea"><?php echo htmlspecialchars($config['site_description']); ?></textarea>
|
||||||
<small class="form-help">Cette description apparaît sur la page d'accueil du site.</small>
|
<small class="form-help">Cette description apparaît sur la page d'accueil du site.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<label for="project_path">Chemin d'installation :</label>
|
|
||||||
<input type="text" id="project_path" name="project_path"
|
|
||||||
value="<?php echo htmlspecialchars($config['project_path']); ?>" required>
|
|
||||||
<small class="form-help">Ce chemin correspond au dossier dans lequel ICO est installé sur votre serveur web.
|
|
||||||
Par exemple, si ICO est accessible via "www.monsite.com/ico", le chemin sera "ico".</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="action-button">Enregistrer les modifications</button>
|
<button type="submit" class="action-button">Enregistrer les modifications</button>
|
||||||
|
132
styles-admin.css
132
styles-admin.css
@ -407,86 +407,6 @@ body {
|
|||||||
background-color: #333;
|
background-color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles spécifiques pour l'onglet de mise à jour */
|
|
||||||
.admin-menu-item.update-available {
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item.update-available::after {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
top: -8px;
|
|
||||||
right: -8px;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
background-color: #ff8c00;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid #1e1e1e;
|
|
||||||
animation: pulse 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item.disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item.disabled .menu-icon svg {
|
|
||||||
stroke: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item.disabled:hover {
|
|
||||||
transform: none;
|
|
||||||
border-color: #333;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles pour le contenu spécifique à la mise à jour */
|
|
||||||
.admin-menu-item .update-status {
|
|
||||||
margin-top: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
background-color: rgba(255, 140, 0, 0.1);
|
|
||||||
border: 1px solid rgba(255, 140, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item .update-status.no-update {
|
|
||||||
background-color: rgba(40, 167, 69, 0.1);
|
|
||||||
border-color: rgba(40, 167, 69, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item .update-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-menu-item .version-label {
|
|
||||||
background-color: rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 0.25rem 0.75rem;
|
|
||||||
border-radius: 1rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes pulse {
|
|
||||||
0% {
|
|
||||||
transform: scale(1);
|
|
||||||
box-shadow: 0 0 0 0 rgba(255, 140, 0, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
70% {
|
|
||||||
transform: scale(1.1);
|
|
||||||
box-shadow: 0 0 0 10px rgba(255, 140, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: scale(1);
|
|
||||||
box-shadow: 0 0 0 0 rgba(255, 140, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Messages */
|
/* Messages */
|
||||||
.message {
|
.message {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@ -1019,58 +939,6 @@ body[data-page="carrousel"] .admin-header {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles pour les logs */
|
|
||||||
.filter-form {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
margin-top: 2rem;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-link {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
background-color: #2a2a2a;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 0.25rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-link:hover {
|
|
||||||
background-color: #3a3a3a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination-link.active {
|
|
||||||
background-color: #2196f3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logs-list {
|
|
||||||
margin-top: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles pour les différents types d'actions */
|
|
||||||
.admin-table tr td:nth-child(3) {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-table tr .log-action-create {
|
|
||||||
color: #4CAF50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-table tr .log-action-edit {
|
|
||||||
color: #FFA726;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-table tr .log-action-delete {
|
|
||||||
color: #EF5350;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Media Queries */
|
/* Media Queries */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.admin-page {
|
.admin-page {
|
||||||
|
118
styles.css
118
styles.css
@ -15,32 +15,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Styles de la page d'accueil */
|
/* Styles de la page d'accueil */
|
||||||
.admin-button {
|
|
||||||
position: fixed;
|
|
||||||
top: 1rem;
|
|
||||||
right: 1rem;
|
|
||||||
width: 2.5rem;
|
|
||||||
height: 2.5rem;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
border: none;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: white;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
cursor: pointer;
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
z-index: 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-button:hover {
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel {
|
.carousel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -186,12 +160,9 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Styles pour le contenu mature dans les albums */
|
/* Styles pour le contenu mature dans les albums */
|
||||||
.album-card-mature {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-card-mature .album-images {
|
.album-card-mature .album-images {
|
||||||
filter: blur(10px);
|
filter: blur(10px);
|
||||||
|
transition: filter 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card-mature::before {
|
.album-card-mature::before {
|
||||||
@ -206,12 +177,8 @@ body {
|
|||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: transform 0.3s ease, background-color 0.3s ease;
|
opacity: 1;
|
||||||
}
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
.album-card-mature:hover::before {
|
|
||||||
transform: translate(-50%, -50%) scale(1.1);
|
|
||||||
background-color: rgba(220, 53, 69, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card-mature::after {
|
.album-card-mature::after {
|
||||||
@ -223,34 +190,12 @@ body {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-background {
|
.album-card-mature:hover .album-images {
|
||||||
position: absolute;
|
filter: blur(0);
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-background.mature-preview {
|
.album-card-mature:hover::before {
|
||||||
filter: blur(10px);
|
opacity: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.mature-preview-indicator {
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
right: 5px;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 10;
|
|
||||||
font-size: 14px;
|
|
||||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card {
|
.album-card {
|
||||||
@ -286,7 +231,6 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.album-image {
|
.album-image {
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
@ -316,6 +260,7 @@ body {
|
|||||||
.album-card .album-info h2 {
|
.album-card .album-info h2 {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles pour la page galerie */
|
/* Styles pour la page galerie */
|
||||||
@ -750,7 +695,7 @@ body {
|
|||||||
|
|
||||||
@media (max-width: 1600px) {
|
@media (max-width: 1600px) {
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
width: 100%;
|
width: calc((100% - (5 * 20px)) / 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +719,7 @@ body {
|
|||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
width: 100%;
|
width: calc((100% - (3 * 20px)) / 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,48 +734,13 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
min-height: unset;
|
width: calc((100% - (2 * 20px)) / 3);
|
||||||
width: 100%;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item img {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-grid {
|
.gallery-grid {
|
||||||
display: flex;
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||||
flex-direction: column;
|
gap: 0.5rem;
|
||||||
align-items: center;
|
|
||||||
gap: 1.5rem;
|
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item-wide,
|
|
||||||
.gallery-item-tall {
|
|
||||||
grid-column: 1;
|
|
||||||
grid-row: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item-top {
|
|
||||||
border: none;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gallery-item-top::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
border: 4px solid #2196f3;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-header {
|
.gallery-header {
|
||||||
@ -905,7 +815,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gallery-item {
|
.gallery-item {
|
||||||
width: 100%;
|
width: calc((100% - 20px) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.gallery-grid {
|
.gallery-grid {
|
||||||
|
@ -6,7 +6,6 @@ if (!isset($_SESSION['admin_id'])) {
|
|||||||
header('Location: admin.php?action=login');
|
header('Location: admin.php?action=login');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
checkAdminSession();
|
|
||||||
|
|
||||||
// Vérifier que c'est bien le premier administrateur
|
// Vérifier que c'est bien le premier administrateur
|
||||||
$db = new SQLite3('database.sqlite');
|
$db = new SQLite3('database.sqlite');
|
||||||
@ -83,11 +82,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$_SESSION['success_message'] = "Utilisateur ajouté avec succès.";
|
$_SESSION['success_message'] = "Utilisateur ajouté avec succès.";
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'ADD_USER',
|
|
||||||
"Création du compte administrateur : " . $username
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de l'ajout de l'utilisateur.";
|
$_SESSION['error_message'] = "Erreur lors de l'ajout de l'utilisateur.";
|
||||||
}
|
}
|
||||||
@ -103,17 +97,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vérifier que le nouvel identifiant n'existe pas déjà (sauf pour l'utilisateur actuel)
|
|
||||||
$stmt = $db->prepare('SELECT COUNT(*) as count FROM admins WHERE username = :username AND id != :id');
|
|
||||||
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
|
|
||||||
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
|
|
||||||
$result = $stmt->execute()->fetchArray();
|
|
||||||
|
|
||||||
if ($result['count'] > 0) {
|
|
||||||
$_SESSION['error_message'] = "Cet identifiant existe déjà.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si un nouveau mot de passe est fourni
|
// Si un nouveau mot de passe est fourni
|
||||||
if (!empty($password)) {
|
if (!empty($password)) {
|
||||||
// Vérification du mot de passe
|
// Vérification du mot de passe
|
||||||
@ -141,30 +124,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un caractère spécial.";
|
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un caractère spécial.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construction de la requête avec nouveau mot de passe
|
|
||||||
$stmt = $db->prepare('UPDATE admins SET username = :username, password_hash = :password_hash WHERE id = :id');
|
|
||||||
$stmt->bindValue(':password_hash', password_hash($password, PASSWORD_DEFAULT), SQLITE3_TEXT);
|
|
||||||
} else {
|
|
||||||
// Construction de la requête sans nouveau mot de passe
|
|
||||||
$stmt = $db->prepare('UPDATE admins SET username = :username WHERE id = :id');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->bindValue(':username', $username, SQLITE3_TEXT);
|
|
||||||
$stmt->bindValue(':id', $userId, SQLITE3_INTEGER);
|
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
|
||||||
$_SESSION['success_message'] = "Utilisateur modifié avec succès.";
|
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'EDIT_USER',
|
|
||||||
"Modification du compte administrateur : " . $username
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$_SESSION['error_message'] = "Erreur lors de la modification de l'utilisateur.";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$userId = $_POST['user_id'] ?? '';
|
$userId = $_POST['user_id'] ?? '';
|
||||||
|
|
||||||
@ -188,12 +149,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$_SESSION['success_message'] = "Utilisateur supprimé avec succès.";
|
$_SESSION['success_message'] = "Utilisateur supprimé avec succès.";
|
||||||
logAdminAction(
|
|
||||||
$_SESSION['admin_id'],
|
|
||||||
'DELETE_USER',
|
|
||||||
"Suppression d'un compte administrateur",
|
|
||||||
"ID: " . $userId
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la suppression de l'utilisateur.";
|
$_SESSION['error_message'] = "Erreur lors de la suppression de l'utilisateur.";
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
1.1.2
|
1.0.2
|
Loading…
x
Reference in New Issue
Block a user