Compare commits
11
Commits
ed9f0d058c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32fc3d3eb7 | ||
|
|
e89ad74121 | ||
|
|
b13dd60788 | ||
|
|
2e13e5d5d5 | ||
|
|
903f4cd1e9 | ||
|
|
c322107dd1 | ||
|
|
c78589f29f | ||
|
|
9c8e0acebe | ||
|
|
61e461237f | ||
|
|
b296d110e3 | ||
|
|
401737ac76 |
@@ -15,3 +15,6 @@ venv/
|
|||||||
# Dependencies (just in case they get added later)
|
# Dependencies (just in case they get added later)
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const response = await fetch('feedback_backend.php', {
|
const response = await fetch('feedback_backend.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ action: 'delete', id: id, secret: '1234' })
|
body: JSON.stringify({ action: 'delete', id: id, secret: sessionStorage.getItem('schnappix_admin_secret') || '' })
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -240,7 +240,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const response = await fetch('feedback_backend.php', {
|
const response = await fetch('feedback_backend.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ action: 'answer', id: id, answer: answer, secret: '1234' })
|
body: JSON.stringify({ action: 'answer', id: id, answer: answer, secret: sessionStorage.getItem('schnappix_admin_secret') || '' })
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
@@ -363,6 +363,49 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Tester Registration Logic ---
|
||||||
|
const testerForm = document.getElementById('tester-form');
|
||||||
|
if (testerForm) {
|
||||||
|
testerForm.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const emailInput = document.getElementById('tester-email');
|
||||||
|
const submitBtn = document.getElementById('tester-submit-btn');
|
||||||
|
const successMsg = document.getElementById('tester-success-msg');
|
||||||
|
const errorMsg = document.getElementById('tester-error-msg');
|
||||||
|
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.textContent = 'Wird gesendet...';
|
||||||
|
successMsg.style.display = 'none';
|
||||||
|
errorMsg.style.display = 'none';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('tester_backend.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ email: emailInput.value })
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
successMsg.style.display = 'block';
|
||||||
|
testerForm.reset();
|
||||||
|
} else {
|
||||||
|
errorMsg.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> ' + (result.error || 'Unbekannter Fehler');
|
||||||
|
errorMsg.style.display = 'block';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
errorMsg.innerHTML = '<i class="fa-solid fa-triangle-exclamation"></i> Netzwerkfehler. Bitte später erneut versuchen.';
|
||||||
|
errorMsg.style.display = 'block';
|
||||||
|
} finally {
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.textContent = 'Als Tester anmelden';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Modal Management for Impressum & Privacy ---
|
// --- Modal Management for Impressum & Privacy ---
|
||||||
const modalTriggers = document.querySelectorAll('.legal-trigger');
|
const modalTriggers = document.querySelectorAll('.legal-trigger');
|
||||||
const modals = document.querySelectorAll('.modal-overlay');
|
const modals = document.querySelectorAll('.modal-overlay');
|
||||||
@@ -424,13 +467,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (adminLoginForm) {
|
if (adminLoginForm) {
|
||||||
adminLoginForm.addEventListener('submit', (e) => {
|
adminLoginForm.addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const password = adminPasswordInput.value;
|
const password = adminPasswordInput.value;
|
||||||
// Admin password matching the default app settings: "1234"
|
|
||||||
if (password === '1234') {
|
try {
|
||||||
|
const response = await fetch('feedback_backend.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ action: 'verify_password', secret: password })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
isAdmin = true;
|
isAdmin = true;
|
||||||
sessionStorage.setItem('schnappix_is_admin', 'true');
|
sessionStorage.setItem('schnappix_is_admin', 'true');
|
||||||
|
sessionStorage.setItem('schnappix_admin_secret', password);
|
||||||
adminPasswordInput.value = '';
|
adminPasswordInput.value = '';
|
||||||
adminLoginError.style.display = 'none';
|
adminLoginError.style.display = 'none';
|
||||||
|
|
||||||
@@ -443,7 +494,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
} else {
|
} else {
|
||||||
adminLoginError.style.display = 'block';
|
adminLoginError.style.display = 'block';
|
||||||
adminPasswordInput.value = '';
|
adminPasswordInput.value = '';
|
||||||
adminPasswordInput.focus();
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
adminLoginError.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -24,6 +24,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
@@ -43,6 +44,9 @@
|
|||||||
<h4>Allgemeine Hinweise</h4>
|
<h4>Allgemeine Hinweise</h4>
|
||||||
<p>Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können.</p>
|
<p>Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie persönlich identifiziert werden können.</p>
|
||||||
|
|
||||||
|
<h4>Verantwortliche Stelle</h4>
|
||||||
|
<p>Verantwortlich für die Datenverarbeitung auf dieser Website ist die im Impressum genannte Person.</p>
|
||||||
|
|
||||||
<h3>2. Datenerfassung auf unserer Website</h3>
|
<h3>2. Datenerfassung auf unserer Website</h3>
|
||||||
<h4>Cookies & Local Storage</h4>
|
<h4>Cookies & Local Storage</h4>
|
||||||
<p>Diese Webseite nutzt den lokalen Speicher (Local Storage) Ihres Browsers, um die von Ihnen geschriebenen Feedbacks sowie Ihre Stimmen (Upvotes) zu speichern. Dies geschieht ausschließlich auf Ihrem Endgerät und wird nicht an externe Server übertragen, es sei denn, Sie senden explizit ein Feedback ab.</p>
|
<p>Diese Webseite nutzt den lokalen Speicher (Local Storage) Ihres Browsers, um die von Ihnen geschriebenen Feedbacks sowie Ihre Stimmen (Upvotes) zu speichern. Dies geschieht ausschließlich auf Ihrem Endgerät und wird nicht an externe Server übertragen, es sei denn, Sie senden explizit ein Feedback ab.</p>
|
||||||
@@ -50,8 +54,12 @@
|
|||||||
<h4>Server-Logfiles</h4>
|
<h4>Server-Logfiles</h4>
|
||||||
<p>Der Provider der Seiten erhebt und speichert automatisch Informationen in sogenannten Server-Logfiles, die Ihr Browser automatisch an uns übermittelt. Dies sind Browsertyp, Betriebssystem, Referrer URL, Hostname des zugreifenden Rechners und Uhrzeit der Serveranfrage. Diese Daten sind nicht bestimmten Personen zuzuordnen.</p>
|
<p>Der Provider der Seiten erhebt und speichert automatisch Informationen in sogenannten Server-Logfiles, die Ihr Browser automatisch an uns übermittelt. Dies sind Browsertyp, Betriebssystem, Referrer URL, Hostname des zugreifenden Rechners und Uhrzeit der Serveranfrage. Diese Daten sind nicht bestimmten Personen zuzuordnen.</p>
|
||||||
|
|
||||||
<h3>3. Feedback Hub</h3>
|
<h3>3. Feedback Hub & Uploads</h3>
|
||||||
<p>Wenn Sie den Feedback Hub nutzen, werden Ihre Eingaben (Name, Titel, Text, Kategorie) gespeichert, um die Funktionalität der Feedback-Plattform bereitzustellen. Bitte geben Sie keine sensiblen personenbezogenen Daten im Freitextfeld an.</p>
|
<p><strong>Feedback Hub:</strong> Wenn Sie den Feedback Hub nutzen, werden Ihre Eingaben (Name, Titel, Text, Kategorie) gespeichert, um die Funktionalität der Feedback-Plattform bereitzustellen. Bitte geben Sie keine sensiblen personenbezogenen Daten im Freitextfeld an.</p>
|
||||||
|
<p><strong>Upload-Funktion:</strong> Wenn Sie Bilddateien (Rahmen) über die Upload-Funktion hochladen, werden diese gespeichert. Nach erfolgreicher Prüfung werden diese Rahmen öffentlich in die App integriert und sind somit für alle Nutzer der App sichtbar. Bitte stellen Sie sicher, dass Sie keine privaten Bilder oder urheberrechtlich geschützten Inhalte ohne entsprechende Rechte hochladen.</p>
|
||||||
|
|
||||||
|
<h3>4. Ihre Rechte</h3>
|
||||||
|
<p>Sie haben jederzeit das Recht, unentgeltlich Auskunft über Herkunft, Empfänger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem ein Recht, die Berichtigung oder Löschung dieser Daten zu verlangen. Hierzu sowie zu weiteren Fragen zum Thema Datenschutz können Sie sich jederzeit an uns unter der im Impressum angegebenen E-Mail-Adresse wenden. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde zu.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Schnappix - Häufig gestellte Fragen (FAQ)</title>
|
<title>Schnappix - Häufig gestellte Fragen (FAQ)</title>
|
||||||
<meta name="description" content="Antworten auf Fragen zum Kiosk-Modus, IPP-Drucker-Einrichtung, Fotorahmen-Konfiguration und Foto-Speicherung.">
|
<meta name="description"
|
||||||
|
content="Antworten auf Fragen zum Kiosk-Modus, IPP-Drucker-Einrichtung, Fotorahmen-Konfiguration und Foto-Speicherung.">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap" rel="stylesheet">
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- Header Navigation -->
|
<!-- Header Navigation -->
|
||||||
@@ -25,6 +30,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html" class="active-nav">FAQ</a>
|
<a href="faq.html" class="active-nav">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
@@ -36,8 +42,10 @@
|
|||||||
<!-- Page Title -->
|
<!-- Page Title -->
|
||||||
<div style="padding: 140px 0 20px;">
|
<div style="padding: 140px 0 20px;">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="section-title" style="margin-bottom: 20px;">Häufig gestellte <span class="text-gradient">Fragen</span></h1>
|
<h1 class="section-title" style="margin-bottom: 20px;">Häufig gestellte <span
|
||||||
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 40px;">Alles Wissenswerte zur Einrichtung, Hardware-Kompatibilität und Anpassung der Photobooth App.</p>
|
class="text-gradient">Fragen</span></h1>
|
||||||
|
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 40px;">Alles
|
||||||
|
Wissenswerte zur Einrichtung, Hardware-Kompatibilität und Anpassung der Schnappix App.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -45,59 +53,17 @@
|
|||||||
<section class="faq-section" style="padding-top: 20px;">
|
<section class="faq-section" style="padding-top: 20px;">
|
||||||
<div class="container faq-container">
|
<div class="container faq-container">
|
||||||
<div class="accordion">
|
<div class="accordion">
|
||||||
|
<!-- Voraussetzungen & Kompatibilität -->
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<button class="accordion-header">
|
<button class="accordion-header">
|
||||||
<span>Wie aktiviere ich den Kiosk-Modus auf dem Tablet?</span>
|
<span>Ist die App auch für Smartphones geeignet?</span>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Damit der Kiosk-Modus (LockTask) ohne Bestätigungsdialoge funktioniert, muss die App als <strong>Device Owner</strong> festgelegt werden. Führe dazu folgenden ADB-Befehl aus:</p>
|
<p>Die Benutzeroberfläche von Schnappix ist primär für die Nutzung auf <strong>Tablets</strong>
|
||||||
<code>adb shell dpm set-device-owner de.orfel.schnappix/de.orfel.schnappix.KioskDeviceAdminReceiver</code>
|
optimiert. Tablets bieten den Gästen auf Events einen größeren Bildschirm als
|
||||||
<p>Danach kannst du den Kiosk-Modus einfach im passwortgeschützten Admin-Bereich ein- und ausschalten.</p>
|
Vorschau-Spiegel und eine angenehmere Bedienung für den Sticker-Editor.
|
||||||
</div>
|
Allerdings funktioniert die App auch auf jedem Android Smartphone.</p>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="accordion-item">
|
|
||||||
<button class="accordion-header">
|
|
||||||
<span>Wie füge ich eigene Fotorahmen hinzu?</span>
|
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="accordion-content">
|
|
||||||
<p>Erstelle ein transparentes PNG im Format <strong>1500 x 1000 px</strong> (3:2 Querformat, max. 500 KB) und lege es im Ordner <code>assets/frames/</code> als <code>frame_name.png</code> ab. Trage das Template anschließend in <code>src/data/frames.ts</code> ein und führe einen neuen Build aus.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="accordion-item">
|
|
||||||
<button class="accordion-header">
|
|
||||||
<span>Welche Drucker werden unterstützt?</span>
|
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="accordion-content">
|
|
||||||
<p>Es werden alle Drucker unterstützt, die das <strong>IPP (Internet Printing Protocol)</strong> im lokalen Netzwerk unterstützen. Beliebte Fotodrucker wie der Canon CP1300 lassen sich direkt über ihre lokale IP-Adresse ansteuern.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="accordion-item">
|
|
||||||
<button class="accordion-header">
|
|
||||||
<span>Wo werden die Fotos gespeichert?</span>
|
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="accordion-content">
|
|
||||||
<p>Schnappix nutzt die moderne Android MediaStore API und speichert Collagen sowie Originale direkt in der Galerie deines Tablets unter den Ordnern:</p>
|
|
||||||
<ul>
|
|
||||||
<li><code>Schnappix - <EventName> - Collagen</code></li>
|
|
||||||
<li><code>Schnappix - <EventName> - Originale</code></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="accordion-item">
|
|
||||||
<button class="accordion-header">
|
|
||||||
<span>Benötige ich eine Internetverbindung für die Fotobox?</span>
|
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
<div class="accordion-content">
|
|
||||||
<p>Nein, für das Aufnehmen von Fotos, die Speicherung auf dem Gerät und den lokalen Druck (z.B. über ein lokales WLAN) wird <strong>keine</strong> aktive Internetverbindung benötigt.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -107,7 +73,20 @@
|
|||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Schnappix benötigt mindestens <strong>Android 10 (API Level 29)</strong>, um die volle Unterstützung für die moderne CameraX-API und das aktuelle Dateisystem (Scoped Storage) zu gewährleisten.</p>
|
<p>Schnappix benötigt mindestens <strong>Android 10 (API Level 29)</strong>, um die volle
|
||||||
|
Unterstützung für die moderne CameraX-API und das aktuelle Dateisystem (Scoped Storage) zu
|
||||||
|
gewährleisten.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Benötige ich eine Internetverbindung für die Fotobox?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Nein, für das Aufnehmen von Fotos, die Speicherung auf dem Gerät und den lokalen Druck (z.B.
|
||||||
|
über ein lokales WLAN) wird <strong>keine</strong> aktive Internetverbindung benötigt.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -117,7 +96,31 @@
|
|||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Ja, Schnappix unterstützt neben der eingebauten Tablet-Frontkamera auch externe USB-Kameras (UVC). Schließe einfach eine USB-Webcam an dein Tablet an, um für eine noch bessere Bildqualität zu sorgen.</p>
|
<p>Ja, Schnappix unterstützt neben der eingebauten Tablet-Frontkamera auch externe USB-Kameras
|
||||||
|
(UVC). Schließe einfach eine USB-Webcam an dein Tablet an, um für eine noch bessere
|
||||||
|
Bildqualität zu sorgen. Sollte dies nicht sofort funktionieren, probiere es mit einem
|
||||||
|
USB-C-Hub.
|
||||||
|
Wenn eine externe Kamera verbunden wird, muss der Schnappix App in den
|
||||||
|
Admineinstellungen die Frontkamera deaktiviert werden, damit die USB-Kamera funktioniert,
|
||||||
|
da dies im Hintergrund nicht automatisch geschieht.
|
||||||
|
<br>Wichtig: Die Foto-Qualität hängt von der verbundenen Kamera ab.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Funktionen der Fotobox -->
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Wie viele Fotos werden für eine Collage aufgenommen?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Die App ist für Serienbilder ausgelegt und nimmt automatisch 1 bis 5 Fotos hintereinander
|
||||||
|
auf. Aus dieser Serie wird im Anschluss direkt eine fertige Collage zum Drucken oder
|
||||||
|
Speichern generiert.
|
||||||
|
Wie viel Bilder aufgenommen werden sollen, kann in den Admineinstellungen voreingestellt
|
||||||
|
werden. In einer Serie kann immer nur die eingestellte Anzahl an Fotos aufgenommen werden.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -127,27 +130,103 @@
|
|||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Schnappix bietet einen integrierten Editor, mit dem die Gäste ihre Collagen vor dem Speichern oder Drucken verzieren können. Emojis, Datumsanzeigen und der Eventname können dabei wie auf Social Media frei auf dem Bildschirm verschoben, gedreht und skaliert werden.</p>
|
<p>Schnappix bietet einen integrierten Editor, mit dem die Gäste ihre Collagen vor dem Speichern
|
||||||
|
oder Drucken verzieren können. Emojis, Datumsanzeigen und der Eventname können dabei wie auf
|
||||||
|
Social Media frei auf dem Bildschirm verschoben, gedreht und skaliert werden.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<button class="accordion-header">
|
<button class="accordion-header">
|
||||||
<span>Wie viele Fotos werden für eine Collage aufgenommen?</span>
|
<span>Wie füge ich eigene Fotorahmen hinzu?</span>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Die App ist für Serienbilder ausgelegt und nimmt automatisch 1 bis 5 Fotos hintereinander auf. Aus dieser Serie wird im Anschluss direkt eine fertige Collage zum Drucken oder Speichern generiert.</p>
|
<p>Erstelle ein transparentes PNG im Format <strong>1500 x 1000 px</strong> (3:2 Querformat,
|
||||||
|
max. 5 MB)
|
||||||
|
<strong>Variante 1 - Für besondere Anlässe: </strong>Das erstelle Bild kann auf der Seite
|
||||||
|
unter Upload
|
||||||
|
an den Entwickler übermittelt werden. Dieser kann das Template dann in die App integrieren.
|
||||||
|
Wichtig hierbei ist, dass nicht alle Anfragen berücksichtigt werden können.
|
||||||
|
Außerdem werden alle Rahmen öffentlich für alle sichtbar sein, sobald sie in die App
|
||||||
|
integriert wurden.
|
||||||
|
Die Bildrechte der hochgeladenen Rahmen müssen frei von Rechten Dritter sein (z.B. selber
|
||||||
|
gemalt oder selbst erstellt).
|
||||||
|
Der Entwickler übernimmt keine Haftung für die Nutzung der hochgeladenen Rahmen.
|
||||||
|
<strong>Variante 2 - Nur für Entwickler: </strong>Lege das erstellte PNG im Ordner
|
||||||
|
<code>assets/frames/</code> als <code>frame_name.png</code> ab.
|
||||||
|
Trage das Template anschließend in <code>src/data/frames.ts</code> ein
|
||||||
|
und führe einen neuen Build aus.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<button class="accordion-header">
|
<button class="accordion-header">
|
||||||
<span>Wie kann ich die Fotobox-App wieder verlassen?</span>
|
<span>Welche Drucker werden unterstützt?</span>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Da Schnappix für den sicheren Betrieb auf Events entwickelt wurde, sperrt der Kiosk-Modus das Tablet zuverlässig ab. Gäste können die App nicht beenden. Das Verlassen ist nur durch die Eingabe deines sicheren Passworts im versteckten Admin-Bereich möglich.</p>
|
<p>Es werden alle Drucker unterstützt, die das <strong>IPP (Internet Printing Protocol)</strong>
|
||||||
|
im lokalen Netzwerk unterstützen. Beliebte Fotodrucker wie der Canon CP1300 lassen sich
|
||||||
|
direkt über ihre lokale IP-Adresse ansteuern.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Kann ich die App auch ohne Drucker nutzen?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Ja, die App lässt sich auch <strong>komplett digital</strong> nutzen! Lässt du im
|
||||||
|
Admin-Bereich das Feld für die Drucker-IP-Adresse leer, schaltet die App automatisch in
|
||||||
|
einen digitalen Modus um und alle Druck-Buttons in der Benutzeroberfläche werden für die
|
||||||
|
Gäste ausgeblendet. Die Fotos werden dann rein lokal auf dem Gerät gespeichert.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Speicherung & Event-Verwaltung -->
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Wie erstelle ich ein neues Event?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>In Schnappix gibt es keine komplizierte Account- oder Event-Verwaltung. Öffne einfach den
|
||||||
|
versteckten Admin-Bereich in der App und trage dort unter "Event-Name" den Titel deiner
|
||||||
|
Veranstaltung ein (z. B. "Hochzeit Anna & Paul"). Ab diesem Moment werden alle Fotos und
|
||||||
|
Collagen automatisch unter diesem Namen verknüpft und gespeichert.
|
||||||
|
Bei Neuinstallation der App wird automatisch nach dem Event gefragt, welcher in der
|
||||||
|
Admin-Übersicht jederzeit eingesehen und geändert werden kann.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Kann ich den Event-Namen nachträglich ändern?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Ja, du kannst den Namen im Admin-Bereich jederzeit ändern. Beachte aber: Diese Namensänderung
|
||||||
|
gilt nur für zukünftige Aufnahmen. Bereits gespeicherte Fotos verbleiben in der
|
||||||
|
Galerie-Ordnerstruktur unter dem alten Event-Namen, damit deine bisherigen Dateien nicht
|
||||||
|
durcheinandergeraten.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Wo werden die Fotos gespeichert?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Schnappix nutzt die moderne Android MediaStore API und speichert Collagen sowie Originale
|
||||||
|
direkt in der Galerie deines Tablets unter den Ordnern:</p>
|
||||||
|
<ul>
|
||||||
|
<li><code>Schnappix - <EventName> - Collagen</code></li>
|
||||||
|
<li><code>Schnappix - <EventName> - Originale</code></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -157,17 +236,86 @@
|
|||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Ja, alle aufgenommenen Originalfotos und die fertigen Collagen werden automatisch und lokal in der Standard-Galerie-App deines Tablets (unter dem jeweiligen Event-Namen) abgespeichert und können jederzeit eingesehen oder exportiert werden.</p>
|
<p>Ja, alle aufgenommenen Originalfotos und die fertigen Collagen werden automatisch und lokal
|
||||||
|
in der Standard-Galerie-App deines Gerätes (unter dem jeweiligen Event-Namen) abgespeichert
|
||||||
|
und können jederzeit eingesehen oder exportiert werden.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sicherheit & Administration -->
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Wie aktiviere ich den Kiosk-Modus auf dem Tablet?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p><strong>Wichtiger Hinweis: </strong>Die Kiosk-Funktion ist nur für Entwickler gedacht!
|
||||||
|
Damit der Kiosk-Modus (LockTask) ohne Bestätigungsdialoge funktioniert, muss die App als
|
||||||
|
<strong>Device Owner</strong> festgelegt werden. Führe dazu folgenden ADB-Befehl aus:
|
||||||
|
</p>
|
||||||
|
<code>adb shell dpm set-device-owner de.orfel.schnappix/de.orfel.schnappix.KioskDeviceAdminReceiver</code>
|
||||||
|
<p>Danach kannst du den Kiosk-Modus einfach im passwortgeschützten Admin-Bereich ein- und
|
||||||
|
ausschalten.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<button class="accordion-header">
|
<button class="accordion-header">
|
||||||
<span>Ist die App auch für Smartphones geeignet?</span>
|
<span>Wie kann ich die Fotobox-App wieder verlassen?</span>
|
||||||
<i class="fa-solid fa-chevron-down"></i>
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="accordion-content">
|
<div class="accordion-content">
|
||||||
<p>Die Benutzeroberfläche von Schnappix ist primär für die Nutzung auf <strong>Tablets</strong> optimiert. Tablets bieten den Gästen auf Events einen größeren Bildschirm als Vorschau-Spiegel und eine angenehmere Bedienung für den Sticker-Editor.</p>
|
<p>Da Schnappix für den sicheren Betrieb auf Events entwickelt wurde, sperrt der Kiosk-Modus das
|
||||||
|
Tablet zuverlässig ab. Gäste können die App nicht beenden. Das Verlassen ist nur durch die
|
||||||
|
Eingabe deines sicheren Passworts im versteckten Admin-Bereich möglich.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Wo finde ich die Einstellungen in der Fotobox-App?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Die eigentliche Schnappix-App verfügt über einen versteckten Admin-Bereich.
|
||||||
|
Dieser ist durch ein Passwort (Standardmäßig: 1234) geschützt und ermöglicht es dir,
|
||||||
|
grundlegende
|
||||||
|
Parameter wie Event-Namen, Drucker-IP-Adressen oder Fotorahmen einzustellen und den
|
||||||
|
Kiosk-Modus zu steuern.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="accordion-item">
|
||||||
|
<button class="accordion-header">
|
||||||
|
<span>Welche Einstellungsfelder gibt es im Admin-Bereich der App?</span>
|
||||||
|
<i class="fa-solid fa-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
<div class="accordion-content">
|
||||||
|
<p>Der passwortgeschützte Admin-Bereich der App steuert das gesamte Verhalten der
|
||||||
|
Fotobox. Folgende Felder und Schalter stehen dir zur Verfügung:</p>
|
||||||
|
<ul>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Event-Name:</strong> Der Titel der Veranstaltung.
|
||||||
|
Dieser wird für die Datei-Ordnerstruktur genutzt und steht den Gästen als Sticker zur
|
||||||
|
Verfügung.</li>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Admin-Passwort:</strong> Erlaubt dir, das Passwort
|
||||||
|
für diesen Bereich zu ändern, damit Gäste sich keinen Zugriff verschaffen können.
|
||||||
|
Standardmäßig ist das Passwort: 1234.
|
||||||
|
Bei Vergessen ist ein Rücksetzen nur durch Neuinstallation der App möglich.</li>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Kiosk-Modus (LockTask):</strong> Ein Schalter, der
|
||||||
|
das Tablet sicher sperrt. Ist er aktiviert, gibt es keinen Zurück- oder Home-Button
|
||||||
|
mehr. Die App lässt sich erst nach Eingabe des Passworts wieder verlassen.</li>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Drucker-IP-Adresse:</strong> Trage hier die lokale
|
||||||
|
WLAN-Adresse (IPv4) deines Fotodruckers ein. Lässt du das Feld leer, schaltet die App in
|
||||||
|
einen rein digitalen Modus und versteckt alle Druck-Buttons.</li>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Fotorahmen / Template:</strong> Hier kannst du
|
||||||
|
festlegen, welche Rahmen als Überlagerung für die Collagen genutzt werden sollen.</li>
|
||||||
|
<li style="margin-bottom: 8px;"><strong>Log-Funktion:</strong> Ein Diagnose-Tool, das im
|
||||||
|
Hintergrund Ereignisse und potenzielle Abstürze protokolliert. Die Fehlerberichte
|
||||||
|
werden nicht automatisch an die Entwickler gesendet. Falls es zu Problemen kommt,
|
||||||
|
kannst du die Log-Funktion hier jederzeit aktivieren. Die Logs können dann gern
|
||||||
|
direkt mit dem Betreff "Schnappix Crash-Report" und einer kurzen Beschreibung des
|
||||||
|
Problems per E-Mail an <code>jannik.schnappix@gmail.com</code> gesendet werden.</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -195,7 +343,9 @@
|
|||||||
<label for="admin-password">Admin Passwort</label>
|
<label for="admin-password">Admin Passwort</label>
|
||||||
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
||||||
</div>
|
</div>
|
||||||
<div id="admin-login-error" style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!</div>
|
<div id="admin-login-error"
|
||||||
|
style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -212,7 +362,8 @@
|
|||||||
<button id="admin-export-btn" class="btn btn-primary">Einträge-Code exportieren</button>
|
<button id="admin-export-btn" class="btn btn-primary">Einträge-Code exportieren</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="export-container" style="display: none; margin-top: 20px;">
|
<div id="export-container" style="display: none; margin-top: 20px;">
|
||||||
<textarea id="export-textarea" rows="6" readonly style="width: 100%; font-family: monospace; font-size: 12px; background: rgba(5,5,16,0.8); border: 1px solid var(--border-color); color: var(--accent-color); padding: 10px; border-radius: 8px;"></textarea>
|
<textarea id="export-textarea" rows="6" readonly
|
||||||
|
style="width: 100%; font-family: monospace; font-size: 12px; background: rgba(5,5,16,0.8); border: 1px solid var(--border-color); color: var(--accent-color); padding: 10px; border-radius: 8px;"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -220,4 +371,5 @@
|
|||||||
|
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html" class="active-nav">Feedback Hub</a>
|
<a href="feedback.html" class="active-nav">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
|
|||||||
+35
-3
@@ -5,6 +5,29 @@ header('Content-Type: application/json');
|
|||||||
$dataDir = __DIR__ . '/data/';
|
$dataDir = __DIR__ . '/data/';
|
||||||
$dataFile = $dataDir . 'feedback.json';
|
$dataFile = $dataDir . 'feedback.json';
|
||||||
|
|
||||||
|
function getAdminPassword() {
|
||||||
|
$envVar = getenv('ADMIN_PASSWORD');
|
||||||
|
if ($envVar !== false && $envVar !== '') {
|
||||||
|
return $envVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
$envFile = __DIR__ . '/.env';
|
||||||
|
if (file_exists($envFile)) {
|
||||||
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (strpos(trim($line), '#') === 0) continue;
|
||||||
|
$parts = explode('=', $line, 2);
|
||||||
|
if (count($parts) === 2) {
|
||||||
|
if (trim($parts[0]) === 'ADMIN_PASSWORD') {
|
||||||
|
return trim($parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '1234';
|
||||||
|
}
|
||||||
|
$adminPassword = getAdminPassword();
|
||||||
|
|
||||||
// Create data directory if not exists
|
// Create data directory if not exists
|
||||||
if (!is_dir($dataDir)) {
|
if (!is_dir($dataDir)) {
|
||||||
@mkdir($dataDir, 0755, true);
|
@mkdir($dataDir, 0755, true);
|
||||||
@@ -114,12 +137,21 @@ switch ($_SERVER['REQUEST_METHOD']) {
|
|||||||
echo json_encode(['error' => 'Feedback not found']);
|
echo json_encode(['error' => 'Feedback not found']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elseif ($action === 'verify_password') {
|
||||||
|
$secret = $input['secret'] ?? '';
|
||||||
|
if ($secret === $adminPassword) {
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
elseif ($action === 'delete') {
|
elseif ($action === 'delete') {
|
||||||
$id = $input['id'] ?? '';
|
$id = $input['id'] ?? '';
|
||||||
$secret = $input['secret'] ?? '';
|
$secret = $input['secret'] ?? '';
|
||||||
|
|
||||||
// Simple authentication via secret "1234" (as defined in frontend)
|
if ($secret !== $adminPassword) {
|
||||||
if ($secret !== '1234') {
|
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
echo json_encode(['error' => 'Unauthorized']);
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
exit;
|
exit;
|
||||||
@@ -139,7 +171,7 @@ switch ($_SERVER['REQUEST_METHOD']) {
|
|||||||
$answer = $input['answer'] ?? '';
|
$answer = $input['answer'] ?? '';
|
||||||
$secret = $input['secret'] ?? '';
|
$secret = $input['secret'] ?? '';
|
||||||
|
|
||||||
if ($secret !== '1234') {
|
if ($secret !== $adminPassword) {
|
||||||
http_response_code(401);
|
http_response_code(401);
|
||||||
echo json_encode(['error' => 'Unauthorized']);
|
echo json_encode(['error' => 'Unauthorized']);
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
+10
-10
@@ -24,6 +24,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
@@ -39,21 +40,20 @@
|
|||||||
<h1 class="text-gradient" style="font-family: var(--font-titles); font-size: 36px; margin-bottom: 24px; border-bottom: 1px solid var(--border-color); padding-bottom: 12px;">Impressum</h1>
|
<h1 class="text-gradient" style="font-family: var(--font-titles); font-size: 36px; margin-bottom: 24px; border-bottom: 1px solid var(--border-color); padding-bottom: 12px;">Impressum</h1>
|
||||||
|
|
||||||
<h3 style="margin-top: 20px; font-size: 18px;">Angaben gemäß § 5 TMG</h3>
|
<h3 style="margin-top: 20px; font-size: 18px;">Angaben gemäß § 5 TMG</h3>
|
||||||
<p style="color: var(--text-muted); margin-bottom: 15px;">Jannik [Nachname]<br>
|
<p style="color: var(--text-muted); margin-bottom: 15px;">Jannik Müller<br>
|
||||||
[Straße und Hausnummer]<br>
|
Rothenthaler Straße 2<br>
|
||||||
[PLZ und Ort]</p>
|
09526 Olbernhau</p>
|
||||||
|
|
||||||
<h3 style="margin-top: 20px; font-size: 18px;">Kontakt</h3>
|
<h3 style="margin-top: 20px; font-size: 18px;">Kontakt</h3>
|
||||||
<p style="color: var(--text-muted); margin-bottom: 15px;">E-Mail: info@schnappix.de<br>
|
<p style="color: var(--text-muted); margin-bottom: 15px;">E-Mail: jannik.schnappix@gmail.com<br>
|
||||||
Telefon: [Telefonnummer - optional]</p>
|
Telefon: 015678579661</p>
|
||||||
|
|
||||||
<h3 style="margin-top: 20px; font-size: 18px;">Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h3>
|
<h3 style="margin-top: 20px; font-size: 18px;">Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h3>
|
||||||
<p style="color: var(--text-muted); margin-bottom: 15px;">Jannik [Nachname]<br>
|
<p style="color: var(--text-muted); margin-bottom: 15px;">Jannik Müller<br>
|
||||||
[Adresse wie oben]</p>
|
Rothenthaler Straße 2<br>
|
||||||
|
09526 Olbernhau</p>
|
||||||
|
|
||||||
|
|
||||||
<h3 style="margin-top: 20px; font-size: 18px;">Streitschlichtung</h3>
|
|
||||||
<p style="color: var(--text-muted); margin-bottom: 15px;">Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit: <a href="https://ec.europa.eu/consumers/odr" target="_blank" rel="noopener" style="color: var(--accent-color);">https://ec.europa.eu/consumers/odr</a>.<br>
|
|
||||||
Unsere E-Mail-Adresse finden Sie oben im Impressum. Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+25
-8
@@ -1,17 +1,22 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Schnappix - Die ultimative Photobooth App</title>
|
<title>Schnappix - Die ultimative Photobooth App</title>
|
||||||
<meta name="description" content="Schnappix ist eine Tablet-optimierte Photobooth-App mit Kiosk-Modus, Direktdruck, Filtern, Stickern und anpassbaren Rahmen für dein perfektes Event.">
|
<meta name="description"
|
||||||
|
content="Schnappix ist eine Tablet-optimierte Photobooth-App mit Kiosk-Modus, Direktdruck, Filtern, Stickern und anpassbaren Rahmen für dein perfektes Event.">
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap" rel="stylesheet">
|
<link
|
||||||
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<!-- Header Navigation -->
|
<!-- Header Navigation -->
|
||||||
@@ -25,6 +30,7 @@
|
|||||||
<a href="upload.html">Upload</a>
|
<a href="upload.html">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
@@ -38,9 +44,11 @@
|
|||||||
<div class="hero-bg-glow"></div>
|
<div class="hero-bg-glow"></div>
|
||||||
<div class="container hero-container">
|
<div class="container hero-container">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="badge">🚀 Version 23.01.X</div>
|
<div class="badge">🚀 Version 23.01.17</div>
|
||||||
<h1>Erstelle unvergessliche Momente mit <span class="text-gradient">Schnappix</span></h1>
|
<h1>Erstelle unvergessliche Momente mit <span class="text-gradient">Schnappix</span></h1>
|
||||||
<p class="hero-lead">Die professionelle, Tablet-optimierte Photobooth-App für dein Event. Kiosk-Modus, Direktdruck, kreative Sticker und maßgeschneiderte Rahmen vereint in einem atemberaubenden Synthwave-Design.</p>
|
<p class="hero-lead">Die professionelle, Tablet-optimierte Photobooth-App für dein Event. Kiosk-Modus,
|
||||||
|
Direktdruck, kreative Sticker und maßgeschneiderte Rahmen vereint in einem atemberaubenden
|
||||||
|
Synthwave-Design.</p>
|
||||||
<div class="hero-actions">
|
<div class="hero-actions">
|
||||||
<a href="features.html" class="btn btn-primary">Features entdecken</a>
|
<a href="features.html" class="btn btn-primary">Features entdecken</a>
|
||||||
<a href="feedback.html" class="btn btn-secondary">Feedback abgeben</a>
|
<a href="feedback.html" class="btn btn-secondary">Feedback abgeben</a>
|
||||||
@@ -90,7 +98,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; justify-content: center; gap: 8px; margin-bottom: 5px;">
|
<div style="display: flex; justify-content: center; gap: 8px; margin-bottom: 5px;">
|
||||||
<button class="btn btn-secondary" id="demo-btn-reset" style="padding: 6px 12px; font-size: 11px; border-radius: 6px;"><i class="fa-solid fa-rotate-left"></i> Neues Foto aufnehmen</button>
|
<button class="btn btn-secondary" id="demo-btn-reset"
|
||||||
|
style="padding: 6px 12px; font-size: 11px; border-radius: 6px;"><i
|
||||||
|
class="fa-solid fa-rotate-left"></i> Neues Foto aufnehmen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,7 +131,9 @@
|
|||||||
<label for="admin-password">Admin Passwort</label>
|
<label for="admin-password">Admin Passwort</label>
|
||||||
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
||||||
</div>
|
</div>
|
||||||
<div id="admin-login-error" style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!</div>
|
<div id="admin-login-error"
|
||||||
|
style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,8 +151,12 @@
|
|||||||
<button id="admin-export-btn" class="btn btn-primary">Einträge-Code exportieren</button>
|
<button id="admin-export-btn" class="btn btn-primary">Einträge-Code exportieren</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="export-container" style="display: none; margin-top: 20px;">
|
<div id="export-container" style="display: none; margin-top: 20px;">
|
||||||
<label style="color: var(--text-muted); font-size: 13px; display: block; margin-bottom: 8px;">Kopiere diesen Code und ersetze damit den <code>defaultFeedback</code> Block in <code>app.js</code>, um Änderungen dauerhaft für alle Besucher zu speichern:</label>
|
<label
|
||||||
<textarea id="export-textarea" rows="6" readonly style="width: 100%; font-family: monospace; font-size: 12px; background: rgba(5,5,16,0.8); border: 1px solid var(--border-color); color: var(--accent-color); padding: 10px; border-radius: 8px;"></textarea>
|
style="color: var(--text-muted); font-size: 13px; display: block; margin-bottom: 8px;">Kopiere
|
||||||
|
diesen Code und ersetze damit den <code>defaultFeedback</code> Block in <code>app.js</code>, um
|
||||||
|
Änderungen dauerhaft für alle Besucher zu speichern:</label>
|
||||||
|
<textarea id="export-textarea" rows="6" readonly
|
||||||
|
style="width: 100%; font-family: monospace; font-size: 12px; background: rgba(5,5,16,0.8); border: 1px solid var(--border-color); color: var(--accent-color); padding: 10px; border-radius: 8px;"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -148,4 +164,5 @@
|
|||||||
|
|
||||||
<script src="app.js"></script>
|
<script src="app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
+141
@@ -0,0 +1,141 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Schnappix - App Testing Anmeldung</title>
|
||||||
|
<meta name="description" content="Melde dich als App Tester für Schnappix an und hilf uns, die App für den Release vorzubereiten.">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Outfit:wght@500;700;800&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- Header Navigation -->
|
||||||
|
<header class="main-header">
|
||||||
|
<div class="container nav-container">
|
||||||
|
<div class="logo">
|
||||||
|
<a href="index.html" style="text-decoration: none;"><span class="logo-glow">Schnappix</span></a>
|
||||||
|
</div>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="features.html">Features</a>
|
||||||
|
<a href="upload.html">Upload</a>
|
||||||
|
<a href="faq.html">FAQ</a>
|
||||||
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html" class="active-nav">App Testing</a>
|
||||||
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
|
</nav>
|
||||||
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
|
<i class="fa-solid fa-bars"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Page Title -->
|
||||||
|
<div style="padding: 140px 0 20px;">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="section-title" style="margin-bottom: 20px;">Werde <span class="text-gradient">App Tester</span></h1>
|
||||||
|
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 40px;">Hilf uns beim finalen Feinschliff! Trage dich als Tester ein und erhalte exklusiven Vorabzugang zu Schnappix im Google Play Store.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tester Registration Section -->
|
||||||
|
<section class="feedback-section" style="border-top: none; padding-top: 20px; padding-bottom: 80px;">
|
||||||
|
<div class="container">
|
||||||
|
<div class="feedback-grid" style="grid-template-columns: 1fr; max-width: 800px; margin: 0 auto;">
|
||||||
|
|
||||||
|
<!-- Info Card -->
|
||||||
|
<div class="glass" style="padding: 30px; border-radius: 20px; border: 1px solid rgba(139, 92, 246, 0.3); margin-bottom: 30px; background: rgba(5, 5, 16, 0.6);">
|
||||||
|
<h3 style="color: var(--accent-color); margin-bottom: 15px; display: flex; align-items: center; gap: 10px;">
|
||||||
|
<i class="fa-solid fa-circle-info"></i> Wichtige Hinweise für Tester
|
||||||
|
</h3>
|
||||||
|
<ul style="list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 15px; color: var(--text-muted);">
|
||||||
|
<li style="display: flex; gap: 15px; align-items: flex-start;">
|
||||||
|
<i class="fa-brands fa-google" style="color: #fff; font-size: 20px; margin-top: 2px;"></i>
|
||||||
|
<div>
|
||||||
|
<strong style="color: #fff; display: block; margin-bottom: 5px;">GoogleMail-Adresse erforderlich</strong>
|
||||||
|
Bitte gib unbedingt die GoogleMail (Gmail) Adresse an, welche fest mit deinem Android-Gerät und dem Google Play Store verknüpft ist. Nur so können wir dich für das Testing freischalten.
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li style="display: flex; gap: 15px; align-items: flex-start;">
|
||||||
|
<i class="fa-solid fa-calendar-check" style="color: #fff; font-size: 20px; margin-top: 2px;"></i>
|
||||||
|
<div>
|
||||||
|
<strong style="color: #fff; display: block; margin-bottom: 5px;">Mindestens 14 Tage installiert lassen</strong>
|
||||||
|
Aufgrund der Google Play Console Richtlinien müssen 20 Tester die App für mindestens 14 Tage durchgehend installiert behalten, damit wir die Freigabe für den öffentlichen Store (Production Access) erhalten. Bitte deinstalliere die App in dieser Zeit nicht!
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submit Form -->
|
||||||
|
<div class="feedback-form-card glass">
|
||||||
|
<h3 style="margin-bottom: 20px;">Registrierung</h3>
|
||||||
|
<form id="tester-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="tester-email">Deine Google Play Store E-Mail Adresse</label>
|
||||||
|
<input type="email" id="tester-email" placeholder="deine.adresse@gmail.com" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="display: flex; gap: 10px; align-items: flex-start;">
|
||||||
|
<input type="checkbox" id="tester-consent" required style="margin-top: 5px; accent-color: var(--accent-color);">
|
||||||
|
<label for="tester-consent" style="font-size: 13px; font-weight: normal; margin-bottom: 0;">Ich habe die Hinweise gelesen und werde die App nach der Installation für mindestens 14 Tage nicht deinstallieren.</label>
|
||||||
|
</div>
|
||||||
|
<div id="tester-success-msg" style="display: none; background: rgba(34, 197, 94, 0.2); color: #4ade80; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; border: 1px solid rgba(74, 222, 128, 0.3);">
|
||||||
|
<i class="fa-solid fa-check-circle"></i> Erfolgreich eingereicht! Nach erfolgreicher Prüfung senden wir dir eine Mail mit den Registrierungslinks.
|
||||||
|
</div>
|
||||||
|
<div id="tester-error-msg" style="display: none; background: rgba(2ef, 68, 102, 0.2); color: #ff4466; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: center; border: 1px solid rgba(255, 68, 102, 0.3);">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation"></i> Es gab einen Fehler. Bitte versuche es später noch einmal.
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block" id="tester-submit-btn">Als Tester anmelden</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="main-footer">
|
||||||
|
<div class="container footer-container">
|
||||||
|
<p>© 2026 Schnappix. Alle Rechte vorbehalten.</p>
|
||||||
|
<div class="footer-links">
|
||||||
|
<a href="impressum.html">Impressum</a>
|
||||||
|
<a href="datenschutz.html">Datenschutzerklärung</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Admin Login Modal -->
|
||||||
|
<div id="admin-login-modal" class="modal-overlay">
|
||||||
|
<div class="modal-content glass">
|
||||||
|
<button class="modal-close" aria-label="Schließen">×</button>
|
||||||
|
<h2>Admin Login</h2>
|
||||||
|
<form id="admin-login-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="admin-password">Admin Passwort</label>
|
||||||
|
<input type="password" id="admin-password" placeholder="Passwort eingeben" required>
|
||||||
|
</div>
|
||||||
|
<div id="admin-login-error" style="color: #ff4466; font-size: 14px; margin-bottom: 15px; display: none;">Falsches Passwort!</div>
|
||||||
|
<button type="submit" class="btn btn-primary btn-block">Anmelden</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Admin Panel Control Modal (If Logged In) -->
|
||||||
|
<div id="admin-panel-modal" class="modal-overlay">
|
||||||
|
<div class="modal-content glass">
|
||||||
|
<button class="modal-close" aria-label="Schließen">×</button>
|
||||||
|
<h2>Admin Panel</h2>
|
||||||
|
<div class="modal-scroll-area">
|
||||||
|
<p>Sie sind als Administrator angemeldet.</p>
|
||||||
|
<div style="margin-top: 20px; display: flex; gap: 15px;">
|
||||||
|
<button id="admin-logout-btn" class="btn btn-secondary">Abmelden</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
error_reporting(0);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$inputJSON = file_get_contents('php://input');
|
||||||
|
$input = json_decode($inputJSON, true);
|
||||||
|
|
||||||
|
if (!$input || empty($input['email'])) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültige Eingabe']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = filter_var($input['email'], FILTER_SANITIZE_EMAIL);
|
||||||
|
|
||||||
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültige E-Mail Adresse']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notification to Ntfy
|
||||||
|
$ntfyUrl = 'https://ntfy.orfel.de/Schnappix-App-Testing';
|
||||||
|
$message = "E-Mail: $email";
|
||||||
|
|
||||||
|
$ch = curl_init($ntfyUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
'Title: Neue Tester-Anmeldung',
|
||||||
|
'Tags: calling,bust_in_silhouette'
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||||
|
curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['error' => 'Method not allowed']);
|
||||||
+23
-1
@@ -159,6 +159,7 @@
|
|||||||
<a href="upload.html" class="active-nav">Upload</a>
|
<a href="upload.html" class="active-nav">Upload</a>
|
||||||
<a href="faq.html">FAQ</a>
|
<a href="faq.html">FAQ</a>
|
||||||
<a href="feedback.html">Feedback Hub</a>
|
<a href="feedback.html">Feedback Hub</a>
|
||||||
|
<a href="tester.html">App Testing</a>
|
||||||
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
<a href="#" id="admin-login-trigger"><i class="fa-solid fa-user-gear"></i> Admin</a>
|
||||||
</nav>
|
</nav>
|
||||||
<button class="menu-toggle" aria-label="Menu öffnen">
|
<button class="menu-toggle" aria-label="Menu öffnen">
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="upload-card glass">
|
<div class="upload-card glass">
|
||||||
<h1 style="font-family: var(--font-titles); font-size: 32px; margin-bottom: 10px;">Hintergrund <span class="text-gradient">Upload</span></h1>
|
<h1 style="font-family: var(--font-titles); font-size: 32px; margin-bottom: 10px;">Hintergrund <span class="text-gradient">Upload</span></h1>
|
||||||
<p style="color: var(--text-muted); font-size: 15px;">Lade dein eigenes PNG hoch. Es wird uns per E-Mail gesendet und wir binden es in deine App ein.</p>
|
<p style="color: var(--text-muted); font-size: 15px;">Lade dein eigenes PNG hoch. Nach erfolgreicher Prüfung wird dein Rahmen in die App eingebaut und ist dort für alle Nutzer sichtbar.</p>
|
||||||
|
|
||||||
<!-- FormSubmit.co is used to send the email directly with attachment without a backend -->
|
<!-- FormSubmit.co is used to send the email directly with attachment without a backend -->
|
||||||
<form id="bg-upload-form">
|
<form id="bg-upload-form">
|
||||||
@@ -184,6 +185,12 @@
|
|||||||
<input type="file" id="file-input" name="background_image" accept="image/png" required>
|
<input type="file" id="file-input" name="background_image" accept="image/png" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" style="text-align: left; display: none;" id="frame-info">
|
||||||
|
<label for="frame-name">Name des Rahmens (nur Kleinbuchstaben & Zahlen)</label>
|
||||||
|
<input type="text" id="frame-name" name="frame_name" pattern="[a-z0-9_]+" placeholder="z.B. summer1" required>
|
||||||
|
<div class="upload-hint" style="margin-top: 5px;">Wird gespeichert als: <strong style="color: var(--primary-color);">frame_<span id="frame-preview-name">...</span>.png</strong></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group" style="text-align: left; display: none;" id="customer-info">
|
<div class="form-group" style="text-align: left; display: none;" id="customer-info">
|
||||||
<label for="customer-email">Deine E-Mail Adresse (für Rückfragen)</label>
|
<label for="customer-email">Deine E-Mail Adresse (für Rückfragen)</label>
|
||||||
<input type="email" id="customer-email" name="email" placeholder="mail@example.com" required>
|
<input type="email" id="customer-email" name="email" placeholder="mail@example.com" required>
|
||||||
@@ -245,8 +252,17 @@
|
|||||||
const previewBox = document.getElementById('preview-box');
|
const previewBox = document.getElementById('preview-box');
|
||||||
const imagePreview = document.getElementById('image-preview');
|
const imagePreview = document.getElementById('image-preview');
|
||||||
const submitBtn = document.getElementById('submit-btn');
|
const submitBtn = document.getElementById('submit-btn');
|
||||||
|
const frameInfo = document.getElementById('frame-info');
|
||||||
|
const frameNameInput = document.getElementById('frame-name');
|
||||||
|
const framePreviewName = document.getElementById('frame-preview-name');
|
||||||
const customerInfo = document.getElementById('customer-info');
|
const customerInfo = document.getElementById('customer-info');
|
||||||
|
|
||||||
|
frameNameInput.addEventListener('input', (e) => {
|
||||||
|
const val = e.target.value.trim().toLowerCase().replace(/[^a-z0-9_]/g, '');
|
||||||
|
e.target.value = val;
|
||||||
|
framePreviewName.textContent = val || '...';
|
||||||
|
});
|
||||||
|
|
||||||
// Drag and Drop Effects
|
// Drag and Drop Effects
|
||||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
dropZone.addEventListener(eventName, preventDefaults, false);
|
dropZone.addEventListener(eventName, preventDefaults, false);
|
||||||
@@ -295,6 +311,7 @@
|
|||||||
validationBox.style.display = 'block';
|
validationBox.style.display = 'block';
|
||||||
previewBox.style.display = 'none';
|
previewBox.style.display = 'none';
|
||||||
submitBtn.style.display = 'none';
|
submitBtn.style.display = 'none';
|
||||||
|
frameInfo.style.display = 'none';
|
||||||
customerInfo.style.display = 'none';
|
customerInfo.style.display = 'none';
|
||||||
|
|
||||||
valFormat.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Dateiformat...`;
|
valFormat.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Überprüfe Dateiformat...`;
|
||||||
@@ -327,6 +344,7 @@
|
|||||||
// Show Preview and Submit Button
|
// Show Preview and Submit Button
|
||||||
imagePreview.src = e.target.result;
|
imagePreview.src = e.target.result;
|
||||||
previewBox.style.display = 'block';
|
previewBox.style.display = 'block';
|
||||||
|
frameInfo.style.display = 'block';
|
||||||
customerInfo.style.display = 'block';
|
customerInfo.style.display = 'block';
|
||||||
submitBtn.style.display = 'block';
|
submitBtn.style.display = 'block';
|
||||||
} else {
|
} else {
|
||||||
@@ -364,8 +382,10 @@
|
|||||||
console.error('Server antwortete nicht mit gültigem JSON:', responseText);
|
console.error('Server antwortete nicht mit gültigem JSON:', responseText);
|
||||||
alert('Ein Server-Fehler ist aufgetreten (siehe Konsole). Das Bild wurde eventuell trotzdem hochgeladen.');
|
alert('Ein Server-Fehler ist aufgetreten (siehe Konsole). Das Bild wurde eventuell trotzdem hochgeladen.');
|
||||||
form.reset();
|
form.reset();
|
||||||
|
framePreviewName.textContent = '...';
|
||||||
previewBox.style.display = 'none';
|
previewBox.style.display = 'none';
|
||||||
submitBtn.style.display = 'none';
|
submitBtn.style.display = 'none';
|
||||||
|
frameInfo.style.display = 'none';
|
||||||
customerInfo.style.display = 'none';
|
customerInfo.style.display = 'none';
|
||||||
validationBox.style.display = 'none';
|
validationBox.style.display = 'none';
|
||||||
return;
|
return;
|
||||||
@@ -374,8 +394,10 @@
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
alert('Erfolgreich hochgeladen!');
|
alert('Erfolgreich hochgeladen!');
|
||||||
form.reset();
|
form.reset();
|
||||||
|
framePreviewName.textContent = '...';
|
||||||
previewBox.style.display = 'none';
|
previewBox.style.display = 'none';
|
||||||
submitBtn.style.display = 'none';
|
submitBtn.style.display = 'none';
|
||||||
|
frameInfo.style.display = 'none';
|
||||||
customerInfo.style.display = 'none';
|
customerInfo.style.display = 'none';
|
||||||
validationBox.style.display = 'none';
|
validationBox.style.display = 'none';
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+42
-4
@@ -36,10 +36,48 @@ if ($mimeType !== 'image/png') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate unique filename
|
$rawFrameName = isset($_POST['frame_name']) ? trim($_POST['frame_name']) : '';
|
||||||
$filename = uniqid('frame_') . '_' . time() . '.png';
|
$frameName = preg_replace('/[^a-z0-9_]/', '', strtolower($rawFrameName));
|
||||||
|
|
||||||
|
if (empty($frameName)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ungültiger oder fehlender Frame-Name.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filename = 'frame_' . $frameName . '.png';
|
||||||
$destination = $uploadDir . $filename;
|
$destination = $uploadDir . $filename;
|
||||||
|
|
||||||
|
// Check local uniqueness (already uploaded via web)
|
||||||
|
if (file_exists($destination)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ein Rahmen mit diesem Namen existiert bereits auf dem Server. Bitte wähle einen anderen.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Gitea uniqueness
|
||||||
|
$giteaUrl = 'https://git.orfel.de/api/v1/repos/Jannik/Schnappix/contents/assets/frames';
|
||||||
|
$chGitea = curl_init($giteaUrl);
|
||||||
|
curl_setopt($chGitea, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($chGitea, CURLOPT_TIMEOUT, 5);
|
||||||
|
curl_setopt($chGitea, CURLOPT_HTTPHEADER, ['User-Agent: Schnappix-Web Backend']);
|
||||||
|
$giteaResponse = curl_exec($chGitea);
|
||||||
|
$giteaCode = curl_getinfo($chGitea, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($chGitea);
|
||||||
|
|
||||||
|
if ($giteaCode === 200 && $giteaResponse) {
|
||||||
|
$framesData = json_decode($giteaResponse, true);
|
||||||
|
if (is_array($framesData)) {
|
||||||
|
foreach ($framesData as $frameFile) {
|
||||||
|
if (isset($frameFile['name']) && strtolower($frameFile['name']) === strtolower($filename)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'Ein Rahmen mit diesem Namen existiert bereits in der App. Bitte wähle einen anderen.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move uploaded file
|
// Move uploaded file
|
||||||
if (move_uploaded_file($file['tmp_name'], $destination)) {
|
if (move_uploaded_file($file['tmp_name'], $destination)) {
|
||||||
// Build public URL
|
// Build public URL
|
||||||
@@ -50,14 +88,14 @@ if (move_uploaded_file($file['tmp_name'], $destination)) {
|
|||||||
|
|
||||||
// Send notification to Ntfy
|
// Send notification to Ntfy
|
||||||
$ntfyUrl = 'https://ntfy.orfel.de/Schnappix-Frame-Upload';
|
$ntfyUrl = 'https://ntfy.orfel.de/Schnappix-Frame-Upload';
|
||||||
$message = "Ein neuer Rahmen wurde hochgeladen!\n\nVon: $email\nBild-URL: $publicUrl";
|
$message = "Name: $filename\nVon: $email\nBild-URL: $publicUrl";
|
||||||
|
|
||||||
$ch = curl_init($ntfyUrl);
|
$ch = curl_init($ntfyUrl);
|
||||||
curl_setopt($ch, CURLOPT_POST, 1);
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
'Title: Neuer Schnappix Hintergrund',
|
'Title: Neuer Schnappix Hintergrund',
|
||||||
'Tags: frame_with_picture,tada',
|
'Tags: framed_picture,tada',
|
||||||
'Click: ' . $publicUrl,
|
'Click: ' . $publicUrl,
|
||||||
'Attach: ' . $publicUrl
|
'Attach: ' . $publicUrl
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user