v1.1
This commit is contained in:
40
README.md
40
README.md
@@ -1,4 +1,36 @@
|
|||||||
Noch zu machen:
|
# Fotobox Projekt-Updates
|
||||||
- Max. Anzahl upload von 15 Bildern per upload
|
|
||||||
- Benutzer darf seine eigenen Bilder "löschen" werden aber nur in einen anderen Ordner verschoben
|
Dieses Dokument beschreibt die vorgenommenen technischen Änderungen an der `index.php`.
|
||||||
- Bild Kompression bei der vorschau, um dir ladegeschwindigkeit zu verschnellern und das netzwerk nicht zu überlasten.
|
|
||||||
|
## v1.1 - Fix für gedrehte Bilder (Aktuell)
|
||||||
|
|
||||||
|
### Problembeschreibung
|
||||||
|
Bilder, die mit dem Smartphone im Hochformat (oder quer) aufgenommen wurden, erschienen in der Galerie-Vorschau (Thumbnails) oft um 90° nach links gedreht.
|
||||||
|
|
||||||
|
### Technische Lösung
|
||||||
|
Das Problem lag in der Thumbnail-Generierung mittels PHP GD. Smartphones speichern die korrekte Ausrichtung oft nur in den EXIF-Metadaten, die PHP standardmäßig beim Skalieren ignoriert hat.
|
||||||
|
|
||||||
|
* **Änderung**: Die Funktion `createThumbnail()` wurde erweitert.
|
||||||
|
* Sie nutzt nun `exif_read_data()` (falls auf dem Server verfügbar), um den `Orientation`-Tag aus JPEGs auszulesen.
|
||||||
|
* Basierend auf diesem Tag wird das Bild mittels `imagerotate()` korrekt gedreht, *bevor* das Thumbnail resampled und gespeichert wird.
|
||||||
|
|
||||||
|
*Hinweis:* Diese Änderung betrifft nur Bilder, die *nach* diesem Update hochgeladen werden. Um alte, falsch gedrehte Thumbnails zu korrigieren, muss der Inhalt des Ordners `/thumbnails/` auf dem Server gelöscht werden, damit sie neu generiert werden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.0 - Basis-Funktionen & Performance
|
||||||
|
|
||||||
|
Initiales Update mit Kernfunktionalitäten.
|
||||||
|
|
||||||
|
### 1. Upload-Limitierung
|
||||||
|
* Maximal **15 Bilder** pro Upload-Vorgang möglich.
|
||||||
|
* Validierung erfolgt sowohl im Frontend (JS) als auch Backend (PHP).
|
||||||
|
|
||||||
|
### 2. Bild-Kompression (Thumbnails)
|
||||||
|
* Automatische Erstellung von optimierten Vorschaubildern im Ordner `/thumbnails/`.
|
||||||
|
* Galerie lädt Thumbnails für schnellere Ladezeiten; Originale werden im Ordner `/uploads/` gespeichert und erst in der Vollansicht geladen.
|
||||||
|
|
||||||
|
### 3. Benutzerbasierte Löschfunktion
|
||||||
|
* Über `LocalStorage` merkt sich der Browser, welche Bilder der Nutzer hochgeladen hat.
|
||||||
|
* Lösch-Button erscheint in der Lightbox nur für eigene Bilder.
|
||||||
|
* Bilder werden nicht gelöscht, sondern in `/deleted_uploads/` verschoben.
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Einfaches Deployment Skript
|
|
||||||
echo "Starte Pull... \n";
|
|
||||||
$output = shell_exec('git pull origin main 2>&1');
|
|
||||||
echo $output;
|
|
||||||
?>
|
|
||||||
325
index.php
325
index.php
@@ -1,6 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
$brautpaar_namen = "Damian & Melissa";
|
$brautpaar_namen = "Damian & Melissa";
|
||||||
$upload_verzeichnis = __DIR__ . '/uploads/';
|
$upload_verzeichnis = __DIR__ . '/uploads/';
|
||||||
|
$thumbnail_verzeichnis = __DIR__ . '/thumbnails/';
|
||||||
|
$deleted_verzeichnis = __DIR__ . '/deleted_uploads/';
|
||||||
|
|
||||||
|
// Sicherstellen, dass alle Verzeichnisse existieren
|
||||||
|
if (!is_dir($upload_verzeichnis)) mkdir($upload_verzeichnis, 0777, true);
|
||||||
|
if (!is_dir($thumbnail_verzeichnis)) mkdir($thumbnail_verzeichnis, 0777, true);
|
||||||
|
if (!is_dir($deleted_verzeichnis)) mkdir($deleted_verzeichnis, 0777, true);
|
||||||
|
|
||||||
function get_image_count($dir) {
|
function get_image_count($dir) {
|
||||||
if (!is_dir($dir)) return 0;
|
if (!is_dir($dir)) return 0;
|
||||||
@@ -10,9 +17,146 @@ function get_image_count($dir) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funktion zum Erstellen eines komprimierten Vorschaubildes inkl. EXIF-Rotationskorrektur
|
||||||
|
function createThumbnail($sourcePath, $destPath, $maxWidth = 400, $maxHeight = 400) {
|
||||||
|
if (!function_exists('imagecreatefromjpeg')) return false; // Fallback, falls GD-Lib fehlt
|
||||||
|
|
||||||
|
$info = getimagesize($sourcePath);
|
||||||
|
if (!$info) return false;
|
||||||
|
|
||||||
|
$mime = $info['mime'];
|
||||||
|
$sourceImage = null;
|
||||||
|
|
||||||
|
// Bild laden basierend auf MIME-Typ
|
||||||
|
switch ($mime) {
|
||||||
|
case 'image/jpeg': $sourceImage = imagecreatefromjpeg($sourcePath); break;
|
||||||
|
case 'image/png': $sourceImage = imagecreatefrompng($sourcePath); break;
|
||||||
|
case 'image/gif': $sourceImage = imagecreatefromgif($sourcePath); break;
|
||||||
|
case 'image/webp': $sourceImage = imagecreatefromwebp($sourcePath); break;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$sourceImage) return false;
|
||||||
|
|
||||||
|
// --- NEU: EXIF Orientierung korrigieren (nur für JPEG) ---
|
||||||
|
if ($mime == 'image/jpeg' && function_exists('exif_read_data')) {
|
||||||
|
@$exif = exif_read_data($sourcePath); // @ unterdrückt Warnungen, falls keine EXIF-Daten vorhanden
|
||||||
|
if (!empty($exif['Orientation'])) {
|
||||||
|
switch ($exif['Orientation']) {
|
||||||
|
case 3: // 180 Grad drehen
|
||||||
|
$sourceImage = imagerotate($sourceImage, 180, 0);
|
||||||
|
break;
|
||||||
|
case 6: // 90 Grad nach rechts drehen (war vermutlich dein Problem)
|
||||||
|
$sourceImage = imagerotate($sourceImage, -90, 0);
|
||||||
|
break;
|
||||||
|
case 8: // 90 Grad nach links drehen
|
||||||
|
$sourceImage = imagerotate($sourceImage, 90, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
// Dimensionen NACH möglicher Drehung holen
|
||||||
|
$srcWidth = imagesx($sourceImage);
|
||||||
|
$srcHeight = imagesy($sourceImage);
|
||||||
|
|
||||||
|
// Proportionale Skalierung berechnen
|
||||||
|
$ratio = min($maxWidth / $srcWidth, $maxHeight / $srcHeight);
|
||||||
|
if ($ratio >= 1) {
|
||||||
|
$newWidth = $srcWidth;
|
||||||
|
$newHeight = $srcHeight;
|
||||||
|
} else {
|
||||||
|
$newWidth = (int)($srcWidth * $ratio);
|
||||||
|
$newHeight = (int)($srcHeight * $ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
$destImage = imagecreatetruecolor($newWidth, $newHeight);
|
||||||
|
|
||||||
|
// Transparenz für PNG und WEBP beibehalten
|
||||||
|
if ($mime == 'image/png' || $mime == 'image/webp') {
|
||||||
|
imagealphablending($destImage, false);
|
||||||
|
imagesavealpha($destImage, true);
|
||||||
|
$transparent = imagecolorallocatealpha($destImage, 255, 255, 255, 127);
|
||||||
|
imagefilledrectangle($destImage, 0, 0, $newWidth, $newHeight, $transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
imagecopyresampled($destImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $srcWidth, $srcHeight);
|
||||||
|
|
||||||
|
// Thumbnail speichern
|
||||||
|
switch ($mime) {
|
||||||
|
case 'image/jpeg': imagejpeg($destImage, $destPath, 80); break;
|
||||||
|
case 'image/png': imagepng($destImage, $destPath, 8); break;
|
||||||
|
case 'image/gif': imagegif($destImage, $destPath); break;
|
||||||
|
case 'image/webp': imagewebp($destImage, $destPath, 80); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
imagedestroy($sourceImage);
|
||||||
|
imagedestroy($destImage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- LÖSCHEN LOGIK --
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete') {
|
||||||
|
$fileToDelete = basename($_POST['filename']);
|
||||||
|
|
||||||
|
if (is_file($upload_verzeichnis . $fileToDelete)) {
|
||||||
|
// Verschiebe Originalbild
|
||||||
|
rename($upload_verzeichnis . $fileToDelete, $deleted_verzeichnis . $fileToDelete);
|
||||||
|
|
||||||
|
// Verschiebe Thumbnail, falls vorhanden
|
||||||
|
if (is_file($thumbnail_verzeichnis . $fileToDelete)) {
|
||||||
|
rename($thumbnail_verzeichnis . $fileToDelete, $deleted_verzeichnis . 'thumb_' . $fileToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Datei nicht gefunden.']);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- UPLOAD LOGIK --
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['photos'])) {
|
||||||
|
// Limit auf 15 Bilder pro Upload serverseitig prüfen
|
||||||
|
if (count($_FILES['photos']['tmp_name']) > 15) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Maximal 15 Bilder auf einmal erlaubt.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanName = 'Foto';
|
||||||
|
$uploadedCount = 0;
|
||||||
|
$uploadedFiles = []; // Speichert die Namen für das LocalStorage des Nutzers
|
||||||
|
|
||||||
|
foreach ($_FILES['photos']['tmp_name'] as $key => $tmpName) {
|
||||||
|
if ($tmpName != "") {
|
||||||
|
$extension = strtolower(pathinfo($_FILES['photos']['name'][$key], PATHINFO_EXTENSION));
|
||||||
|
$newName = $cleanName . '_' . time() . '_' . substr(md5(uniqid()), 0, 6) . '.' . $extension;
|
||||||
|
$destination = $upload_verzeichnis . $newName;
|
||||||
|
$thumbDestination = $thumbnail_verzeichnis . $newName;
|
||||||
|
|
||||||
|
if (move_uploaded_file($tmpName, $destination)) {
|
||||||
|
$uploadedCount++;
|
||||||
|
$uploadedFiles[] = $newName;
|
||||||
|
|
||||||
|
// Erstelle Thumbnail (jetzt mit Rotationskorrektur)
|
||||||
|
createThumbnail($destination, $thumbDestination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'count' => $uploadedCount,
|
||||||
|
'newTotal' => get_image_count($upload_verzeichnis),
|
||||||
|
'uploadedFiles' => $uploadedFiles
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
$current_count = get_image_count($upload_verzeichnis);
|
$current_count = get_image_count($upload_verzeichnis);
|
||||||
|
|
||||||
// Bilder für die Galerie laden (nur die neuesten 30, um Abstürze am Handy zu vermeiden)
|
// Bilder für die Galerie laden (nur die neuesten 30)
|
||||||
$gallery_images = [];
|
$gallery_images = [];
|
||||||
if (is_dir($upload_verzeichnis)) {
|
if (is_dir($upload_verzeichnis)) {
|
||||||
$files = scandir($upload_verzeichnis);
|
$files = scandir($upload_verzeichnis);
|
||||||
@@ -25,35 +169,8 @@ if (is_dir($upload_verzeichnis)) {
|
|||||||
|
|
||||||
// ZUVERLÄSSIGE SORTIERUNG: Alphabetisch absteigend, da Dateiname = Zeitstempel
|
// ZUVERLÄSSIGE SORTIERUNG: Alphabetisch absteigend, da Dateiname = Zeitstempel
|
||||||
rsort($valid_files);
|
rsort($valid_files);
|
||||||
|
|
||||||
$gallery_images = array_slice($valid_files, 0, 30);
|
$gallery_images = array_slice($valid_files, 0, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|
||||||
$cleanName = 'Foto';
|
|
||||||
|
|
||||||
if (!is_dir($upload_verzeichnis)) {
|
|
||||||
mkdir($upload_verzeichnis, 0777, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$uploadedCount = 0;
|
|
||||||
foreach ($_FILES['photos']['tmp_name'] as $key => $tmpName) {
|
|
||||||
if ($tmpName != "") {
|
|
||||||
$extension = strtolower(pathinfo($_FILES['photos']['name'][$key], PATHINFO_EXTENSION));
|
|
||||||
$newName = $cleanName . '_' . time() . '_' . substr(md5(uniqid()), 0, 6) . '.' . $extension;
|
|
||||||
if (move_uploaded_file($tmpName, $upload_verzeichnis . $newName)) {
|
|
||||||
$uploadedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
'success' => true,
|
|
||||||
'count' => $uploadedCount,
|
|
||||||
'newTotal' => get_image_count($upload_verzeichnis)
|
|
||||||
]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -65,7 +182,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
/* Farbpalette */
|
|
||||||
--dark-green: #484D46;
|
--dark-green: #484D46;
|
||||||
--olive-green: #5F6251;
|
--olive-green: #5F6251;
|
||||||
--brown: #856856;
|
--brown: #856856;
|
||||||
@@ -73,6 +189,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
--beige: #BEB7AD;
|
--beige: #BEB7AD;
|
||||||
--bg-page: #FAF9F7;
|
--bg-page: #FAF9F7;
|
||||||
--white: #ffffff;
|
--white: #ffffff;
|
||||||
|
--red: #d9534f;
|
||||||
}
|
}
|
||||||
|
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
@@ -84,7 +201,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: flex-start; /* Wichtig für die Galerie */
|
align-items: flex-start;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
transition: align-items 0.3s ease;
|
transition: align-items 0.3s ease;
|
||||||
@@ -145,7 +262,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tabs Navigation */
|
|
||||||
.tabs {
|
.tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -174,7 +290,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
border-color: var(--olive-green);
|
border-color: var(--olive-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Upload Bereich */
|
|
||||||
.form-group { margin-bottom: 25px; text-align: left; }
|
.form-group { margin-bottom: 25px; text-align: left; }
|
||||||
|
|
||||||
.upload-area {
|
.upload-area {
|
||||||
@@ -198,7 +313,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Progress Bar */
|
|
||||||
#progress-wrapper { display: none; margin-top: 20px; text-align: left; }
|
#progress-wrapper { display: none; margin-top: 20px; text-align: left; }
|
||||||
.progress-bar-container {
|
.progress-bar-container {
|
||||||
width: 100%; height: 6px; background: var(--beige);
|
width: 100%; height: 6px; background: var(--beige);
|
||||||
@@ -206,7 +320,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
}
|
}
|
||||||
#progress-bar { height: 100%; background: var(--olive-green); width: 0%; transition: width 0.2s; }
|
#progress-bar { height: 100%; background: var(--olive-green); width: 0%; transition: width 0.2s; }
|
||||||
|
|
||||||
/* Galerie Bereich */
|
|
||||||
#gallery-section { display: none; }
|
#gallery-section { display: none; }
|
||||||
.gallery-grid {
|
.gallery-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -233,7 +346,6 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Lightbox (Vollbild) */
|
|
||||||
.lightbox {
|
.lightbox {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -245,15 +357,17 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lightbox.show { display: flex; opacity: 1; }
|
.lightbox.show { display: flex; opacity: 1; }
|
||||||
|
|
||||||
.lightbox img {
|
.lightbox img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 90vh;
|
max-height: 80vh;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-lightbox {
|
.close-lightbox {
|
||||||
@@ -266,7 +380,22 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Success Screen */
|
.delete-btn {
|
||||||
|
background: var(--red);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
display: none; /* Wird via JS angezeigt */
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn:hover { background: #c9302c; }
|
||||||
|
|
||||||
.success-screen { display: none; animation: fadeIn 0.5s ease; }
|
.success-screen { display: none; animation: fadeIn 0.5s ease; }
|
||||||
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
||||||
.heart { font-size: 2.5rem; margin: 20px 0; color: var(--brown); }
|
.heart { font-size: 2.5rem; margin: 20px 0; color: var(--brown); }
|
||||||
@@ -304,7 +433,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="upload-area" id="drop-area" onclick="document.getElementById('photos').click()">
|
<div class="upload-area" id="drop-area" onclick="document.getElementById('photos').click()">
|
||||||
<span style="display:block; margin-bottom: 10px; font-size: 2rem;">📸</span>
|
<span style="display:block; margin-bottom: 10px; font-size: 2rem;">📸</span>
|
||||||
<span>Klicken zum Auswählen<br>oder Kamera öffnen</span>
|
<span>Klicken zum Auswählen<br>oder Kamera öffnen<br><small>(Max. 15 Bilder)</small></span>
|
||||||
<input type="file" id="photos" name="photos[]" accept="image/*" multiple style="display:none">
|
<input type="file" id="photos" name="photos[]" accept="image/*" multiple style="display:none">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -322,7 +451,18 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
<div class="gallery-grid">
|
<div class="gallery-grid">
|
||||||
<?php if (count($gallery_images) > 0): ?>
|
<?php if (count($gallery_images) > 0): ?>
|
||||||
<?php foreach ($gallery_images as $img): ?>
|
<?php foreach ($gallery_images as $img): ?>
|
||||||
<img src="uploads/<?php echo urlencode($img); ?>" class="gallery-item" loading="lazy" onclick="openLightbox(this.src)" alt="Hochzeitsfoto">
|
<?php
|
||||||
|
// Prüfen, ob Thumbnail existiert, sonst Original nehmen
|
||||||
|
$thumbSrc = is_file($thumbnail_verzeichnis . $img) ? "thumbnails/" . urlencode($img) : "uploads/" . urlencode($img);
|
||||||
|
$fullSrc = "uploads/" . urlencode($img);
|
||||||
|
?>
|
||||||
|
<img src="<?php echo $thumbSrc; ?>"
|
||||||
|
data-full="<?php echo $fullSrc; ?>"
|
||||||
|
data-filename="<?php echo htmlspecialchars($img); ?>"
|
||||||
|
class="gallery-item"
|
||||||
|
loading="lazy"
|
||||||
|
onclick="openLightbox(this.dataset.full, this.dataset.filename)"
|
||||||
|
alt="Hochzeitsfoto">
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="empty-gallery">Noch keine Bilder hochgeladen. Sei der oder die Erste!</div>
|
<div class="empty-gallery">Noch keine Bilder hochgeladen. Sei der oder die Erste!</div>
|
||||||
@@ -342,6 +482,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
<div id="lightbox" class="lightbox" onclick="closeLightbox(event)">
|
<div id="lightbox" class="lightbox" onclick="closeLightbox(event)">
|
||||||
<span class="close-lightbox" onclick="closeLightbox(event)">×</span>
|
<span class="close-lightbox" onclick="closeLightbox(event)">×</span>
|
||||||
<img id="lightbox-img" src="" alt="Vollbild">
|
<img id="lightbox-img" src="" alt="Vollbild">
|
||||||
|
<button id="lightbox-delete-btn" class="delete-btn">Bild löschen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -357,6 +498,13 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
const files = fileInput.files;
|
const files = fileInput.files;
|
||||||
if(files.length === 0) return;
|
if(files.length === 0) return;
|
||||||
|
|
||||||
|
// Validierung: Max 15 Bilder
|
||||||
|
if (files.length > 15) {
|
||||||
|
alert("Bitte wähle maximal 15 Bilder auf einmal aus.");
|
||||||
|
fileInput.value = ''; // Input zurücksetzen
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dropArea.classList.add('uploading');
|
dropArea.classList.add('uploading');
|
||||||
dropArea.innerHTML = `<span style="display:block; margin-bottom: 10px; font-size: 2rem;">⏳</span><span>Wird verarbeitet...</span>`;
|
dropArea.innerHTML = `<span style="display:block; margin-bottom: 10px; font-size: 2rem;">⏳</span><span>Wird verarbeitet...</span>`;
|
||||||
progressWrapper.style.display = 'block';
|
progressWrapper.style.display = 'block';
|
||||||
@@ -379,27 +527,43 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
|
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
const resp = JSON.parse(xhr.responseText);
|
try {
|
||||||
if(resp.success) {
|
const resp = JSON.parse(xhr.responseText);
|
||||||
counterDisplay.innerText = resp.newTotal;
|
if(resp.success) {
|
||||||
document.getElementById('main-ui').style.display = 'none';
|
// Speicher hochgeladene Dateien im LocalStorage
|
||||||
document.getElementById('success-ui').style.display = 'block';
|
let myUploads = JSON.parse(localStorage.getItem('myUploads') || '[]');
|
||||||
|
myUploads = myUploads.concat(resp.uploadedFiles);
|
||||||
|
localStorage.setItem('myUploads', JSON.stringify(myUploads));
|
||||||
|
|
||||||
// FIX: Zentriert den Success-Screen wieder perfekt in der Mitte!
|
counterDisplay.innerText = resp.newTotal;
|
||||||
document.body.style.alignItems = 'center';
|
document.getElementById('main-ui').style.display = 'none';
|
||||||
document.querySelector('.container').style.marginTop = '0';
|
document.getElementById('success-ui').style.display = 'block';
|
||||||
|
|
||||||
|
document.body.style.alignItems = 'center';
|
||||||
|
document.querySelector('.container').style.marginTop = '0';
|
||||||
|
} else {
|
||||||
|
alert(resp.error || "Fehler beim Upload.");
|
||||||
|
resetUploadArea();
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
alert("Fehler beim Verarbeiten der Serverantwort.");
|
||||||
|
resetUploadArea();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert("Fehler beim Upload. Bitte prüfe die Dateigröße.");
|
alert("Ein Serverfehler ist aufgetreten.");
|
||||||
dropArea.classList.remove('uploading');
|
resetUploadArea();
|
||||||
dropArea.innerHTML = `<span style="display:block; margin-bottom: 10px; font-size: 2rem;">📸</span><span>Klicken zum Auswählen<br>oder Kamera öffnen</span>`;
|
|
||||||
progressWrapper.style.display = 'none';
|
|
||||||
fileInput.value = '';
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.send(formData);
|
xhr.send(formData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function resetUploadArea() {
|
||||||
|
dropArea.classList.remove('uploading');
|
||||||
|
dropArea.innerHTML = `<span style="display:block; margin-bottom: 10px; font-size: 2rem;">📸</span><span>Klicken zum Auswählen<br>oder Kamera öffnen<br><small>(Max. 15 Bilder)</small></span>`;
|
||||||
|
progressWrapper.style.display = 'none';
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
// --- Tabs Navigation ---
|
// --- Tabs Navigation ---
|
||||||
function switchTab(tab) {
|
function switchTab(tab) {
|
||||||
document.getElementById('upload-section').style.display = tab === 'upload' ? 'block' : 'none';
|
document.getElementById('upload-section').style.display = tab === 'upload' ? 'block' : 'none';
|
||||||
@@ -409,24 +573,67 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['photos'])) {
|
|||||||
document.getElementById('btn-gallery').classList.toggle('active', tab === 'gallery');
|
document.getElementById('btn-gallery').classList.toggle('active', tab === 'gallery');
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Lightbox Logik ---
|
// --- Lightbox & Löschen Logik ---
|
||||||
const lightbox = document.getElementById('lightbox');
|
const lightbox = document.getElementById('lightbox');
|
||||||
const lightboxImg = document.getElementById('lightbox-img');
|
const lightboxImg = document.getElementById('lightbox-img');
|
||||||
|
const deleteBtn = document.getElementById('lightbox-delete-btn');
|
||||||
|
|
||||||
function openLightbox(imageSrc) {
|
function openLightbox(imageFullSrc, filename) {
|
||||||
lightboxImg.src = imageSrc;
|
lightboxImg.src = imageFullSrc;
|
||||||
lightbox.classList.add('show');
|
lightbox.classList.add('show');
|
||||||
document.body.style.overflow = 'hidden'; // Verhindert scrollen im Hintergrund
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
// Prüfen, ob Bild dem Nutzer gehört
|
||||||
|
const myUploads = JSON.parse(localStorage.getItem('myUploads') || '[]');
|
||||||
|
if (myUploads.includes(filename)) {
|
||||||
|
deleteBtn.style.display = 'inline-block';
|
||||||
|
deleteBtn.onclick = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
deleteImage(filename);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
deleteBtn.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeLightbox(e) {
|
function closeLightbox(e) {
|
||||||
// Schließt nur, wenn man aufs X oder den dunklen Hintergrund klickt (nicht aufs Bild selbst)
|
|
||||||
if (e.target === lightbox || e.target.className === 'close-lightbox') {
|
if (e.target === lightbox || e.target.className === 'close-lightbox') {
|
||||||
lightbox.classList.remove('show');
|
lightbox.classList.remove('show');
|
||||||
document.body.style.overflow = 'auto'; // Scrollen wieder erlauben
|
document.body.style.overflow = 'auto';
|
||||||
setTimeout(() => { lightboxImg.src = ''; }, 300); // Bild leeren nach Animation
|
setTimeout(() => { lightboxImg.src = ''; }, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteImage(filename) {
|
||||||
|
if (!confirm("Möchtest du dein Bild wirklich entfernen?")) return;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('action', 'delete');
|
||||||
|
formData.append('filename', filename);
|
||||||
|
|
||||||
|
fetch('', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
// Aus dem LocalStorage entfernen
|
||||||
|
let myUploads = JSON.parse(localStorage.getItem('myUploads') || '[]');
|
||||||
|
myUploads = myUploads.filter(f => f !== filename);
|
||||||
|
localStorage.setItem('myUploads', JSON.stringify(myUploads));
|
||||||
|
|
||||||
|
// Seite neuladen um die Galerie zu aktualisieren
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
alert(data.error || "Fehler beim Löschen.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
alert("Netzwerkfehler beim Löschen.");
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user