Compare commits
34 Commits
Author | SHA1 | Date | |
---|---|---|---|
8e22f1c0f6 | |||
6f9e20283f | |||
a8ca9da433 | |||
c218d407e5 | |||
d42ac6ddba | |||
025ed3cd9c | |||
a5263e321b | |||
ed3d0ec909 | |||
325791eb56 | |||
95564b33c3 | |||
fb1b86b471 | |||
5037ec6982 | |||
abee696da8 | |||
5eed24e223 | |||
71f0aac614 | |||
43f84ce75d | |||
0c0a68f9ae | |||
3da7711660 | |||
eb06ff5ed0 | |||
e6cf9b40ae | |||
0ae1ec7d1c | |||
9b14c8f028 | |||
368305252f | |||
73736fa36b | |||
c004d6014b | |||
c900a1b280 | |||
37207927bd | |||
ca32d05ce6 | |||
a05bbbd43d | |||
a3a1819eb8 | |||
57548ab658 | |||
9d375850f6 | |||
b953112cb0 | |||
831a2ec875 |
@ -37,4 +37,10 @@ Options -Indexes
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "SAMEORIGIN"
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
</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>
|
@ -10,4 +10,5 @@ Il s'agit d'un outil de galeries d'illustrations simple et léger utilisant prin
|
||||
![image](https://concepts.esenjin.xyz/cyla/v2/file/381D93.png)
|
||||
|
||||
### 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,6 +1,7 @@
|
||||
<?php
|
||||
require_once 'fonctions.php';
|
||||
session_start();
|
||||
checkAdminSession();
|
||||
|
||||
// Vérifier si un utilisateur est connecté
|
||||
function checkAuth() {
|
||||
@ -148,15 +149,68 @@ function showAdminInterface() {
|
||||
</a>
|
||||
<a href="personnalisation.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="M12 20s8-3 8-10V4l-8-2-8 2v6c0 7 8 10 8 10z"/>
|
||||
<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">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<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>
|
||||
</div>
|
||||
<div class="menu-content">
|
||||
<h2>Personnalisation</h2>
|
||||
<h2>Options de personnalisation</h2>
|
||||
<p>Personnalisez le titre et la description de votre galerie.</p>
|
||||
</div>
|
||||
</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>
|
||||
<?php include 'footer.php'; ?>
|
||||
|
25
albums.php
25
albums.php
@ -17,17 +17,22 @@ $currentAlbumInfo = getAlbumInfo($currentPath);
|
||||
|
||||
// Récupérer tous les sous-dossiers
|
||||
$albums = [];
|
||||
$tempAlbums = [];
|
||||
foreach (new DirectoryIterator($currentPath) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isDir()) {
|
||||
$albumPath = $item->getPathname();
|
||||
|
||||
// Ajout de la vérification de sécurité
|
||||
if (!isSecurePath($albumPath)) continue;
|
||||
|
||||
$info = getAlbumInfo($albumPath);
|
||||
|
||||
$images = hasSubfolders($albumPath) ?
|
||||
getImagesRecursively($albumPath) :
|
||||
getLatestImages($albumPath);
|
||||
|
||||
$albums[] = [
|
||||
$tempAlbums[] = [
|
||||
'path' => str_replace('\\', '/', $albumPath),
|
||||
'title' => $info['title'],
|
||||
'description' => $info['description'],
|
||||
@ -39,6 +44,13 @@ 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
|
||||
$parentPath = dirname($currentPath);
|
||||
if (!isSecurePath($parentPath)) {
|
||||
@ -69,7 +81,7 @@ $config = getSiteConfig();
|
||||
<p><?php echo nl2br(htmlspecialchars($currentAlbumInfo['description'])); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="albums-grid">
|
||||
<?php foreach ($albums as $album): ?>
|
||||
<a href="<?php echo $album['hasSubfolders'] ? 'albums.php' : 'galeries.php'; ?>?path=<?php echo urlencode($album['path']); ?>"
|
||||
@ -80,7 +92,14 @@ $config = getSiteConfig();
|
||||
<div class="empty-album"></div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($album['images'] as $index => $image): ?>
|
||||
<div class="album-image" style="background-image: url('<?php echo htmlspecialchars($image); ?>')"></div>
|
||||
<div class="album-image">
|
||||
<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 for ($i = count($album['images']); $i < 4; $i++): ?>
|
||||
<div class="empty-image"></div>
|
||||
|
@ -7,6 +7,7 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
// Récupérer le chemin courant
|
||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums_prives';
|
||||
@ -24,16 +25,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
switch ($_POST['action']) {
|
||||
case 'upload':
|
||||
$uploadedFiles = $_FILES['images'] ?? [];
|
||||
$successCount = 0;
|
||||
$successCount = 0; // Initialiser le compteur
|
||||
$errors = [];
|
||||
|
||||
|
||||
// Gérer les uploads multiples
|
||||
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
||||
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
||||
$tmpName = $uploadedFiles['tmp_name'][$i];
|
||||
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
||||
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
|
||||
// Vérifier l'extension
|
||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||
$destination = $currentPath . '/' . $fileName;
|
||||
@ -48,9 +49,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (move_uploaded_file($tmpName, $destination)) {
|
||||
$successCount++;
|
||||
$successCount++; // Incrémenter le compteur en cas de succès
|
||||
} else {
|
||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||
}
|
||||
@ -59,7 +60,17 @@ 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) {
|
||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||
}
|
||||
@ -96,20 +107,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
case 'delete':
|
||||
$images = $_POST['images'] ?? [];
|
||||
$deleteCount = 0;
|
||||
$deleteCount = 0; // Initialiser le compteur
|
||||
$errors = [];
|
||||
|
||||
foreach ($images as $image) {
|
||||
$imagePath = $currentPath . '/' . basename($image);
|
||||
if (isSecurePrivatePath($imagePath) && file_exists($imagePath)) {
|
||||
if (isSecurePrivatePath($imagePath) && file_exists($imagePath)) { // Notez l'utilisation de isSecurePrivatePath
|
||||
if (unlink($imagePath)) {
|
||||
$deleteCount++;
|
||||
$deleteCount++; // Incrémenter le compteur en cas de succès
|
||||
} 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) {
|
||||
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).";
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['error_message'] = implode("\n", $errors);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -170,6 +195,9 @@ $config = getSiteConfig();
|
||||
<button onclick="deleteSelected()" id="deleteSelectedBtn" class="action-button action-button-danger">
|
||||
Supprimer la sélection
|
||||
</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">
|
||||
Retour
|
||||
</a>
|
||||
@ -200,7 +228,10 @@ $config = getSiteConfig();
|
||||
<div class="images-grid">
|
||||
<?php foreach($images as $image):
|
||||
$imagePath = str_replace('\\', '/', substr($currentPath, strpos($currentPath, '/liste_albums_prives/') + strlen('/liste_albums_prives/')));
|
||||
$imageUrl = getBaseUrl() . '/liste_albums_prives/' . ($imagePath ? $imagePath . '/' : '') . $image;
|
||||
$imageUrl = getBaseUrl() . '/images.php?path=' . urlencode($currentPath . '/' . $image);
|
||||
if (isset($_SESSION['admin_id'])) {
|
||||
$imageUrl .= '&admin_session=' . session_id();
|
||||
}
|
||||
?>
|
||||
<div class="image-item">
|
||||
<input type="checkbox" name="images[]" value="<?php echo htmlspecialchars($image); ?>"
|
||||
@ -291,6 +322,40 @@ $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>
|
||||
<button class="scroll-top" title="Retour en haut">↑</button>
|
||||
<script>
|
||||
@ -301,6 +366,61 @@ $config = getSiteConfig();
|
||||
scrollBtn.addEventListener('click', () => {
|
||||
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>
|
||||
<?php include 'footer.php'; ?>
|
||||
</body>
|
||||
|
120
arbre-img.php
120
arbre-img.php
@ -7,6 +7,7 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
// Récupérer le chemin courant
|
||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums';
|
||||
@ -24,16 +25,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
switch ($_POST['action']) {
|
||||
case 'upload':
|
||||
$uploadedFiles = $_FILES['images'] ?? [];
|
||||
$successCount = 0;
|
||||
$successCount = 0; // Initialiser le compteur
|
||||
$errors = [];
|
||||
|
||||
|
||||
// Gérer les uploads multiples
|
||||
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
||||
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
||||
$tmpName = $uploadedFiles['tmp_name'][$i];
|
||||
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
||||
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
|
||||
// Vérifier l'extension
|
||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||
$destination = $currentPath . '/' . $fileName;
|
||||
@ -48,9 +49,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (move_uploaded_file($tmpName, $destination)) {
|
||||
$successCount++;
|
||||
$successCount++; // Incrémenter le compteur en cas de succès
|
||||
} else {
|
||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||
}
|
||||
@ -59,7 +60,17 @@ 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) {
|
||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||
}
|
||||
@ -94,28 +105,49 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$images = $_POST['images'] ?? [];
|
||||
$deleteCount = 0;
|
||||
|
||||
foreach ($images as $image) {
|
||||
$imagePath = $currentPath . '/' . basename($image);
|
||||
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
||||
if (unlink($imagePath)) {
|
||||
$deleteCount++;
|
||||
case 'delete':
|
||||
$images = $_POST['images'] ?? [];
|
||||
$deleteCount = 0; // Initialiser le compteur
|
||||
$errors = [];
|
||||
|
||||
foreach ($images as $image) {
|
||||
$imagePath = $currentPath . '/' . basename($image);
|
||||
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
||||
if (unlink($imagePath)) {
|
||||
$deleteCount++; // Incrémenter le compteur en cas de succès
|
||||
} else {
|
||||
$errors[] = "Erreur lors de la suppression de " . basename($image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($deleteCount > 0) {
|
||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
||||
}
|
||||
break;
|
||||
|
||||
// Loguer l'action une fois que toutes les suppressions sont terminées
|
||||
if ($deleteCount > 0) {
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'DELETE_IMAGES',
|
||||
"Suppression de $deleteCount image(s)",
|
||||
$currentPath
|
||||
);
|
||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$_SESSION['error_message'] = implode("\n", $errors);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'move':
|
||||
$images = $_POST['images'] ?? [];
|
||||
$destinationPath = $_POST['destination_path'] ?? '';
|
||||
$moveCount = 0;
|
||||
if ($moveCount > 0) {
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'MOVE_IMAGES',
|
||||
"Déplacement de $moveCount image(s) vers " . basename($_POST['destination_path']),
|
||||
$currentPath . ' -> ' . $_POST['destination_path']
|
||||
);
|
||||
}
|
||||
$errors = [];
|
||||
|
||||
// Vérifier que le dossier de destination existe et est valide
|
||||
@ -221,6 +253,9 @@ $config = getSiteConfig();
|
||||
<button onclick="moveSelected()" id="moveSelectedBtn" class="action-button action-button-warning">
|
||||
Déplacer la sélection
|
||||
</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">
|
||||
Retour
|
||||
</a>
|
||||
@ -246,6 +281,18 @@ $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
|
||||
function deleteSelected() {
|
||||
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||
@ -432,27 +479,22 @@ $config = getSiteConfig();
|
||||
// Gestion des boutons d'action
|
||||
console.log("Définition de updateActionButtons");
|
||||
function updateActionButtons() {
|
||||
console.log("updateActionButtons appelé");
|
||||
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||
const count = checkboxes.length;
|
||||
console.log("Nombre d'images sélectionnées:", count);
|
||||
const checkboxes = document.querySelectorAll('.image-checkbox');
|
||||
const selectedCheckboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||
const count = selectedCheckboxes.length;
|
||||
|
||||
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
||||
const moveBtn = document.getElementById('moveSelectedBtn');
|
||||
const selectAllBtn = document.getElementById('selectAllBtn');
|
||||
|
||||
if (!deleteBtn || !moveBtn) {
|
||||
console.log("Boutons non trouvés");
|
||||
return;
|
||||
if (deleteBtn && moveBtn) {
|
||||
deleteBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
||||
moveBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
deleteBtn.style.display = 'inline-flex';
|
||||
moveBtn.style.display = 'inline-flex';
|
||||
console.log("Affichage des boutons");
|
||||
} else {
|
||||
deleteBtn.style.display = 'none';
|
||||
moveBtn.style.display = 'none';
|
||||
console.log("Masquage des boutons");
|
||||
|
||||
if (selectAllBtn) {
|
||||
selectAllBtn.textContent = checkboxes.length === selectedCheckboxes.length ?
|
||||
'Tout désélectionner' : 'Tout sélectionner';
|
||||
}
|
||||
}
|
||||
|
||||
|
134
arbre-prive.php
134
arbre-prive.php
@ -6,6 +6,7 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
// Gérer la génération de lien de partage
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'generate_link') {
|
||||
@ -23,7 +24,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
||||
|
||||
if ($shareKey) {
|
||||
$shareUrl = getBaseUrl() . '/galeries-privees.php?key=' . urlencode($shareKey);
|
||||
$_SESSION['success_message'] = "Lien de partage généré avec succès. URL : " . $shareUrl;
|
||||
logAdminAction(
|
||||
$_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;
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de la génération du lien de partage.";
|
||||
@ -52,13 +59,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||
if (!file_exists($newPath)) {
|
||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||
mkdir($newPath, 0755, true);
|
||||
mkdir($newPath, 0775, true);
|
||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||
$_SESSION['success_message'] = "Dossier privé créé avec succès.";
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'CREATE_PRIVATE_FOLDER',
|
||||
"Création du dossier privé : " . $newName,
|
||||
$newPath
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -72,6 +85,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'EDIT_PRIVATE_FOLDER',
|
||||
"Modification du dossier privé : " . $newName,
|
||||
$path
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -92,6 +111,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'DELETE_PRIVATE_FOLDER',
|
||||
"Suppression du dossier privé",
|
||||
$path
|
||||
);
|
||||
rrmdir($path);
|
||||
$_SESSION['success_message'] = "Dossier privé supprimé avec succès.";
|
||||
}
|
||||
@ -112,7 +137,7 @@ if (!isSecurePrivatePath($currentPath)) {
|
||||
|
||||
// Créer le dossier racine s'il n'existe pas
|
||||
if (!file_exists('./liste_albums_prives')) {
|
||||
mkdir('./liste_albums_prives', 0755, true);
|
||||
mkdir('./liste_albums_prives', 0775, true);
|
||||
// Créer le fichier infos.txt pour le dossier racine
|
||||
$infoContent = "Albums privés\nVos albums photos privés\n18-\n";
|
||||
file_put_contents('./liste_albums_prives/infos.txt', $infoContent);
|
||||
@ -140,60 +165,69 @@ function generatePrivateTree($path, $currentPath) {
|
||||
$output .= '</div></div>';
|
||||
}
|
||||
|
||||
// Parcourir tous les sous-dossiers
|
||||
// Récupérer et trier les sous-dossiers
|
||||
$dirs = array();
|
||||
foreach (new DirectoryIterator($path) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isDir()) {
|
||||
$fullPath = $item->getPathname();
|
||||
$info = getAlbumInfo($fullPath);
|
||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||
$hasSubfolders = hasSubfolders($fullPath);
|
||||
$hasImages = hasImages($fullPath);
|
||||
|
||||
$output .= '<li class="tree-item' . ($isCurrentPath ? ' active' : '') . '">';
|
||||
$output .= '<div class="tree-item-content">';
|
||||
$output .= '<span class="tree-link">';
|
||||
$output .= '<span class="folder-icon">🔒</span> ' . htmlspecialchars($info['title']);
|
||||
if ($info['mature_content']) {
|
||||
$output .= ' <span class="mature-warning">🔞</span>';
|
||||
}
|
||||
$output .= '</span>';
|
||||
$output .= '<div class="tree-actions">';
|
||||
if (!$hasSubfolders) {
|
||||
$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) {
|
||||
$output .= '<button onclick="generateShareLink(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. htmlspecialchars($info['title'])
|
||||
. '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
||||
}
|
||||
}
|
||||
if (!$hasSubfolders) {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \''
|
||||
. rawurlencode($info['more_info_url']) . '\', '
|
||||
. ($hasImages ? 'true' : 'false')
|
||||
. ')" class="tree-button">✏️</button>';
|
||||
} else {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \'\', false)" class="tree-button">✏️</button>';
|
||||
}
|
||||
if (!$hasImages) {
|
||||
$output .= '<button onclick="createSubfolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button">➕</button>';
|
||||
}
|
||||
if ($fullPath !== './liste_albums_prives') {
|
||||
$output .= '<button onclick="deleteFolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button tree-button-danger">🗑️</button>';
|
||||
}
|
||||
$output .= '</div></div>';
|
||||
|
||||
$output .= generatePrivateTree($fullPath, $currentPath);
|
||||
$output .= '</li>';
|
||||
$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);
|
||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||
$hasSubfolders = hasSubfolders($fullPath);
|
||||
$hasImages = hasImages($fullPath);
|
||||
|
||||
$output .= '<li class="tree-item' . ($isCurrentPath ? ' active' : '') . '">';
|
||||
$output .= '<div class="tree-item-content">';
|
||||
$output .= '<span class="tree-link">';
|
||||
$output .= '<span class="folder-icon">🔒</span> ' . htmlspecialchars($info['title']);
|
||||
if ($info['mature_content']) {
|
||||
$output .= ' <span class="mature-warning">🔞</span>';
|
||||
}
|
||||
$output .= '</span>';
|
||||
$output .= '<div class="tree-actions">';
|
||||
if (!$hasSubfolders) {
|
||||
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
|
||||
if ($hasImages) {
|
||||
$encodedPath = htmlspecialchars(addslashes($fullPath));
|
||||
$encodedTitle = htmlspecialchars(addslashes($info['title']));
|
||||
$output .= '<button onclick="generateShareLink(\'' . $encodedPath . '\', \'' . $encodedTitle . '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
||||
}
|
||||
}
|
||||
if (!$hasSubfolders) {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \''
|
||||
. rawurlencode($info['more_info_url']) . '\', '
|
||||
. ($hasImages ? 'true' : 'false')
|
||||
. ')" class="tree-button">✏️</button>';
|
||||
} else {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \'\', false)" class="tree-button">✏️</button>';
|
||||
}
|
||||
if (!$hasImages) {
|
||||
$output .= '<button onclick="createSubfolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button">➕</button>';
|
||||
}
|
||||
if ($fullPath !== './liste_albums_prives') {
|
||||
$output .= '<button onclick="deleteFolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button tree-button-danger">🗑️</button>';
|
||||
}
|
||||
$output .= '</div></div>';
|
||||
|
||||
$output .= generatePrivateTree($fullPath, $currentPath);
|
||||
$output .= '</li>';
|
||||
}
|
||||
|
||||
$output .= '</ul>';
|
||||
return $output;
|
||||
|
114
arbre.php
114
arbre.php
@ -6,6 +6,7 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
@ -20,13 +21,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||
if (!file_exists($newPath)) {
|
||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||
mkdir($newPath, 0755, true);
|
||||
mkdir($newPath, 0775, true);
|
||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||
$_SESSION['success_message'] = "Dossier créé avec succès.";
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'CREATE_FOLDER',
|
||||
"Création du dossier : " . $newName,
|
||||
$newPath
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -40,6 +47,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'EDIT_FOLDER',
|
||||
"Modification du dossier : " . $newName,
|
||||
$path
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -60,6 +73,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'DELETE_FOLDER',
|
||||
"Suppression du dossier",
|
||||
$path
|
||||
);
|
||||
rrmdir($path);
|
||||
$_SESSION['success_message'] = "Dossier supprimé avec succès.";
|
||||
}
|
||||
@ -111,55 +130,63 @@ function generateTree($path, $currentPath) {
|
||||
$output .= '</div></div>';
|
||||
}
|
||||
|
||||
// Parcourir tous les sous-dossiers
|
||||
// Récupérer et trier les sous-dossiers
|
||||
$dirs = array();
|
||||
foreach (new DirectoryIterator($path) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isDir()) {
|
||||
$fullPath = $item->getPathname();
|
||||
$info = getAlbumInfo($fullPath);
|
||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||
$hasSubfolders = hasSubfolders($fullPath);
|
||||
|
||||
$output .= '<li class="tree-item' . ($isCurrentPath ? ' active' : '') . '">';
|
||||
$output .= '<div class="tree-item-content">';
|
||||
$output .= '<span class="tree-link">';
|
||||
$output .= '<span class="folder-icon">📁</span> ' . htmlspecialchars($info['title']);
|
||||
if ($info['mature_content']) {
|
||||
$output .= ' <span class="mature-warning">🔞</span>';
|
||||
}
|
||||
$output .= '</span>';
|
||||
$output .= '<div class="tree-actions">';
|
||||
if (!$hasSubfolders) {
|
||||
$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) {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \''
|
||||
. rawurlencode($info['more_info_url']) . '\', '
|
||||
. (hasImages($fullPath) ? 'true' : 'false')
|
||||
. ')" class="tree-button">✏️</button>';
|
||||
} else {
|
||||
// Pour les dossiers sans images
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \'\', false)" class="tree-button">✏️</button>';
|
||||
}
|
||||
if (!hasImages($fullPath)) {
|
||||
$output .= '<button onclick="createSubfolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button">➕</button>';
|
||||
}
|
||||
if ($fullPath !== './liste_albums') {
|
||||
$output .= '<button onclick="deleteFolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button tree-button-danger">🗑️</button>';
|
||||
}
|
||||
$output .= '</div></div>';
|
||||
|
||||
$output .= generateTree($fullPath, $currentPath);
|
||||
$output .= '</li>';
|
||||
$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);
|
||||
$isCurrentPath = realpath($fullPath) === $currentPath;
|
||||
$hasSubfolders = hasSubfolders($fullPath);
|
||||
|
||||
$output .= '<li class="tree-item' . ($isCurrentPath ? ' active' : '') . '">';
|
||||
$output .= '<div class="tree-item-content">';
|
||||
$output .= '<span class="tree-link">';
|
||||
$output .= '<span class="folder-icon">📁</span> ' . htmlspecialchars($info['title']);
|
||||
if ($info['mature_content']) {
|
||||
$output .= ' <span class="mature-warning">🔞</span>';
|
||||
}
|
||||
$output .= '</span>';
|
||||
$output .= '<div class="tree-actions">';
|
||||
if (!$hasSubfolders) {
|
||||
$output .= '<a href="arbre-img.php?path=' . urlencode($fullPath) . '" class="tree-button" style="text-decoration: none">🖼️</a>';
|
||||
}
|
||||
if (!$hasSubfolders) {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \''
|
||||
. rawurlencode($info['more_info_url']) . '\', '
|
||||
. (hasImages($fullPath) ? 'true' : 'false')
|
||||
. ')" class="tree-button">✏️</button>';
|
||||
} else {
|
||||
$output .= '<button onclick="editFolder(\'' . htmlspecialchars($fullPath) . '\', \''
|
||||
. rawurlencode($info['title']) . '\', \''
|
||||
. rawurlencode($info['description']) . '\', '
|
||||
. ($info['mature_content'] ? 'true' : 'false') . ', \'\', false)" class="tree-button">✏️</button>';
|
||||
}
|
||||
if (!hasImages($fullPath)) {
|
||||
$output .= '<button onclick="createSubfolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button">➕</button>';
|
||||
}
|
||||
if ($fullPath !== './liste_albums') {
|
||||
$output .= '<button onclick="deleteFolder(\'' . htmlspecialchars($fullPath) . '\')" class="tree-button tree-button-danger">🗑️</button>';
|
||||
}
|
||||
$output .= '</div></div>';
|
||||
|
||||
$output .= generateTree($fullPath, $currentPath);
|
||||
$output .= '</li>';
|
||||
}
|
||||
|
||||
$output .= '</ul>';
|
||||
return $output;
|
||||
@ -167,7 +194,6 @@ function generateTree($path, $currentPath) {
|
||||
|
||||
$config = getSiteConfig();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
|
16
clefs.php
16
clefs.php
@ -2,6 +2,7 @@
|
||||
require_once 'fonctions.php';
|
||||
|
||||
session_start();
|
||||
checkAdminSession();
|
||||
|
||||
// Variables pour stocker les messages
|
||||
$successMessage = null;
|
||||
@ -33,6 +34,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$successMessage = "Clé supprimée avec succès.";
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'DELETE_SHARE_KEY',
|
||||
"Suppression d'une clé de partage",
|
||||
"ID: " . $keyId
|
||||
);
|
||||
} else {
|
||||
$errorMessage = "Erreur lors de la suppression de la clé.";
|
||||
}
|
||||
@ -43,6 +50,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$deletedCount = cleanExpiredShareKeys();
|
||||
if ($deletedCount > 0) {
|
||||
$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 {
|
||||
$successMessage = "Aucune clé expirée à supprimer.";
|
||||
}
|
||||
@ -121,7 +133,7 @@ $config = getSiteConfig();
|
||||
|
||||
<div class="filters">
|
||||
<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()">
|
||||
<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>
|
||||
@ -130,7 +142,7 @@ $config = getSiteConfig();
|
||||
</div>
|
||||
|
||||
<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()">
|
||||
<option value="">Tous les albums</option>
|
||||
<?php foreach ($albums as $album): ?>
|
||||
|
@ -1,2 +1,3 @@
|
||||
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: 68 KiB After Width: | Height: | Size: 92 KiB |
171
fonctions.php
171
fonctions.php
@ -1,8 +1,44 @@
|
||||
<?php
|
||||
// Configuration
|
||||
define('PROJECT_ROOT_DIR', 'test-ico');
|
||||
function getProjectRootDir() {
|
||||
$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']);
|
||||
|
||||
// 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
|
||||
* @return string L'URL de base du site
|
||||
@ -103,15 +139,22 @@ function getImagesRecursively($albumPath, $limit = 4) {
|
||||
if ($file->isFile()) {
|
||||
$extension = strtolower($file->getExtension());
|
||||
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('./'))));
|
||||
$images[] = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||
$images[] = [
|
||||
'url' => $baseUrl . '/' . ltrim($relativePath, '/'),
|
||||
'is_mature' => $parentInfo['mature_content']
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($images, function($a, $b) {
|
||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a['url']);
|
||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b['url']);
|
||||
return filectime($pathB) - filectime($pathA);
|
||||
});
|
||||
|
||||
@ -163,7 +206,7 @@ function isSecurePath($path) {
|
||||
|
||||
return $realPath && (
|
||||
(strpos($realPath, $rootPath) === 0) ||
|
||||
($carouselPath && $realPath === $carouselPath)
|
||||
($carouselPath && strpos($realPath, $carouselPath) === 0)
|
||||
);
|
||||
}
|
||||
|
||||
@ -343,6 +386,34 @@ function cleanExpiredShareKeys() {
|
||||
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
|
||||
* @return string La version du projet
|
||||
@ -359,7 +430,8 @@ function getSiteConfig() {
|
||||
$configFile = './config.txt';
|
||||
$config = [
|
||||
'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)) {
|
||||
@ -367,8 +439,95 @@ function getSiteConfig() {
|
||||
$lines = explode("\n", $content);
|
||||
if (isset($lines[0])) $config['site_title'] = trim($lines[0]);
|
||||
if (isset($lines[1])) $config['site_description'] = trim($lines[1]);
|
||||
if (isset($lines[2])) $config['project_path'] = trim($lines[2]);
|
||||
}
|
||||
|
||||
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)) {
|
||||
// Obtenir le chemin relatif depuis la racine du projet
|
||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||
$url = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||
$url = $baseUrl . '/images.php?path=' . urlencode($file->getPathname()) . '&key=' . urlencode($shareKey);
|
||||
// Vérifier que le fichier existe et est accessible
|
||||
if (file_exists($file->getPathname())) {
|
||||
$images[] = $url;
|
||||
@ -148,7 +148,7 @@ $config = getSiteConfig();
|
||||
}
|
||||
?>
|
||||
<div class="gallery-item <?php echo $isTop ? 'gallery-item-top' : ''; ?> <?php echo $spanClass; ?>">
|
||||
<a href="partage.php?image=<?php echo urlencode($image); ?>" target="_blank">
|
||||
<a href="partage.php?image=<?php echo urlencode($image); ?>&key=<?php echo urlencode($shareKey); ?>" target="_blank">
|
||||
<img src="<?php echo htmlspecialchars($image); ?>"
|
||||
alt="Image de la galerie"
|
||||
loading="lazy">
|
||||
|
19
galeries.php
19
galeries.php
@ -133,15 +133,16 @@ $config = getSiteConfig();
|
||||
<script>
|
||||
// Gestion du contenu mature
|
||||
function acceptMatureContent() {
|
||||
document.body.classList.remove('content-blurred');
|
||||
const warning = document.getElementById('mature-warning');
|
||||
if (warning) {
|
||||
warning.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
warning.style.display = 'none';
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
document.body.classList.remove('gallery-page-mature');
|
||||
document.body.classList.remove('content-blurred');
|
||||
const warning = document.getElementById('mature-warning');
|
||||
if (warning) {
|
||||
warning.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
warning.style.display = 'none';
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<button class="scroll-top" title="Retour en haut">↑</button>
|
||||
<script>
|
||||
|
34
images.php
Normal file
34
images.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?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
|
||||
if (!is_dir($carouselDir)) {
|
||||
// Créer le dossier s'il n'existe pas
|
||||
mkdir($carouselDir, 0755, true);
|
||||
mkdir($carouselDir, 0775, true);
|
||||
return $images;
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ $config = getSiteConfig();
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="admin.php" class="admin-button" title="Administration">⚙️</a>
|
||||
<div class="carousel">
|
||||
<?php foreach($carouselImages as $index => $image): ?>
|
||||
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
||||
|
13
init-db.php
13
init-db.php
@ -47,10 +47,23 @@ if ($count === 0) {
|
||||
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
|
||||
$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_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();
|
||||
echo "Base de données initialisée avec succès !";
|
||||
|
276
logs.php
Normal file
276
logs.php
Normal file
@ -0,0 +1,276 @@
|
||||
<?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,6 +4,35 @@ require_once 'fonctions.php';
|
||||
// Vérifier que nous avons une URL d'image
|
||||
$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
|
||||
if (!$imageUrl) {
|
||||
header('Location: index.php');
|
||||
@ -25,7 +54,7 @@ $config = getSiteConfig();
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="share-page">
|
||||
<button onclick="window.close();" class="back-button">Retour à la galerie</button>
|
||||
<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>
|
||||
|
||||
<div class="share-container">
|
||||
<div class="share-image">
|
||||
@ -42,6 +71,7 @@ $config = getSiteConfig();
|
||||
Partager
|
||||
</button>
|
||||
|
||||
<?php if (!$isPrivateImage): ?>
|
||||
<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">
|
||||
<polyline points="16 18 22 12 16 6"></polyline>
|
||||
@ -49,6 +79,7 @@ $config = getSiteConfig();
|
||||
</svg>
|
||||
Intégrer
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
||||
download="<?php echo htmlspecialchars($filename); ?>"
|
||||
@ -60,6 +91,18 @@ $config = getSiteConfig();
|
||||
</svg>
|
||||
Télécharger
|
||||
</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>
|
||||
|
||||
|
@ -6,21 +6,28 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
// Gérer les soumissions du formulaire
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$siteTitle = $_POST['site_title'] ?? '';
|
||||
$siteDescription = $_POST['site_description'] ?? '';
|
||||
$projectPath = $_POST['project_path'] ?? '';
|
||||
|
||||
// Vérifications basiques
|
||||
if (empty($siteTitle)) {
|
||||
$_SESSION['error_message'] = "Le titre du site est requis.";
|
||||
} else {
|
||||
// Sauvegarder la configuration
|
||||
$configContent = $siteTitle . "\n" . $siteDescription;
|
||||
$configContent = $siteTitle . "\n" . $siteDescription . "\n" . $projectPath;
|
||||
|
||||
if (file_put_contents('./config.txt', $configContent) !== false) {
|
||||
$_SESSION['success_message'] = "Configuration mise à jour avec succès.";
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'UPDATE_SETTINGS',
|
||||
"Modification des paramètres du site"
|
||||
);
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de la sauvegarde de la configuration.";
|
||||
}
|
||||
@ -70,13 +77,19 @@ $config = getSiteConfig();
|
||||
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>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="site_description">Description du site :</label>
|
||||
<textarea id="site_description" name="site_description" rows="4"
|
||||
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>
|
||||
</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">
|
||||
<button type="submit" class="action-button">Enregistrer les modifications</button>
|
||||
|
132
styles-admin.css
132
styles-admin.css
@ -407,6 +407,86 @@ body {
|
||||
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 */
|
||||
.message {
|
||||
padding: 1rem;
|
||||
@ -939,6 +1019,58 @@ body[data-page="carrousel"] .admin-header {
|
||||
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 (max-width: 768px) {
|
||||
.admin-page {
|
||||
|
71
styles.css
71
styles.css
@ -15,6 +15,32 @@ body {
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -160,9 +186,12 @@ body {
|
||||
}
|
||||
|
||||
/* Styles pour le contenu mature dans les albums */
|
||||
.album-card-mature {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.album-card-mature .album-images {
|
||||
filter: blur(10px);
|
||||
transition: filter 0.3s ease;
|
||||
}
|
||||
|
||||
.album-card-mature::before {
|
||||
@ -177,8 +206,12 @@ body {
|
||||
border-radius: 0.5rem;
|
||||
z-index: 2;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
transition: transform 0.3s ease, background-color 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 {
|
||||
@ -190,12 +223,34 @@ body {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.album-card-mature:hover .album-images {
|
||||
filter: blur(0);
|
||||
.image-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.album-card-mature:hover::before {
|
||||
opacity: 0;
|
||||
.image-background.mature-preview {
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.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 {
|
||||
@ -231,6 +286,7 @@ body {
|
||||
}
|
||||
|
||||
.album-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
@ -260,7 +316,6 @@ body {
|
||||
.album-card .album-info h2 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Styles pour la page galerie */
|
||||
|
117
utilisateurs.php
117
utilisateurs.php
@ -6,6 +6,7 @@ if (!isset($_SESSION['admin_id'])) {
|
||||
header('Location: admin.php?action=login');
|
||||
exit;
|
||||
}
|
||||
checkAdminSession();
|
||||
|
||||
// Vérifier que c'est bien le premier administrateur
|
||||
$db = new SQLite3('database.sqlite');
|
||||
@ -82,49 +83,87 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$_SESSION['success_message'] = "Utilisateur ajouté avec succès.";
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'ADD_USER',
|
||||
"Création du compte administrateur : " . $username
|
||||
);
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de l'ajout de l'utilisateur.";
|
||||
}
|
||||
break;
|
||||
|
||||
case 'edit':
|
||||
$userId = $_POST['user_id'] ?? '';
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
case 'edit':
|
||||
$userId = $_POST['user_id'] ?? '';
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($userId) || empty($username)) {
|
||||
$_SESSION['error_message'] = "Des informations sont manquantes.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($userId) || empty($username)) {
|
||||
$_SESSION['error_message'] = "Des informations sont manquantes.";
|
||||
// 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
|
||||
if (!empty($password)) {
|
||||
// Vérification du mot de passe
|
||||
if (strlen($password) < 12) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit faire au moins 12 caractères.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[a-z]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins une lettre minuscule.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins une lettre majuscule.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un chiffre.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un caractère spécial.";
|
||||
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;
|
||||
}
|
||||
|
||||
// Si un nouveau mot de passe est fourni
|
||||
if (!empty($password)) {
|
||||
// Vérification du mot de passe
|
||||
if (strlen($password) < 12) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit faire au moins 12 caractères.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[a-z]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins une lettre minuscule.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins une lettre majuscule.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un chiffre.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!preg_match('/[^A-Za-z0-9]/', $password)) {
|
||||
$_SESSION['error_message'] = "Le mot de passe doit contenir au moins un caractère spécial.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
case 'delete':
|
||||
$userId = $_POST['user_id'] ?? '';
|
||||
@ -149,6 +188,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$_SESSION['success_message'] = "Utilisateur supprimé avec succès.";
|
||||
logAdminAction(
|
||||
$_SESSION['admin_id'],
|
||||
'DELETE_USER',
|
||||
"Suppression d'un compte administrateur",
|
||||
"ID: " . $userId
|
||||
);
|
||||
} else {
|
||||
$_SESSION['error_message'] = "Erreur lors de la suppression de l'utilisateur.";
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
1.0.5
|
||||
1.1.2
|
Loading…
x
Reference in New Issue
Block a user