Compare commits
25 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 |
@ -37,4 +37,10 @@ Options -Indexes
|
|||||||
Header set X-Content-Type-Options "nosniff"
|
Header set X-Content-Type-Options "nosniff"
|
||||||
Header set X-Frame-Options "SAMEORIGIN"
|
Header set X-Frame-Options "SAMEORIGIN"
|
||||||
Header set X-XSS-Protection "1; mode=block"
|
Header set X-XSS-Protection "1; mode=block"
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# 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>
|
</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)
|
![image](https://concepts.esenjin.xyz/cyla/v2/file/381D93.png)
|
||||||
|
|
||||||
### Crédits
|
### Crédits
|
||||||
Le code d'**ICO** a été réalisé en grande partie grâce à l'aide de [Claude.ia](https://claude.ai/).
|
Le code d'**ICO** a été réalisé en grande partie grâce à l'aide de [Claude.ia](https://claude.ai/).
|
||||||
|
Le logo vient de [Freepik.com](https://fr.freepik.com/).
|
17
admin.php
17
admin.php
@ -159,6 +159,23 @@ function showAdminInterface() {
|
|||||||
<p>Personnalisez le titre et la description de votre galerie.</p>
|
<p>Personnalisez le titre et la description de votre galerie.</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
<?php if ($_SESSION['admin_id'] == $firstId): ?>
|
||||||
|
<a href="logs.php" class="admin-menu-item">
|
||||||
|
<div class="menu-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||||
|
<polyline points="14 2 14 8 20 8"></polyline>
|
||||||
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||||
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||||
|
<polyline points="10 9 9 9 8 9"></polyline>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="menu-content">
|
||||||
|
<h2>Logs système</h2>
|
||||||
|
<p>Consultez l'historique des actions des administrateurs.</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
$updateStatus = checkUpdate();
|
$updateStatus = checkUpdate();
|
||||||
$updateAvailable = $updateStatus && $updateStatus['available'];
|
$updateAvailable = $updateStatus && $updateStatus['available'];
|
||||||
|
15
albums.php
15
albums.php
@ -22,6 +22,10 @@ foreach (new DirectoryIterator($currentPath) as $item) {
|
|||||||
if ($item->isDot()) continue;
|
if ($item->isDot()) continue;
|
||||||
if ($item->isDir()) {
|
if ($item->isDir()) {
|
||||||
$albumPath = $item->getPathname();
|
$albumPath = $item->getPathname();
|
||||||
|
|
||||||
|
// Ajout de la vérification de sécurité
|
||||||
|
if (!isSecurePath($albumPath)) continue;
|
||||||
|
|
||||||
$info = getAlbumInfo($albumPath);
|
$info = getAlbumInfo($albumPath);
|
||||||
|
|
||||||
$images = hasSubfolders($albumPath) ?
|
$images = hasSubfolders($albumPath) ?
|
||||||
@ -77,7 +81,7 @@ $config = getSiteConfig();
|
|||||||
<p><?php echo nl2br(htmlspecialchars($currentAlbumInfo['description'])); ?></p>
|
<p><?php echo nl2br(htmlspecialchars($currentAlbumInfo['description'])); ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="albums-grid">
|
<div class="albums-grid">
|
||||||
<?php foreach ($albums as $album): ?>
|
<?php foreach ($albums as $album): ?>
|
||||||
<a href="<?php echo $album['hasSubfolders'] ? 'albums.php' : 'galeries.php'; ?>?path=<?php echo urlencode($album['path']); ?>"
|
<a href="<?php echo $album['hasSubfolders'] ? 'albums.php' : 'galeries.php'; ?>?path=<?php echo urlencode($album['path']); ?>"
|
||||||
@ -88,7 +92,14 @@ $config = getSiteConfig();
|
|||||||
<div class="empty-album"></div>
|
<div class="empty-album"></div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($album['images'] as $index => $image): ?>
|
<?php foreach ($album['images'] as $index => $image): ?>
|
||||||
<div class="album-image" 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 endforeach; ?>
|
||||||
<?php for ($i = count($album['images']); $i < 4; $i++): ?>
|
<?php for ($i = count($album['images']); $i < 4; $i++): ?>
|
||||||
<div class="empty-image"></div>
|
<div class="empty-image"></div>
|
||||||
|
@ -25,16 +25,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
switch ($_POST['action']) {
|
switch ($_POST['action']) {
|
||||||
case 'upload':
|
case 'upload':
|
||||||
$uploadedFiles = $_FILES['images'] ?? [];
|
$uploadedFiles = $_FILES['images'] ?? [];
|
||||||
$successCount = 0;
|
$successCount = 0; // Initialiser le compteur
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
// Gérer les uploads multiples
|
// Gérer les uploads multiples
|
||||||
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
||||||
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
||||||
$tmpName = $uploadedFiles['tmp_name'][$i];
|
$tmpName = $uploadedFiles['tmp_name'][$i];
|
||||||
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
||||||
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
// Vérifier l'extension
|
// Vérifier l'extension
|
||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
$destination = $currentPath . '/' . $fileName;
|
$destination = $currentPath . '/' . $fileName;
|
||||||
@ -49,9 +49,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$counter++;
|
$counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move_uploaded_file($tmpName, $destination)) {
|
if (move_uploaded_file($tmpName, $destination)) {
|
||||||
$successCount++;
|
$successCount++; // Incrémenter le compteur en cas de succès
|
||||||
} else {
|
} else {
|
||||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||||
}
|
}
|
||||||
@ -60,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) {
|
if ($successCount > 0) {
|
||||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||||
}
|
}
|
||||||
@ -97,20 +107,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$deleteCount = 0;
|
$deleteCount = 0; // Initialiser le compteur
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
foreach ($images as $image) {
|
foreach ($images as $image) {
|
||||||
$imagePath = $currentPath . '/' . basename($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)) {
|
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) {
|
if ($deleteCount > 0) {
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'DELETE_PRIVATE_IMAGES', // Notez le changement ici pour les images privées
|
||||||
|
"Suppression de $deleteCount image(s) privée(s)", // Message adapté pour les images privées
|
||||||
|
$currentPath
|
||||||
|
);
|
||||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$_SESSION['error_message'] = implode("\n", $errors);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +228,10 @@ $config = getSiteConfig();
|
|||||||
<div class="images-grid">
|
<div class="images-grid">
|
||||||
<?php foreach($images as $image):
|
<?php foreach($images as $image):
|
||||||
$imagePath = str_replace('\\', '/', substr($currentPath, strpos($currentPath, '/liste_albums_prives/') + strlen('/liste_albums_prives/')));
|
$imagePath = str_replace('\\', '/', substr($currentPath, strpos($currentPath, '/liste_albums_prives/') + strlen('/liste_albums_prives/')));
|
||||||
$imageUrl = getBaseUrl() . '/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">
|
<div class="image-item">
|
||||||
<input type="checkbox" name="images[]" value="<?php echo htmlspecialchars($image); ?>"
|
<input type="checkbox" name="images[]" value="<?php echo htmlspecialchars($image); ?>"
|
||||||
@ -339,6 +366,61 @@ $config = getSiteConfig();
|
|||||||
scrollBtn.addEventListener('click', () => {
|
scrollBtn.addEventListener('click', () => {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fonction pour basculer la sélection de toutes les images
|
||||||
|
function toggleSelectAll() {
|
||||||
|
const checkboxes = document.querySelectorAll('.image-checkbox');
|
||||||
|
const allChecked = document.querySelectorAll('.image-checkbox:checked').length === checkboxes.length;
|
||||||
|
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
checkbox.checked = !allChecked;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateActionButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de suppression d'une seule image
|
||||||
|
function deleteImage(imageName) {
|
||||||
|
if (confirm('Êtes-vous sûr de vouloir supprimer cette image ?')) {
|
||||||
|
const form = document.getElementById('imagesForm');
|
||||||
|
form.innerHTML = `
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
<input type="hidden" name="images[]" value="${imageName}">
|
||||||
|
`;
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de suppression multiple
|
||||||
|
function deleteSelected() {
|
||||||
|
const checkboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||||
|
if (checkboxes.length > 0 && confirm('Êtes-vous sûr de vouloir supprimer les images sélectionnées ?')) {
|
||||||
|
document.getElementById('formAction').value = 'delete';
|
||||||
|
document.getElementById('imagesForm').submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fonction de mise à jour de l'état des boutons d'action
|
||||||
|
function updateActionButtons() {
|
||||||
|
const checkboxes = document.querySelectorAll('.image-checkbox');
|
||||||
|
const selectedCheckboxes = document.querySelectorAll('.image-checkbox:checked');
|
||||||
|
const count = selectedCheckboxes.length;
|
||||||
|
|
||||||
|
const deleteBtn = document.getElementById('deleteSelectedBtn');
|
||||||
|
const selectAllBtn = document.getElementById('selectAllBtn');
|
||||||
|
|
||||||
|
if (deleteBtn) {
|
||||||
|
deleteBtn.style.display = count > 0 ? 'inline-flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectAllBtn) {
|
||||||
|
selectAllBtn.textContent = checkboxes.length === selectedCheckboxes.length ?
|
||||||
|
'Tout désélectionner' : 'Tout sélectionner';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation des boutons au chargement
|
||||||
|
document.addEventListener('DOMContentLoaded', updateActionButtons);
|
||||||
</script>
|
</script>
|
||||||
<?php include 'footer.php'; ?>
|
<?php include 'footer.php'; ?>
|
||||||
</body>
|
</body>
|
||||||
|
@ -25,16 +25,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
switch ($_POST['action']) {
|
switch ($_POST['action']) {
|
||||||
case 'upload':
|
case 'upload':
|
||||||
$uploadedFiles = $_FILES['images'] ?? [];
|
$uploadedFiles = $_FILES['images'] ?? [];
|
||||||
$successCount = 0;
|
$successCount = 0; // Initialiser le compteur
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
// Gérer les uploads multiples
|
// Gérer les uploads multiples
|
||||||
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
for ($i = 0; $i < count($uploadedFiles['name']); $i++) {
|
||||||
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
if ($uploadedFiles['error'][$i] === UPLOAD_ERR_OK) {
|
||||||
$tmpName = $uploadedFiles['tmp_name'][$i];
|
$tmpName = $uploadedFiles['tmp_name'][$i];
|
||||||
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
$fileName = sanitizeFilename($uploadedFiles['name'][$i]);
|
||||||
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
$extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
// Vérifier l'extension
|
// Vérifier l'extension
|
||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
$destination = $currentPath . '/' . $fileName;
|
$destination = $currentPath . '/' . $fileName;
|
||||||
@ -49,9 +49,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$counter++;
|
$counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move_uploaded_file($tmpName, $destination)) {
|
if (move_uploaded_file($tmpName, $destination)) {
|
||||||
$successCount++;
|
$successCount++; // Incrémenter le compteur en cas de succès
|
||||||
} else {
|
} else {
|
||||||
$errors[] = "Erreur lors du déplacement de $fileName";
|
$errors[] = "Erreur lors du déplacement de $fileName";
|
||||||
}
|
}
|
||||||
@ -60,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) {
|
if ($successCount > 0) {
|
||||||
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
$_SESSION['success_message'] = "$successCount image(s) téléversée(s) avec succès.";
|
||||||
}
|
}
|
||||||
@ -95,28 +105,49 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'delete':
|
case 'delete':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$deleteCount = 0;
|
$deleteCount = 0; // Initialiser le compteur
|
||||||
|
$errors = [];
|
||||||
foreach ($images as $image) {
|
|
||||||
$imagePath = $currentPath . '/' . basename($image);
|
foreach ($images as $image) {
|
||||||
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
$imagePath = $currentPath . '/' . basename($image);
|
||||||
if (unlink($imagePath)) {
|
if (isSecurePath($imagePath) && file_exists($imagePath)) {
|
||||||
$deleteCount++;
|
if (unlink($imagePath)) {
|
||||||
|
$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) {
|
||||||
if ($deleteCount > 0) {
|
logAdminAction(
|
||||||
$_SESSION['success_message'] = "$deleteCount image(s) supprimée(s).";
|
$_SESSION['admin_id'],
|
||||||
}
|
'DELETE_IMAGES',
|
||||||
break;
|
"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':
|
case 'move':
|
||||||
$images = $_POST['images'] ?? [];
|
$images = $_POST['images'] ?? [];
|
||||||
$destinationPath = $_POST['destination_path'] ?? '';
|
$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 = [];
|
$errors = [];
|
||||||
|
|
||||||
// Vérifier que le dossier de destination existe et est valide
|
// Vérifier que le dossier de destination existe et est valide
|
||||||
|
@ -24,7 +24,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
|||||||
|
|
||||||
if ($shareKey) {
|
if ($shareKey) {
|
||||||
$shareUrl = getBaseUrl() . '/galeries-privees.php?key=' . urlencode($shareKey);
|
$shareUrl = getBaseUrl() . '/galeries-privees.php?key=' . urlencode($shareKey);
|
||||||
$_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;
|
$_SESSION['share_url'] = $shareUrl;
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la génération du lien de partage.";
|
$_SESSION['error_message'] = "Erreur lors de la génération du lien de partage.";
|
||||||
@ -53,13 +59,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||||
if (!file_exists($newPath)) {
|
if (!file_exists($newPath)) {
|
||||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||||
mkdir($newPath, 0755, true);
|
mkdir($newPath, 0775, true);
|
||||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||||
$_SESSION['success_message'] = "Dossier privé créé avec succès.";
|
$_SESSION['success_message'] = "Dossier privé créé avec succès.";
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'CREATE_PRIVATE_FOLDER',
|
||||||
|
"Création du dossier privé : " . $newName,
|
||||||
|
$newPath
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -73,6 +85,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'EDIT_PRIVATE_FOLDER',
|
||||||
|
"Modification du dossier privé : " . $newName,
|
||||||
|
$path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -93,6 +111,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'DELETE_PRIVATE_FOLDER',
|
||||||
|
"Suppression du dossier privé",
|
||||||
|
$path
|
||||||
|
);
|
||||||
rrmdir($path);
|
rrmdir($path);
|
||||||
$_SESSION['success_message'] = "Dossier privé supprimé avec succès.";
|
$_SESSION['success_message'] = "Dossier privé supprimé avec succès.";
|
||||||
}
|
}
|
||||||
@ -113,7 +137,7 @@ if (!isSecurePrivatePath($currentPath)) {
|
|||||||
|
|
||||||
// Créer le dossier racine s'il n'existe pas
|
// Créer le dossier racine s'il n'existe pas
|
||||||
if (!file_exists('./liste_albums_prives')) {
|
if (!file_exists('./liste_albums_prives')) {
|
||||||
mkdir('./liste_albums_prives', 0755, true);
|
mkdir('./liste_albums_prives', 0775, true);
|
||||||
// Créer le fichier infos.txt pour le dossier racine
|
// Créer le fichier infos.txt pour le dossier racine
|
||||||
$infoContent = "Albums privés\nVos albums photos privés\n18-\n";
|
$infoContent = "Albums privés\nVos albums photos privés\n18-\n";
|
||||||
file_put_contents('./liste_albums_prives/infos.txt', $infoContent);
|
file_put_contents('./liste_albums_prives/infos.txt', $infoContent);
|
||||||
@ -174,9 +198,9 @@ function generatePrivateTree($path, $currentPath) {
|
|||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
|
$output .= '<a href="arbre-img-prive.php?path=' . urlencode($fullPath) . '&private=1" class="tree-button" style="text-decoration: none">🖼️</a>';
|
||||||
if ($hasImages) {
|
if ($hasImages) {
|
||||||
$output .= '<button onclick="generateShareLink(\'' . htmlspecialchars($fullPath) . '\', \''
|
$encodedPath = htmlspecialchars(addslashes($fullPath));
|
||||||
. htmlspecialchars($info['title'])
|
$encodedTitle = htmlspecialchars(addslashes($info['title']));
|
||||||
. '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
$output .= '<button onclick="generateShareLink(\'' . $encodedPath . '\', \'' . $encodedTitle . '\')" class="tree-button tree-button-share" title="Générer un lien de partage">🔗</button>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$hasSubfolders) {
|
if (!$hasSubfolders) {
|
||||||
|
20
arbre.php
20
arbre.php
@ -21,13 +21,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$newPath = $path . '/' . sanitizeFilename($newName);
|
$newPath = $path . '/' . sanitizeFilename($newName);
|
||||||
if (!file_exists($newPath)) {
|
if (!file_exists($newPath)) {
|
||||||
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
$moreInfoUrl = $_POST['more_info_url'] ?? '';
|
||||||
mkdir($newPath, 0755, true);
|
mkdir($newPath, 0775, true);
|
||||||
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
$infoContent = $newName . "\n" . $description . "\n" . $matureContent . "\n" . $moreInfoUrl;
|
||||||
file_put_contents($newPath . '/infos.txt', $infoContent);
|
file_put_contents($newPath . '/infos.txt', $infoContent);
|
||||||
$_SESSION['success_message'] = "Dossier créé avec succès.";
|
$_SESSION['success_message'] = "Dossier créé avec succès.";
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
$_SESSION['error_message'] = "Ce dossier existe déjà.";
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'CREATE_FOLDER',
|
||||||
|
"Création du dossier : " . $newName,
|
||||||
|
$newPath
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -41,6 +47,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
$_SESSION['error_message'] = "Erreur lors de la modification du dossier.";
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'EDIT_FOLDER',
|
||||||
|
"Modification du dossier : " . $newName,
|
||||||
|
$path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -61,6 +73,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'DELETE_FOLDER',
|
||||||
|
"Suppression du dossier",
|
||||||
|
$path
|
||||||
|
);
|
||||||
rrmdir($path);
|
rrmdir($path);
|
||||||
$_SESSION['success_message'] = "Dossier supprimé avec succès.";
|
$_SESSION['success_message'] = "Dossier supprimé avec succès.";
|
||||||
}
|
}
|
||||||
|
15
clefs.php
15
clefs.php
@ -34,6 +34,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$successMessage = "Clé supprimée avec succès.";
|
$successMessage = "Clé supprimée avec succès.";
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'DELETE_SHARE_KEY',
|
||||||
|
"Suppression d'une clé de partage",
|
||||||
|
"ID: " . $keyId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$errorMessage = "Erreur lors de la suppression de la clé.";
|
$errorMessage = "Erreur lors de la suppression de la clé.";
|
||||||
}
|
}
|
||||||
@ -44,6 +50,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$deletedCount = cleanExpiredShareKeys();
|
$deletedCount = cleanExpiredShareKeys();
|
||||||
if ($deletedCount > 0) {
|
if ($deletedCount > 0) {
|
||||||
$successMessage = "$deletedCount clé(s) expirée(s) supprimée(s).";
|
$successMessage = "$deletedCount clé(s) expirée(s) supprimée(s).";
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'CLEAN_EXPIRED_KEYS',
|
||||||
|
"Nettoyage de $deletedCount clé(s) de partage expirée(s)"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$successMessage = "Aucune clé expirée à supprimer.";
|
$successMessage = "Aucune clé expirée à supprimer.";
|
||||||
}
|
}
|
||||||
@ -122,7 +133,7 @@ $config = getSiteConfig();
|
|||||||
|
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label for="status-filter">Statut :</label>
|
<label for="status-filter">Statut :</label>
|
||||||
<select id="status-filter" class="form-select" onchange="updateFilters()">
|
<select id="status-filter" class="form-select" onchange="updateFilters()">
|
||||||
<option value="active" <?php echo $filter === 'active' ? 'selected' : ''; ?>>Clés actives</option>
|
<option value="active" <?php echo $filter === 'active' ? 'selected' : ''; ?>>Clés actives</option>
|
||||||
<option value="expired" <?php echo $filter === 'expired' ? 'selected' : ''; ?>>Clés expirées</option>
|
<option value="expired" <?php echo $filter === 'expired' ? 'selected' : ''; ?>>Clés expirées</option>
|
||||||
@ -131,7 +142,7 @@ $config = getSiteConfig();
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label for="album-filter">Album :</label>
|
<label for="album-filter">Album :</label>
|
||||||
<select id="album-filter" class="form-select" onchange="updateFilters()">
|
<select id="album-filter" class="form-select" onchange="updateFilters()">
|
||||||
<option value="">Tous les albums</option>
|
<option value="">Tous les albums</option>
|
||||||
<?php foreach ($albums as $album): ?>
|
<?php foreach ($albums as $album): ?>
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
ICO
|
ICO
|
||||||
ICO est la galerie d'images de l'association Camélia Studio.
|
ICO est la galerie d'images de l'association Camélia Studio.
|
||||||
|
test-ico
|
BIN
favicon.png
BIN
favicon.png
Binary file not shown.
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 92 KiB |
@ -1,6 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
// Configuration
|
// 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']);
|
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif']);
|
||||||
|
|
||||||
// Configuration de la durée de session
|
// Configuration de la durée de session
|
||||||
@ -124,15 +139,22 @@ function getImagesRecursively($albumPath, $limit = 4) {
|
|||||||
if ($file->isFile()) {
|
if ($file->isFile()) {
|
||||||
$extension = strtolower($file->getExtension());
|
$extension = strtolower($file->getExtension());
|
||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
|
// Récupérer les infos du dossier parent de l'image
|
||||||
|
$parentDir = dirname($file->getPathname());
|
||||||
|
$parentInfo = getAlbumInfo($parentDir);
|
||||||
|
|
||||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||||
$images[] = $baseUrl . '/' . ltrim($relativePath, '/');
|
$images[] = [
|
||||||
|
'url' => $baseUrl . '/' . ltrim($relativePath, '/'),
|
||||||
|
'is_mature' => $parentInfo['mature_content']
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($images, function($a, $b) {
|
usort($images, function($a, $b) {
|
||||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a['url']);
|
||||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b['url']);
|
||||||
return filectime($pathB) - filectime($pathA);
|
return filectime($pathB) - filectime($pathA);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -184,7 +206,7 @@ function isSecurePath($path) {
|
|||||||
|
|
||||||
return $realPath && (
|
return $realPath && (
|
||||||
(strpos($realPath, $rootPath) === 0) ||
|
(strpos($realPath, $rootPath) === 0) ||
|
||||||
($carouselPath && $realPath === $carouselPath)
|
($carouselPath && strpos($realPath, $carouselPath) === 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,6 +386,34 @@ function cleanExpiredShareKeys() {
|
|||||||
return $db->changes();
|
return $db->changes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistre une action d'administrateur dans les logs
|
||||||
|
*/
|
||||||
|
function logAdminAction($adminId, $actionType, $description, $targetPath = null) {
|
||||||
|
$db = new SQLite3('database.sqlite');
|
||||||
|
$stmt = $db->prepare('INSERT INTO admin_logs (admin_id, action_type, action_description, target_path)
|
||||||
|
VALUES (:admin_id, :action_type, :description, :target_path)');
|
||||||
|
$stmt->bindValue(':admin_id', $adminId, SQLITE3_INTEGER);
|
||||||
|
$stmt->bindValue(':action_type', $actionType, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':description', $description, SQLITE3_TEXT);
|
||||||
|
$stmt->bindValue(':target_path', $targetPath, SQLITE3_TEXT);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le nom d'utilisateur d'un admin
|
||||||
|
*/
|
||||||
|
function getAdminUsername($adminId) {
|
||||||
|
$db = new SQLite3('database.sqlite');
|
||||||
|
$stmt = $db->prepare('SELECT username FROM admins WHERE id = :id');
|
||||||
|
$stmt->bindValue(':id', $adminId, SQLITE3_INTEGER);
|
||||||
|
$result = $stmt->execute();
|
||||||
|
if ($row = $result->fetchArray()) {
|
||||||
|
return $row['username'];
|
||||||
|
}
|
||||||
|
return 'Inconnu';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère la version actuelle du projet
|
* Récupère la version actuelle du projet
|
||||||
* @return string La version du projet
|
* @return string La version du projet
|
||||||
@ -380,7 +430,8 @@ function getSiteConfig() {
|
|||||||
$configFile = './config.txt';
|
$configFile = './config.txt';
|
||||||
$config = [
|
$config = [
|
||||||
'site_title' => 'ICO',
|
'site_title' => 'ICO',
|
||||||
'site_description' => 'ICO est la galerie d\'images de l\'association Camélia Studio.'
|
'site_description' => 'ICO est la galerie d\'images de l\'association Camélia Studio.',
|
||||||
|
'project_path' => PROJECT_ROOT_DIR
|
||||||
];
|
];
|
||||||
|
|
||||||
if (file_exists($configFile)) {
|
if (file_exists($configFile)) {
|
||||||
@ -388,6 +439,7 @@ function getSiteConfig() {
|
|||||||
$lines = explode("\n", $content);
|
$lines = explode("\n", $content);
|
||||||
if (isset($lines[0])) $config['site_title'] = trim($lines[0]);
|
if (isset($lines[0])) $config['site_title'] = trim($lines[0]);
|
||||||
if (isset($lines[1])) $config['site_description'] = trim($lines[1]);
|
if (isset($lines[1])) $config['site_description'] = trim($lines[1]);
|
||||||
|
if (isset($lines[2])) $config['project_path'] = trim($lines[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
@ -34,7 +34,7 @@ if (empty($shareKey)) {
|
|||||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||||
// Obtenir le chemin relatif depuis la racine du projet
|
// Obtenir le chemin relatif depuis la racine du projet
|
||||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||||
$url = $baseUrl . '/' . ltrim($relativePath, '/');
|
$url = $baseUrl . '/images.php?path=' . urlencode($file->getPathname()) . '&key=' . urlencode($shareKey);
|
||||||
// Vérifier que le fichier existe et est accessible
|
// Vérifier que le fichier existe et est accessible
|
||||||
if (file_exists($file->getPathname())) {
|
if (file_exists($file->getPathname())) {
|
||||||
$images[] = $url;
|
$images[] = $url;
|
||||||
@ -148,7 +148,7 @@ $config = getSiteConfig();
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="gallery-item <?php echo $isTop ? 'gallery-item-top' : ''; ?> <?php echo $spanClass; ?>">
|
<div class="gallery-item <?php echo $isTop ? 'gallery-item-top' : ''; ?> <?php echo $spanClass; ?>">
|
||||||
<a href="partage.php?image=<?php echo urlencode($image); ?>" target="_blank">
|
<a href="partage.php?image=<?php echo urlencode($image); ?>&key=<?php echo urlencode($shareKey); ?>" target="_blank">
|
||||||
<img src="<?php echo htmlspecialchars($image); ?>"
|
<img src="<?php echo htmlspecialchars($image); ?>"
|
||||||
alt="Image de la galerie"
|
alt="Image de la galerie"
|
||||||
loading="lazy">
|
loading="lazy">
|
||||||
|
19
galeries.php
19
galeries.php
@ -133,15 +133,16 @@ $config = getSiteConfig();
|
|||||||
<script>
|
<script>
|
||||||
// Gestion du contenu mature
|
// Gestion du contenu mature
|
||||||
function acceptMatureContent() {
|
function acceptMatureContent() {
|
||||||
document.body.classList.remove('content-blurred');
|
document.body.classList.remove('gallery-page-mature');
|
||||||
const warning = document.getElementById('mature-warning');
|
document.body.classList.remove('content-blurred');
|
||||||
if (warning) {
|
const warning = document.getElementById('mature-warning');
|
||||||
warning.style.opacity = '0';
|
if (warning) {
|
||||||
setTimeout(() => {
|
warning.style.opacity = '0';
|
||||||
warning.style.display = 'none';
|
setTimeout(() => {
|
||||||
}, 300);
|
warning.style.display = 'none';
|
||||||
}
|
}, 300);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<button class="scroll-top" title="Retour en haut">↑</button>
|
<button class="scroll-top" title="Retour en haut">↑</button>
|
||||||
<script>
|
<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
|
// Vérifier si le dossier existe
|
||||||
if (!is_dir($carouselDir)) {
|
if (!is_dir($carouselDir)) {
|
||||||
// Créer le dossier s'il n'existe pas
|
// Créer le dossier s'il n'existe pas
|
||||||
mkdir($carouselDir, 0755, true);
|
mkdir($carouselDir, 0775, true);
|
||||||
return $images;
|
return $images;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +44,7 @@ $config = getSiteConfig();
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<a href="admin.php" class="admin-button" title="Administration">⚙️</a>
|
||||||
<div class="carousel">
|
<div class="carousel">
|
||||||
<?php foreach($carouselImages as $index => $image): ?>
|
<?php foreach($carouselImages as $index => $image): ?>
|
||||||
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
||||||
|
13
init-db.php
13
init-db.php
@ -47,10 +47,23 @@ if ($count === 0) {
|
|||||||
echo "Admin par défaut créé (username: admin, password: admin). Pensez à changer ces identifiants !";
|
echo "Admin par défaut créé (username: admin, password: admin). Pensez à changer ces identifiants !";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Après la création des tables existantes, ajouter :
|
||||||
|
$db->exec('CREATE TABLE IF NOT EXISTS admin_logs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
admin_id INTEGER NOT NULL,
|
||||||
|
action_type TEXT NOT NULL,
|
||||||
|
action_description TEXT NOT NULL,
|
||||||
|
target_path TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (admin_id) REFERENCES admins(id)
|
||||||
|
)');
|
||||||
|
|
||||||
// Créer les index nécessaires
|
// Créer les index nécessaires
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_expires_at ON share_keys(expires_at)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_expires_at ON share_keys(expires_at)');
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_album_identifier ON share_keys(album_identifier)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_share_keys_album_identifier ON share_keys(album_identifier)');
|
||||||
$db->exec('CREATE INDEX IF NOT EXISTS idx_album_identifiers_identifier ON album_identifiers(identifier)');
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_album_identifiers_identifier ON album_identifiers(identifier)');
|
||||||
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_admin_logs_admin_id ON admin_logs(admin_id)');
|
||||||
|
$db->exec('CREATE INDEX IF NOT EXISTS idx_admin_logs_created_at ON admin_logs(created_at)');
|
||||||
|
|
||||||
$db->close();
|
$db->close();
|
||||||
echo "Base de données initialisée avec succès !";
|
echo "Base de données initialisée avec succès !";
|
||||||
|
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
|
// Vérifier que nous avons une URL d'image
|
||||||
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
||||||
|
|
||||||
|
if (!$imageUrl) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si c'est une image privée
|
||||||
|
if (strpos($imageUrl, 'images.php') !== false) {
|
||||||
|
// On récupère les paramètres de l'URL de l'image
|
||||||
|
parse_str(parse_url($imageUrl, PHP_URL_QUERY), $params);
|
||||||
|
$path = $params['path'] ?? '';
|
||||||
|
$key = $params['key'] ?? '';
|
||||||
|
|
||||||
|
if (strpos($path, 'liste_albums_prives') !== false) {
|
||||||
|
$isPrivateImage = true;
|
||||||
|
if (!isset($_SESSION['admin_id'])) {
|
||||||
|
if (!$key || !validateShareKey($key)) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} elseif (isset($_SESSION['admin_id'])) {
|
||||||
|
// Pour les admins, on remplace la clé par la session admin
|
||||||
|
$imageUrl = preg_replace('/&key=[^&]*/', '', $imageUrl) . '&admin_session=' . session_id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupérer le nom du fichier pour le téléchargement
|
||||||
|
$filename = basename(parse_url($imageUrl, PHP_URL_PATH));
|
||||||
|
|
||||||
// Si pas d'image, redirection
|
// Si pas d'image, redirection
|
||||||
if (!$imageUrl) {
|
if (!$imageUrl) {
|
||||||
header('Location: index.php');
|
header('Location: index.php');
|
||||||
@ -25,7 +54,7 @@ $config = getSiteConfig();
|
|||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="share-page">
|
<body class="share-page">
|
||||||
<button onclick="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-container">
|
||||||
<div class="share-image">
|
<div class="share-image">
|
||||||
@ -42,6 +71,7 @@ $config = getSiteConfig();
|
|||||||
Partager
|
Partager
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<?php if (!$isPrivateImage): ?>
|
||||||
<button class="action-button" onclick="embedImage()">
|
<button class="action-button" onclick="embedImage()">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<polyline points="16 18 22 12 16 6"></polyline>
|
<polyline points="16 18 22 12 16 6"></polyline>
|
||||||
@ -49,6 +79,7 @@ $config = getSiteConfig();
|
|||||||
</svg>
|
</svg>
|
||||||
Intégrer
|
Intégrer
|
||||||
</button>
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
||||||
download="<?php echo htmlspecialchars($filename); ?>"
|
download="<?php echo htmlspecialchars($filename); ?>"
|
||||||
@ -60,6 +91,18 @@ $config = getSiteConfig();
|
|||||||
</svg>
|
</svg>
|
||||||
Télécharger
|
Télécharger
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="https://saucenao.com/search.php?url=<?php echo urlencode($imageUrl); ?>"
|
||||||
|
target="_blank"
|
||||||
|
class="action-button">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<circle cx="11" cy="11" r="8"></circle>
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||||
|
<line x1="11" y1="8" x2="11" y2="14"></line>
|
||||||
|
<line x1="8" y1="11" x2="14" y2="11"></line>
|
||||||
|
</svg>
|
||||||
|
Source ?
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -12,16 +12,22 @@ checkAdminSession();
|
|||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$siteTitle = $_POST['site_title'] ?? '';
|
$siteTitle = $_POST['site_title'] ?? '';
|
||||||
$siteDescription = $_POST['site_description'] ?? '';
|
$siteDescription = $_POST['site_description'] ?? '';
|
||||||
|
$projectPath = $_POST['project_path'] ?? '';
|
||||||
|
|
||||||
// Vérifications basiques
|
// Vérifications basiques
|
||||||
if (empty($siteTitle)) {
|
if (empty($siteTitle)) {
|
||||||
$_SESSION['error_message'] = "Le titre du site est requis.";
|
$_SESSION['error_message'] = "Le titre du site est requis.";
|
||||||
} else {
|
} else {
|
||||||
// Sauvegarder la configuration
|
// Sauvegarder la configuration
|
||||||
$configContent = $siteTitle . "\n" . $siteDescription;
|
$configContent = $siteTitle . "\n" . $siteDescription . "\n" . $projectPath;
|
||||||
|
|
||||||
if (file_put_contents('./config.txt', $configContent) !== false) {
|
if (file_put_contents('./config.txt', $configContent) !== false) {
|
||||||
$_SESSION['success_message'] = "Configuration mise à jour avec succès.";
|
$_SESSION['success_message'] = "Configuration mise à jour avec succès.";
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'UPDATE_SETTINGS',
|
||||||
|
"Modification des paramètres du site"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la sauvegarde de la configuration.";
|
$_SESSION['error_message'] = "Erreur lors de la sauvegarde de la configuration.";
|
||||||
}
|
}
|
||||||
@ -71,13 +77,19 @@ $config = getSiteConfig();
|
|||||||
value="<?php echo htmlspecialchars($config['site_title']); ?>">
|
value="<?php echo htmlspecialchars($config['site_title']); ?>">
|
||||||
<small class="form-help">Ce titre apparaîtra dans l'en-tête des pages et la barre de titre du navigateur.</small>
|
<small class="form-help">Ce titre apparaîtra dans l'en-tête des pages et la barre de titre du navigateur.</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="site_description">Description du site :</label>
|
<label for="site_description">Description du site :</label>
|
||||||
<textarea id="site_description" name="site_description" rows="4"
|
<textarea id="site_description" name="site_description" rows="4"
|
||||||
class="form-textarea"><?php echo htmlspecialchars($config['site_description']); ?></textarea>
|
class="form-textarea"><?php echo htmlspecialchars($config['site_description']); ?></textarea>
|
||||||
<small class="form-help">Cette description apparaît sur la page d'accueil du site.</small>
|
<small class="form-help">Cette description apparaît sur la page d'accueil du site.</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="project_path">Chemin d'installation :</label>
|
||||||
|
<input type="text" id="project_path" name="project_path"
|
||||||
|
value="<?php echo htmlspecialchars($config['project_path']); ?>" required>
|
||||||
|
<small class="form-help">Ce chemin correspond au dossier dans lequel ICO est installé sur votre serveur web.
|
||||||
|
Par exemple, si ICO est accessible via "www.monsite.com/ico", le chemin sera "ico".</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="action-button">Enregistrer les modifications</button>
|
<button type="submit" class="action-button">Enregistrer les modifications</button>
|
||||||
|
@ -1019,6 +1019,58 @@ body[data-page="carrousel"] .admin-header {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Styles pour les logs */
|
||||||
|
.filter-form {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-top: 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-link {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-link:hover {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-link.active {
|
||||||
|
background-color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-list {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styles pour les différents types d'actions */
|
||||||
|
.admin-table tr td:nth-child(3) {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tr .log-action-create {
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tr .log-action-edit {
|
||||||
|
color: #FFA726;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table tr .log-action-delete {
|
||||||
|
color: #EF5350;
|
||||||
|
}
|
||||||
|
|
||||||
/* Media Queries */
|
/* Media Queries */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.admin-page {
|
.admin-page {
|
||||||
|
70
styles.css
70
styles.css
@ -15,6 +15,32 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Styles de la page d'accueil */
|
/* Styles de la page d'accueil */
|
||||||
|
.admin-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-button:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
.carousel {
|
.carousel {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -160,9 +186,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Styles pour le contenu mature dans les albums */
|
/* Styles pour le contenu mature dans les albums */
|
||||||
|
.album-card-mature {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.album-card-mature .album-images {
|
.album-card-mature .album-images {
|
||||||
filter: blur(10px);
|
filter: blur(10px);
|
||||||
transition: filter 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card-mature::before {
|
.album-card-mature::before {
|
||||||
@ -177,8 +206,12 @@ body {
|
|||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
opacity: 1;
|
transition: transform 0.3s ease, background-color 0.3s ease;
|
||||||
transition: opacity 0.3s ease;
|
}
|
||||||
|
|
||||||
|
.album-card-mature:hover::before {
|
||||||
|
transform: translate(-50%, -50%) scale(1.1);
|
||||||
|
background-color: rgba(220, 53, 69, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card-mature::after {
|
.album-card-mature::after {
|
||||||
@ -190,12 +223,34 @@ body {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.album-card-mature:hover .album-images {
|
.image-background {
|
||||||
filter: blur(0);
|
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 {
|
.image-background.mature-preview {
|
||||||
opacity: 0;
|
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 {
|
.album-card {
|
||||||
@ -231,6 +286,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.album-image {
|
.album-image {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
116
utilisateurs.php
116
utilisateurs.php
@ -83,49 +83,87 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$_SESSION['success_message'] = "Utilisateur ajouté avec succès.";
|
$_SESSION['success_message'] = "Utilisateur ajouté avec succès.";
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'ADD_USER',
|
||||||
|
"Création du compte administrateur : " . $username
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de l'ajout de l'utilisateur.";
|
$_SESSION['error_message'] = "Erreur lors de l'ajout de l'utilisateur.";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
$userId = $_POST['user_id'] ?? '';
|
$userId = $_POST['user_id'] ?? '';
|
||||||
$username = $_POST['username'] ?? '';
|
$username = $_POST['username'] ?? '';
|
||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if (empty($userId) || empty($username)) {
|
||||||
|
$_SESSION['error_message'] = "Des informations sont manquantes.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($userId) || empty($username)) {
|
// Vérifier que le nouvel identifiant n'existe pas déjà (sauf pour l'utilisateur actuel)
|
||||||
$_SESSION['error_message'] = "Des informations sont manquantes.";
|
$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;
|
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':
|
case 'delete':
|
||||||
$userId = $_POST['user_id'] ?? '';
|
$userId = $_POST['user_id'] ?? '';
|
||||||
@ -150,6 +188,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$_SESSION['success_message'] = "Utilisateur supprimé avec succès.";
|
$_SESSION['success_message'] = "Utilisateur supprimé avec succès.";
|
||||||
|
logAdminAction(
|
||||||
|
$_SESSION['admin_id'],
|
||||||
|
'DELETE_USER',
|
||||||
|
"Suppression d'un compte administrateur",
|
||||||
|
"ID: " . $userId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error_message'] = "Erreur lors de la suppression de l'utilisateur.";
|
$_SESSION['error_message'] = "Erreur lors de la suppression de l'utilisateur.";
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
1.0.8
|
1.1.2
|
Loading…
x
Reference in New Issue
Block a user