ajout de la partie publique
This commit is contained in:
parent
c6af349844
commit
bd1bb9bb36
100
albums.php
Normal file
100
albums.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
require_once 'fonctions.php';
|
||||
session_start();
|
||||
|
||||
// Récupérer le chemin actuel depuis l'URL
|
||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums';
|
||||
$currentPath = realpath($currentPath);
|
||||
|
||||
// Vérification de sécurité
|
||||
if (!isSecurePath($currentPath)) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Récupérer tous les sous-dossiers
|
||||
$albums = [];
|
||||
foreach (new DirectoryIterator($currentPath) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isDir()) {
|
||||
$albumPath = $item->getPathname();
|
||||
$info = getAlbumInfo($albumPath);
|
||||
|
||||
$images = hasSubfolders($albumPath) ?
|
||||
getImagesRecursively($albumPath) :
|
||||
getLatestImages($albumPath);
|
||||
|
||||
$albums[] = [
|
||||
'path' => str_replace('\\', '/', $albumPath),
|
||||
'title' => $info['title'],
|
||||
'description' => $info['description'],
|
||||
'images' => $images,
|
||||
'hasSubfolders' => hasSubfolders($albumPath),
|
||||
'hasImages' => hasImages($albumPath)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Déterminer le chemin parent pour le bouton retour
|
||||
$parentPath = dirname($currentPath);
|
||||
if (!isSecurePath($parentPath)) {
|
||||
$parentPath = null;
|
||||
}
|
||||
?><!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Albums - ICO</title>
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="albums-page">
|
||||
<?php if ($parentPath): ?>
|
||||
<a href="?path=<?php echo urlencode($parentPath); ?>" class="back-button">Retour</a>
|
||||
<?php else: ?>
|
||||
<a href="index.php" class="back-button">Retour</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="albums-grid">
|
||||
<?php foreach ($albums as $album): ?>
|
||||
<a href="<?php echo $album['hasSubfolders'] ? 'albums.php' : 'galeries.php'; ?>?path=<?php echo urlencode($album['path']); ?>"
|
||||
class="album-card">
|
||||
<div class="album-images">
|
||||
<?php if (empty($album['images'])): ?>
|
||||
<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>
|
||||
<?php endforeach; ?>
|
||||
<?php for ($i = count($album['images']); $i < 4; $i++): ?>
|
||||
<div class="empty-image"></div>
|
||||
<?php endfor; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="album-info">
|
||||
<h2><?php echo htmlspecialchars($album['title']); ?></h2>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('visible');
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
document.querySelectorAll('.album-card').forEach(card => {
|
||||
observer.observe(card);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
BIN
favicon.png
Normal file
BIN
favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
204
fonctions.php
Normal file
204
fonctions.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
// Configuration
|
||||
define('PROJECT_ROOT_DIR', 'test-ico');
|
||||
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif']);
|
||||
|
||||
/**
|
||||
* Obtient l'URL de base du site
|
||||
* @return string L'URL de base du site
|
||||
*/
|
||||
function getBaseUrl() {
|
||||
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
|
||||
return $protocol . $_SERVER['HTTP_HOST'] . '/' . PROJECT_ROOT_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les informations d'un album depuis son fichier infos.txt
|
||||
* @param string $albumPath Chemin vers l'album
|
||||
* @return array Tableau contenant le titre et la description de l'album
|
||||
*/
|
||||
function getAlbumInfo($albumPath) {
|
||||
$infoFile = $albumPath . '/infos.txt';
|
||||
$info = [
|
||||
'title' => basename($albumPath),
|
||||
'description' => ''
|
||||
];
|
||||
|
||||
if (file_exists($infoFile)) {
|
||||
$content = file_get_contents($infoFile);
|
||||
$lines = explode("\n", $content);
|
||||
if (isset($lines[0])) $info['title'] = trim($lines[0]);
|
||||
if (isset($lines[1])) $info['description'] = trim($lines[1]);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les dernières images d'un dossier
|
||||
* @param string $albumPath Chemin vers l'album
|
||||
* @param int $limit Nombre d'images à récupérer
|
||||
* @return array Tableau des URLs des images
|
||||
*/
|
||||
function getLatestImages($albumPath, $limit = 4) {
|
||||
$images = [];
|
||||
$baseUrl = getBaseUrl();
|
||||
|
||||
if (!is_dir($albumPath)) return $images;
|
||||
|
||||
foreach (new DirectoryIterator($albumPath) as $file) {
|
||||
if ($file->isDot()) continue;
|
||||
if ($file->isFile()) {
|
||||
$extension = strtolower($file->getExtension());
|
||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||
$images[] = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($images, function($a, $b) {
|
||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
||||
return filectime($pathB) - filectime($pathA);
|
||||
});
|
||||
|
||||
return array_slice($images, 0, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les images de manière récursive dans tous les sous-dossiers
|
||||
* @param string $albumPath Chemin vers l'album
|
||||
* @param int $limit Nombre d'images à récupérer
|
||||
* @return array Tableau des URLs des images
|
||||
*/
|
||||
function getImagesRecursively($albumPath, $limit = 4) {
|
||||
$images = [];
|
||||
$baseUrl = getBaseUrl();
|
||||
|
||||
if (!is_dir($albumPath)) return $images;
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($albumPath),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$extension = strtolower($file->getExtension());
|
||||
if (in_array($extension, ALLOWED_EXTENSIONS)) {
|
||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||
$images[] = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($images, function($a, $b) {
|
||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
||||
return filectime($pathB) - filectime($pathA);
|
||||
});
|
||||
|
||||
return array_slice($images, 0, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un dossier contient des sous-dossiers
|
||||
* @param string $path Chemin du dossier
|
||||
* @return bool True si le dossier contient des sous-dossiers
|
||||
*/
|
||||
function hasSubfolders($path) {
|
||||
if (!is_dir($path)) return false;
|
||||
|
||||
foreach (new DirectoryIterator($path) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isDir()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un dossier contient des images
|
||||
* @param string $path Chemin du dossier
|
||||
* @return bool True si le dossier contient des images
|
||||
*/
|
||||
function hasImages($path) {
|
||||
if (!is_dir($path)) return false;
|
||||
|
||||
foreach (new DirectoryIterator($path) as $item) {
|
||||
if ($item->isDot()) continue;
|
||||
if ($item->isFile()) {
|
||||
$extension = strtolower($item->getExtension());
|
||||
if (in_array($extension, ALLOWED_EXTENSIONS)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le chemin est sécurisé (dans le dossier liste_albums)
|
||||
* @param string $path Chemin à vérifier
|
||||
* @return bool True si le chemin est sécurisé
|
||||
*/
|
||||
function isSecurePath($path) {
|
||||
$realPath = realpath($path);
|
||||
$rootPath = realpath('./liste_albums');
|
||||
return $realPath && strpos($realPath, $rootPath) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formate la taille d'un fichier en format lisible
|
||||
* @param int $bytes Taille en octets
|
||||
* @return string Taille formatée (ex: "1.2 MB")
|
||||
*/
|
||||
function formatFileSize($bytes) {
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
$bytes /= pow(1024, $pow);
|
||||
return round($bytes, 1) . ' ' . $units[$pow];
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les dimensions d'une image de manière sécurisée
|
||||
* @param string $path Chemin vers l'image
|
||||
* @return array|false Tableau contenant width et height ou false en cas d'erreur
|
||||
*/
|
||||
function getSecureImageSize($path) {
|
||||
try {
|
||||
if (!file_exists($path)) return false;
|
||||
$imageInfo = getimagesize($path);
|
||||
if ($imageInfo === false) return false;
|
||||
return [
|
||||
'width' => $imageInfo[0],
|
||||
'height' => $imageInfo[1]
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère un identifiant unique sécurisé
|
||||
* @param int $length Longueur de l'identifiant
|
||||
* @return string Identifiant unique
|
||||
*/
|
||||
function generateSecureId($length = 32) {
|
||||
return bin2hex(random_bytes($length / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoie et sécurise un nom de fichier
|
||||
* @param string $filename Nom du fichier à nettoyer
|
||||
* @return string Nom de fichier sécurisé
|
||||
*/
|
||||
function sanitizeFilename($filename) {
|
||||
// Supprime les caractères spéciaux
|
||||
$filename = preg_replace('/[^a-zA-Z0-9._-]/', '-', $filename);
|
||||
// Évite les noms de fichiers commençant par un point
|
||||
$filename = ltrim($filename, '.');
|
||||
// Limite la longueur du nom de fichier
|
||||
return substr($filename, 0, 255);
|
||||
}
|
||||
?>
|
110
galeries.php
Normal file
110
galeries.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
require_once 'fonctions.php';
|
||||
session_start();
|
||||
|
||||
// Récupérer le chemin actuel depuis l'URL
|
||||
$currentPath = isset($_GET['path']) ? $_GET['path'] : './liste_albums';
|
||||
$currentPath = realpath($currentPath);
|
||||
|
||||
// Vérification de sécurité
|
||||
if (!isSecurePath($currentPath)) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$albumInfo = getAlbumInfo($currentPath);
|
||||
$images = [];
|
||||
$baseUrl = getBaseUrl();
|
||||
|
||||
foreach (new DirectoryIterator($currentPath) as $file) {
|
||||
if ($file->isDot()) continue;
|
||||
if ($file->isFile()) {
|
||||
$extension = strtolower($file->getExtension());
|
||||
if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
|
||||
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
|
||||
$url = $baseUrl . '/' . ltrim($relativePath, '/');
|
||||
$images[] = $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($images, function($a, $b) {
|
||||
$pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
|
||||
$pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
|
||||
return filectime($pathB) - filectime($pathA);
|
||||
});
|
||||
|
||||
$headerImage = !empty($images) ? $images[0] : null;
|
||||
|
||||
$parentPath = dirname($currentPath);
|
||||
if (!isSecurePath($parentPath)) {
|
||||
$parentPath = './liste_albums';
|
||||
}
|
||||
?><!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo htmlspecialchars($albumInfo['title']); ?> - ICO</title>
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="gallery-page">
|
||||
<a href="albums.php?path=<?php echo urlencode($parentPath); ?>" class="back-button">Retour</a>
|
||||
|
||||
<?php if ($headerImage): ?>
|
||||
<div class="gallery-header">
|
||||
<img src="<?php echo htmlspecialchars($headerImage); ?>" alt="Image principale" class="header-image">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="gallery-info">
|
||||
<h1><?php echo htmlspecialchars($albumInfo['title']); ?></h1>
|
||||
<?php if (!empty($albumInfo['description'])): ?>
|
||||
<p><?php echo nl2br(htmlspecialchars($albumInfo['description'])); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="gallery-grid" id="gallery-grid">
|
||||
<?php foreach($images as $image): ?>
|
||||
<div class="gallery-item">
|
||||
<a href="partage.php?image=<?php echo urlencode($image); ?>">
|
||||
<img src="<?php echo htmlspecialchars($image); ?>" alt="Image de la galerie" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
|
||||
<script src="https://unpkg.com/imagesloaded@5/imagesloaded.pkgd.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const grid = document.querySelector('#gallery-grid');
|
||||
const loadingClass = 'is-loading';
|
||||
|
||||
// Ajouter une classe de chargement
|
||||
grid.classList.add(loadingClass);
|
||||
|
||||
// Initialiser Masonry avec imagesLoaded
|
||||
imagesLoaded(grid, function() {
|
||||
const masonry = new Masonry(grid, {
|
||||
itemSelector: '.gallery-item',
|
||||
columnWidth: '.gallery-item',
|
||||
gutter: 20,
|
||||
fitWidth: true,
|
||||
transitionDuration: 0 // Désactiver l'animation pour le premier rendu
|
||||
});
|
||||
|
||||
// Retirer la classe de chargement
|
||||
grid.classList.remove(loadingClass);
|
||||
|
||||
// Réactiver les animations après le premier rendu
|
||||
setTimeout(() => {
|
||||
masonry.options.transitionDuration = '0.3s';
|
||||
masonry.layout();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
79
index.php
Normal file
79
index.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
// Fonction pour récupérer les 5 dernières images
|
||||
function getLatestImages($rootDir = './liste_albums', $limit = 5) {
|
||||
$images = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($rootDir),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$extension = strtolower($file->getExtension());
|
||||
if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
|
||||
$images[] = str_replace('\\', '/', $file->getPathname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($images, function($a, $b) {
|
||||
return filectime($b) - filectime($a);
|
||||
});
|
||||
|
||||
return array_slice($images, 0, $limit);
|
||||
}
|
||||
|
||||
$latestImages = getLatestImages();
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ICO - Galerie d'images</title>
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="carousel">
|
||||
<?php foreach($latestImages as $index => $image): ?>
|
||||
<div class="carousel-slide <?php echo $index === 0 ? 'active' : ''; ?>">
|
||||
<img src="<?php echo htmlspecialchars($image); ?>" alt="Image de la galerie">
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="overlay">
|
||||
<h1>ICO</h1>
|
||||
<p>ICO est la galerie d'images de l'association Camélia Studio.</p>
|
||||
<a href="albums.php" class="cta-button">Accéder aux galeries</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
let currentSlide = 0;
|
||||
const slides = document.querySelectorAll('.carousel-slide');
|
||||
|
||||
function showSlide(index) {
|
||||
slides.forEach(slide => {
|
||||
slide.classList.remove('active');
|
||||
});
|
||||
|
||||
slides[index].classList.add('active');
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
currentSlide = (currentSlide + 1) % slides.length;
|
||||
showSlide(currentSlide);
|
||||
}
|
||||
|
||||
// Initialiser le premier slide
|
||||
showSlide(0);
|
||||
|
||||
// Changer de slide toutes les 5 secondes
|
||||
setInterval(nextSlide, 5000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
2
liste_albums/infos.txt
Normal file
2
liste_albums/infos.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Titre
|
||||
Description sur la seconde ligne.
|
200
partage.php
Normal file
200
partage.php
Normal file
@ -0,0 +1,200 @@
|
||||
<?php
|
||||
require_once 'fonctions.php';
|
||||
|
||||
// Vérifier que nous avons une URL d'image
|
||||
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
||||
|
||||
// Si pas d'image, redirection
|
||||
if (!$imageUrl) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Récupérer le nom du fichier pour le téléchargement
|
||||
$filename = basename($imageUrl);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Image - ICO</title>
|
||||
<link rel="icon" type="image/png" href="favicon.png">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="share-page">
|
||||
<div class="share-container">
|
||||
<div class="share-image">
|
||||
<img src="<?php echo htmlspecialchars($imageUrl); ?>" alt="Image partagée">
|
||||
</div>
|
||||
|
||||
<div class="share-actions">
|
||||
<button class="action-button" onclick="shareImage()">
|
||||
<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">
|
||||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
||||
<polyline points="16 6 12 2 8 6"></polyline>
|
||||
<line x1="12" y1="2" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
Partager
|
||||
</button>
|
||||
|
||||
<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>
|
||||
<polyline points="8 6 2 12 8 18"></polyline>
|
||||
</svg>
|
||||
Intégrer
|
||||
</button>
|
||||
|
||||
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
||||
download="<?php echo htmlspecialchars($filename); ?>"
|
||||
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">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="7 10 12 15 17 10"></polyline>
|
||||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||||
</svg>
|
||||
Télécharger
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text, button) {
|
||||
const input = document.createElement('input');
|
||||
input.value = text;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
|
||||
// Sauvegarder l'HTML original
|
||||
const originalHTML = button.innerHTML;
|
||||
|
||||
// Afficher la confirmation
|
||||
button.innerHTML = `
|
||||
<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">
|
||||
<path d="M20 6L9 17l-5-5"></path>
|
||||
</svg>
|
||||
Copié !
|
||||
`;
|
||||
|
||||
// Restaurer l'état original après 2 secondes
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function shareImage() {
|
||||
copyToClipboard(window.location.href, document.querySelector('.action-button'));
|
||||
}
|
||||
|
||||
function embedImage() {
|
||||
const imageUrl = '<?php echo htmlspecialchars($imageUrl); ?>';
|
||||
copyToClipboard(imageUrl, document.querySelector('.action-button:nth-child(2)'));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html><?php
|
||||
require_once 'functions.php';
|
||||
|
||||
// Vérifier que nous avons une URL d'image
|
||||
$imageUrl = isset($_GET['image']) ? $_GET['image'] : null;
|
||||
$returnUrl = isset($_GET['return']) ? $_GET['return'] : 'index.php';
|
||||
|
||||
// Si pas d'image, redirection
|
||||
if (!$imageUrl) {
|
||||
header('Location: ' . $returnUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Récupérer le nom du fichier pour le téléchargement
|
||||
$filename = basename($imageUrl);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Partager l'image - ICO</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="share-page">
|
||||
<!-- Remplacer le lien de retour par un bouton -->
|
||||
<button onclick="window.close();" class="back-button">Retour à la galerie</button>
|
||||
|
||||
<div class="share-container">
|
||||
<div class="share-image">
|
||||
<img src="<?php echo htmlspecialchars($imageUrl); ?>" alt="Image partagée">
|
||||
</div>
|
||||
|
||||
<div class="share-actions">
|
||||
<button class="action-button" onclick="shareImage()">
|
||||
<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">
|
||||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
||||
<polyline points="16 6 12 2 8 6"></polyline>
|
||||
<line x1="12" y1="2" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
Partager
|
||||
</button>
|
||||
|
||||
<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>
|
||||
<polyline points="8 6 2 12 8 18"></polyline>
|
||||
</svg>
|
||||
Intégrer
|
||||
</button>
|
||||
|
||||
<a href="<?php echo htmlspecialchars($imageUrl); ?>"
|
||||
download="<?php echo htmlspecialchars($filename); ?>"
|
||||
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">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="7 10 12 15 17 10"></polyline>
|
||||
<line x1="12" y1="15" x2="12" y2="3"></line>
|
||||
</svg>
|
||||
Télécharger
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text, button) {
|
||||
const input = document.createElement('input');
|
||||
input.value = text;
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(input);
|
||||
|
||||
// Sauvegarder l'HTML original
|
||||
const originalHTML = button.innerHTML;
|
||||
|
||||
// Afficher la confirmation
|
||||
button.innerHTML = `
|
||||
<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">
|
||||
<path d="M20 6L9 17l-5-5"></path>
|
||||
</svg>
|
||||
Copié !
|
||||
`;
|
||||
|
||||
// Restaurer l'état original après 2 secondes
|
||||
setTimeout(() => {
|
||||
button.innerHTML = originalHTML;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function shareImage() {
|
||||
copyToClipboard(window.location.href, document.querySelector('.action-button'));
|
||||
}
|
||||
|
||||
function embedImage() {
|
||||
const imageUrl = '<?php echo htmlspecialchars($imageUrl); ?>';
|
||||
copyToClipboard(imageUrl, document.querySelector('.action-button:nth-child(2)'));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
460
styles.css
Normal file
460
styles.css
Normal file
@ -0,0 +1,460 @@
|
||||
/* Accessibilité */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}/* Reset et styles de base */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', system-ui, sans-serif;
|
||||
background-color: #121212;
|
||||
color: #ffffff;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Styles de la page d'accueil */
|
||||
.carousel {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.carousel-slide {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: opacity 1s ease-in-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.carousel-slide.active {
|
||||
opacity: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.carousel-slide img {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
object-fit: cover;
|
||||
filter: brightness(0.6);
|
||||
}
|
||||
|
||||
/* Overlay contenu */
|
||||
.overlay {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
padding: 2rem;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 1rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 2rem;
|
||||
line-height: 1.6;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
display: inline-block;
|
||||
padding: 1rem 2rem;
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 2rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background-color: #1976d2;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);
|
||||
}
|
||||
|
||||
/* Styles pour la page des albums */
|
||||
.albums-page {
|
||||
background-color: #121212;
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.albums-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
max-width: 1800px;
|
||||
margin: 4rem auto 2rem auto;
|
||||
}
|
||||
|
||||
.album-card {
|
||||
background-color: #1e1e1e;
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||
}
|
||||
|
||||
.album-card.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.album-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.album-images {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: repeat(2, 1fr);
|
||||
aspect-ratio: 1;
|
||||
gap: 2px;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.album-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.empty-album {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #2a2a2a;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1 / -1;
|
||||
}
|
||||
|
||||
.album-info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.album-info h2 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Styles pour la page galerie */
|
||||
.gallery-page {
|
||||
background-color: #121212;
|
||||
min-height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gallery-header {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.gallery-info {
|
||||
padding: 2rem;
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
padding: 2rem;
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.gallery-grid.is-loading .gallery-item {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gallery-grid .gallery-item {
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
width: calc((100% - (7 * 20px)) / 8);
|
||||
min-width: 200px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #1e1e1e;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.gallery-item img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
min-height: 150px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Bouton retour commun */
|
||||
.back-button {
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
left: 2rem;
|
||||
padding: 0.8rem 1.5rem;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 2rem;
|
||||
backdrop-filter: blur(10px);
|
||||
z-index: 100;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
@media (min-width: 1400px) {
|
||||
.albums-grid {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
.gallery-item {
|
||||
width: calc((100% - (5 * 20px)) / 6);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.albums-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
.gallery-item {
|
||||
width: calc((100% - (3 * 20px)) / 5);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.albums-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.gallery-item {
|
||||
width: calc((100% - (3 * 20px)) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.albums-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.albums-page {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
width: calc((100% - (2 * 20px)) / 3);
|
||||
}
|
||||
|
||||
.gallery-header {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.gallery-info {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.gallery-info h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
padding: 0.8rem 1.6rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.albums-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
width: calc((100% - 20px) / 2);
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.gallery-header {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.share-page {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.share-container {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.share-image {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #1e1e1e;
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.share-image img {
|
||||
max-width: 100%;
|
||||
max-height: 90vh;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.share-actions {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
padding: 1rem;
|
||||
border-radius: 2rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* Ajuster .action-button pour la page de partage */
|
||||
.share-actions .action-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.8rem 1.5rem;
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 2rem;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.share-actions .action-button:hover {
|
||||
background-color: #1976d2;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.share-actions .action-button .icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
/* Protection par mot de passe */
|
||||
.protected-indicator {
|
||||
display: inline-block;
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.8em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Media queries pour la page de partage */
|
||||
@media (max-width: 768px) {
|
||||
.share-actions {
|
||||
flex-direction: column;
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
.share-actions .action-button {
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user