Compare commits

...

26 Commits
1.0.7 ... main

Author SHA1 Message Date
8e22f1c0f6 ajout du n° de version 2025-01-11 17:00:05 +01:00
6f9e20283f les vignettes des albums matures sont toujours floutées 2025-01-11 16:59:30 +01:00
a8ca9da433 le flou ne disparait plus au survol des albums matures 2025-01-11 16:00:35 +01:00
c218d407e5 espaces insécables pour un meilleur rendu visuel 2025-01-11 15:52:24 +01:00
d42ac6ddba l'accès aux images privées est correctement protégé 2025-01-10 15:37:04 +01:00
025ed3cd9c ajout du numéro de version 2025-01-09 23:17:58 +01:00
a5263e321b quelques améliorations sur la page des logs 2025-01-09 23:17:35 +01:00
ed3d0ec909 création des dossiers avec les permissions 0775 plutôt que 0755 2025-01-09 18:48:03 +01:00
325791eb56 espace insécable pour un affichage plus naturel 2025-01-09 18:42:25 +01:00
95564b33c3 le bouton de retour (page de partage) renvoie à l'accueil si l'on ne venait pas d'une galerie 2025-01-09 18:37:50 +01:00
fb1b86b471 les images s'affichent de nouveau dans les pages de partage 2025-01-09 18:31:31 +01:00
5037ec6982 le bouton "j'ai plus de 18 ans" fonctionne à nouveau 2025-01-09 17:33:33 +01:00
abee696da8 les albums s'affichent à nouveau 2025-01-09 17:31:27 +01:00
5eed24e223 la génération de lien pour les albums privés fonctionne de nouveau correctement si le dossier comporte des caractères non-alphanumériques 2025-01-09 13:36:42 +01:00
71f0aac614 le lien de partage n'apparait plus en double 2025-01-09 13:18:56 +01:00
43f84ce75d il est de nouveau possible de supprimer des images dans les albums privés 2025-01-09 13:16:18 +01:00
0c0a68f9ae correction du bug qui supprimait un utilisateur lors de l'édition de celui-ci 2025-01-09 13:11:38 +01:00
3da7711660 appel à la fonction de log aux endroits sensibles 2025-01-09 13:01:53 +01:00
eb06ff5ed0 intégration de la base de la gestion des logs 2025-01-09 12:21:06 +01:00
e6cf9b40ae préparation de l'implémentation de gestion des logs 2025-01-09 12:12:54 +01:00
0ae1ec7d1c nouveau logo 2025-01-08 21:53:01 +01:00
9b14c8f028 ajout d'un bouton "source" à la page de partage 2025-01-07 18:54:37 +01:00
368305252f ajout d'un bouton à l'accueil permettant d'accéder au panel d'administration 2025-01-07 18:46:41 +01:00
73736fa36b le chemin d'installation est désormais défini dans le fichier de configuration 2025-01-07 18:28:18 +01:00
c004d6014b il est de nouveau possible de supprimer les images dans l'album du carrousel 2025-01-07 17:56:55 +01:00
c900a1b280 correction de l'affichage du titre des albums 2025-01-07 17:00:01 +01:00
24 changed files with 897 additions and 112 deletions

View File

@ -38,3 +38,9 @@ Options -Indexes
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>

View File

@ -11,3 +11,4 @@ Il s'agit d'un outil de galeries d'illustrations simple et léger utilisant prin
### Crédits
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/).

View File

@ -159,6 +159,23 @@ function showAdminInterface() {
<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'];

View File

@ -22,6 +22,10 @@ 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) ?
@ -88,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>

View File

@ -25,7 +25,7 @@ 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
@ -51,7 +51,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
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";
}
@ -61,6 +61,16 @@ 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.";
}
@ -97,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;
}
}
@ -204,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); ?>"
@ -339,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>

View File

@ -25,7 +25,7 @@ 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
@ -51,7 +51,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
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";
}
@ -61,6 +61,16 @@ 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.";
}
@ -97,26 +107,47 @@ 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 (isSecurePath($imagePath) && file_exists($imagePath)) {
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_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

View File

@ -24,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.";
@ -53,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;
@ -73,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;
@ -93,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.";
}
@ -113,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);
@ -174,9 +198,9 @@ function generatePrivateTree($path, $currentPath) {
if (!$hasSubfolders) {
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
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>';
$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) {

View File

@ -21,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;
@ -41,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;
@ -61,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.";
}

View File

@ -34,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é.";
}
@ -44,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.";
}
@ -122,7 +133,7 @@ $config = getSiteConfig();
<div class="filters">
<div class="filter-group">
<label for="status-filter">Statut :</label>
<label for="status-filter">Statut&nbsp;:</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>
@ -131,7 +142,7 @@ $config = getSiteConfig();
</div>
<div class="filter-group">
<label for="album-filter">Album :</label>
<label for="album-filter">Album&nbsp;:</label>
<select id="album-filter" class="form-select" onchange="updateFilters()">
<option value="">Tous les albums</option>
<?php foreach ($albums as $album): ?>

View File

@ -1,2 +1,3 @@
ICO
ICO est la galerie d'images de l'association Camélia Studio.
test-ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@ -1,6 +1,21 @@
<?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
@ -124,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);
});
@ -184,7 +206,7 @@ function isSecurePath($path) {
return $realPath && (
(strpos($realPath, $rootPath) === 0) ||
($carouselPath && $realPath === $carouselPath)
($carouselPath && strpos($realPath, $carouselPath) === 0)
);
}
@ -364,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
@ -380,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)) {
@ -388,6 +439,7 @@ 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;

View File

@ -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">

View File

@ -133,6 +133,7 @@ $config = getSiteConfig();
<script>
// Gestion du contenu mature
function acceptMatureContent() {
document.body.classList.remove('gallery-page-mature');
document.body.classList.remove('content-blurred');
const warning = document.getElementById('mature-warning');
if (warning) {

34
images.php Normal file
View 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);

View File

@ -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' : ''; ?>">

View File

@ -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
View 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&nbsp;:</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&nbsp;:</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&nbsp;:</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>

View File

@ -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>

View File

@ -12,16 +12,22 @@ checkAdminSession();
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.";
}
@ -71,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>

View File

@ -1019,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 {

View File

@ -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 */

View File

@ -83,6 +83,11 @@ 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.";
}
@ -98,6 +103,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
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
if (!empty($password)) {
// Vérification du mot de passe
@ -125,8 +141,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_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;
case 'delete':
$userId = $_POST['user_id'] ?? '';
@ -150,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.";
}

View File

@ -1 +1 @@
1.0.7
1.1.2