Ergänzung Datenschutzerklärung und Impressum, Fristenformular in Einstellungen
This commit is contained in:
@@ -1465,3 +1465,94 @@ button.archive-head .chev { color: var(--muted); }
|
||||
.reg-toggle .checkline { margin-top: 0; }
|
||||
|
||||
@media print { ul.suggestions, .card.archive { display: none !important; } }
|
||||
|
||||
/* ===================================================================
|
||||
Fußzeile mit Impressum und Datenschutz
|
||||
=================================================================== */
|
||||
|
||||
.app-footer {
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 1rem calc(1.5rem + env(safe-area-inset-bottom));
|
||||
text-align: center;
|
||||
font-size: .82rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.app-footer a { color: var(--muted); }
|
||||
.app-footer span { margin: 0 .4rem; }
|
||||
|
||||
/* Über dem Sucher und über Dialogen darf sie nicht durchscheinen. */
|
||||
body.scanning .app-footer,
|
||||
body.modal-open .app-footer { display: none; }
|
||||
|
||||
/* ===================================================================
|
||||
Rechtliche Seiten
|
||||
=================================================================== */
|
||||
|
||||
.legal-text { line-height: 1.6; }
|
||||
|
||||
.legal-text h1 { margin-bottom: .2rem; }
|
||||
|
||||
.legal-text h2 {
|
||||
margin: 2rem 0 .5rem;
|
||||
font-size: 1.05rem;
|
||||
padding-bottom: .3rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.legal-text h3 { margin: 1.4rem 0 .4rem; font-size: .95rem; }
|
||||
|
||||
.legal-text p { margin: .6rem 0; }
|
||||
.legal-text ul { margin: .6rem 0; padding-left: 1.2rem; }
|
||||
.legal-text li { margin: .25rem 0; }
|
||||
|
||||
.legal-text table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: .8rem 0;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.legal-text th,
|
||||
.legal-text td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
padding: .45rem .5rem .45rem 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.legal-text th { font-size: .8rem; text-transform: uppercase; color: var(--muted); }
|
||||
|
||||
/* Hinweis, dass es sich um einen Entwurf handelt - soll nach dem
|
||||
Ausfüllen entfernt werden. */
|
||||
.legal-text .draft {
|
||||
background: var(--warn-bg);
|
||||
border: 1px solid var(--warn-line);
|
||||
border-radius: var(--radius);
|
||||
padding: .8rem 1rem;
|
||||
margin: 0 0 1.5rem;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
/* Erläuterungen zur Rechtslage - gehören nicht in den fertigen Text. */
|
||||
.legal-text .hint {
|
||||
font-size: .85rem;
|
||||
color: var(--muted);
|
||||
border-left: 2px solid var(--line);
|
||||
padding-left: .8rem;
|
||||
margin: .5rem 0 1rem;
|
||||
}
|
||||
|
||||
.legal-text code {
|
||||
background: rgba(127, 127, 127, .18);
|
||||
padding: .1em .35em;
|
||||
border-radius: 3px;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
@media print {
|
||||
/* Im Ausdruck haben Rechtslinks nichts zu suchen - er soll die
|
||||
Einkaufsliste zeigen, nicht die Fußzeile der Anwendung. */
|
||||
.app-footer { display: none !important; }
|
||||
}
|
||||
|
||||
316
web/html/datenschutz.html
Normal file
316
web/html/datenschutz.html
Normal file
@@ -0,0 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title>Datenschutzerklärung</title>
|
||||
<link rel="stylesheet" href="/app.css">
|
||||
<link rel="icon" href="/icons/favicon.ico" sizes="any">
|
||||
</head>
|
||||
<body class="legal">
|
||||
<main id="app">
|
||||
|
||||
<header class="app-bar">
|
||||
<a class="bar-back" href="/">‹ Zurück zur App</a>
|
||||
</header>
|
||||
|
||||
<article class="card legal-text">
|
||||
|
||||
<p class="draft">
|
||||
<strong>Entwurf.</strong> Dieser Text beschreibt die Verarbeitungen, die
|
||||
diese Anwendung technisch vornimmt – vollständig und nach bestem Wissen,
|
||||
aber ohne Rechtsberatung. Vor der Veröffentlichung sollte er von jemandem
|
||||
geprüft werden, der dazu befugt ist. Alle mit <code>[…]</code> markierten
|
||||
Stellen müssen ausgefüllt werden.
|
||||
</p>
|
||||
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p class="lead">Stand: [Datum]</p>
|
||||
|
||||
<h2>1. Verantwortlicher</h2>
|
||||
<p>
|
||||
Verantwortlich für die Verarbeitung personenbezogener Daten im Sinne von
|
||||
Art. 4 Nr. 7 DSGVO ist:
|
||||
</p>
|
||||
<p>
|
||||
[Vorname Nachname]<br>
|
||||
[Straße und Hausnummer]<br>
|
||||
[PLZ Ort]<br>
|
||||
E-Mail: [E-Mail-Adresse]<br>
|
||||
Telefon: [Telefonnummer]
|
||||
</p>
|
||||
<p>
|
||||
[Falls benannt:] Datenschutzbeauftragte/r: [Name, Kontakt]. Ein
|
||||
Datenschutzbeauftragter ist nur unter den Voraussetzungen des Art. 37 DSGVO
|
||||
und § 38 BDSG zu benennen; für eine privat oder in kleinem Rahmen betriebene
|
||||
Anwendung ist das regelmäßig nicht der Fall.
|
||||
</p>
|
||||
|
||||
<h2>2. Grundsatz</h2>
|
||||
<p>
|
||||
Diese Anwendung ist auf Datensparsamkeit ausgelegt. Es findet keine
|
||||
Reichweitenmessung, kein Tracking, keine Profilbildung und keine Werbung
|
||||
statt. Es werden keine Inhalte von fremden Servern nachgeladen – weder
|
||||
Schriftarten noch Skripte, Karten oder Symbole. Alle Darstellungsmittel
|
||||
liegen auf demselben Server wie die Anwendung; für die Schrift wird die
|
||||
auf dem Gerät bereits vorhandene Systemschrift verwendet.
|
||||
</p>
|
||||
|
||||
<h2>3. Welche Daten verarbeitet werden</h2>
|
||||
|
||||
<h3>3.1 Benutzerkonto</h3>
|
||||
<table>
|
||||
<tr><th>Angabe</th><th>Pflicht</th><th>Zweck</th></tr>
|
||||
<tr><td>E-Mail-Adresse</td><td>ja</td><td>Anmeldung, Bestätigung, Einladungen, Passwort zurücksetzen</td></tr>
|
||||
<tr><td>Passwort</td><td>ja</td><td>Wird ausschließlich als Streuwert (Argon2id) gespeichert, nie im Klartext</td></tr>
|
||||
<tr><td>Anzeigename</td><td>nein</td><td>Darstellung gegenüber anderen Mitgliedern; ein Pseudonym genügt</td></tr>
|
||||
<tr><td>Zeitpunkt der Registrierung und der letzten Nutzung</td><td>automatisch</td><td>Bestätigung des Kontos, automatische Bereinigung ungenutzter Konten</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
<strong>Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. b DSGVO – die
|
||||
Verarbeitung ist zur Bereitstellung der Anwendung erforderlich.
|
||||
</p>
|
||||
|
||||
<h3>3.2 Inhalte, die Sie selbst eingeben</h3>
|
||||
<p>
|
||||
Einkaufslisten, Artikelbezeichnungen, Mengen, Eigenschaften, Notizen,
|
||||
Märkte, Warengruppen und Preise. Bei geteilten Listen ist für die übrigen
|
||||
Mitglieder erkennbar, wer einen Eintrag angelegt hat.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Bitte beachten:</strong> Welche Angaben in einer Liste stehen,
|
||||
bestimmen ausschließlich Sie. Tragen Sie dort Hinweise auf Gesundheit,
|
||||
religiöse Überzeugungen oder Ähnliches ein – etwa Medikamente oder
|
||||
Speisevorschriften –, verarbeiten Sie besondere Kategorien
|
||||
personenbezogener Daten im Sinne des Art. 9 DSGVO. Der Betreiber hat darauf
|
||||
keinen Einfluss und rät davon ab, solche Angaben aufzunehmen.
|
||||
</p>
|
||||
<p><strong>Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. b DSGVO.</p>
|
||||
|
||||
<h3>3.3 Einladungen</h3>
|
||||
<p>
|
||||
Wenn Sie eine Liste mit einer anderen Person teilen, geben Sie deren
|
||||
E-Mail-Adresse an. Diese wird gespeichert, um die Einladung zuzustellen und
|
||||
ihren Stand nachvollziehbar zu machen (versendet, angenommen, widerrufen).
|
||||
Nicht angenommene Einladungen laufen nach [14] Tagen ab; der Datensatz
|
||||
wird [30] Tage <em>nach Ablauf</em> gelöscht (zusammen also [44] Tage).
|
||||
</p>
|
||||
<p>
|
||||
<strong>Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. f DSGVO – berechtigtes
|
||||
Interesse an der gemeinsamen Nutzung. Die eingeladene Person wird mit der
|
||||
Einladung über die Verarbeitung informiert.
|
||||
</p>
|
||||
|
||||
<h3>3.4 Sitzungen und Cookies</h3>
|
||||
<p>Es werden ausschließlich zwei technisch notwendige Cookies gesetzt:</p>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Zweck</th><th>Dauer</th></tr>
|
||||
<tr><td><code>ea_session</code></td><td>Erkennung der angemeldeten Sitzung; <code>HttpOnly</code>, für Skripte unlesbar</td><td>[30] Tage</td></tr>
|
||||
<tr><td><code>ea_csrf</code></td><td>Schutz vor seitenübergreifend untergeschobenen Anfragen</td><td>[30] Tage</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
Beide sind zur Erbringung des ausdrücklich gewünschten Dienstes unbedingt
|
||||
erforderlich; eine Einwilligung ist nach § 25 Abs. 2 Nr. 2 TDDDG nicht
|
||||
erforderlich. Es werden keine Cookies zu Analyse- oder Werbezwecken gesetzt.
|
||||
</p>
|
||||
|
||||
<h3>3.5 Speicherung auf Ihrem Gerät</h3>
|
||||
<p>
|
||||
Damit die Anwendung ohne Verbindung nutzbar bleibt, legt sie den zuletzt
|
||||
bekannten Stand Ihrer Listen sowie noch nicht übertragene Änderungen im
|
||||
lokalen Speicher des Browsers (IndexedDB) ab. Diese Daten verlassen Ihr
|
||||
Gerät nur, um sie mit dem Server abzugleichen, und lassen sich über die
|
||||
Browsereinstellungen jederzeit löschen.
|
||||
</p>
|
||||
|
||||
<h3>3.6 Protokolle und Missbrauchsabwehr</h3>
|
||||
<p>
|
||||
Der Webserver protokolliert jeden Abruf mit <strong>gekürzter</strong>
|
||||
IP-Adresse (letzter Block auf Null gesetzt), Zeitpunkt, angefordertem Pfad,
|
||||
Statuscode und Browserkennung. Aufrufe von Einladungs-, Freigabe- und
|
||||
Passwortlinks werden gar nicht protokolliert, da deren Adresse den
|
||||
Zugangsschlüssel enthält. Aufbewahrung: [7] Tage.
|
||||
</p>
|
||||
<p>
|
||||
Zur Abwehr von Anmeldeversuchen in großer Zahl werden Zähler geführt. Als
|
||||
Schlüssel dient ein Streuwert aus IP- beziehungsweise E-Mail-Adresse und
|
||||
einem zufälligen, installationsweiten Zusatz; die Adressen selbst werden
|
||||
dafür nicht gespeichert. Die Zähler werden spätestens nach 24 Stunden
|
||||
gelöscht.
|
||||
</p>
|
||||
<p><strong>Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. f DSGVO – berechtigtes
|
||||
Interesse am sicheren Betrieb.</p>
|
||||
|
||||
<h3>3.7 Öffentlich geteilte Listen</h3>
|
||||
<p>
|
||||
Sie können eine Liste über einen Link freigeben. Wer den Link hat, kann die
|
||||
Liste ansehen und – sofern beim Erzeugen erlaubt – Artikel abhaken. In
|
||||
dieser Ansicht wird <strong>nicht</strong> angezeigt, wer welchen Eintrag
|
||||
angelegt hat. Der Link hat stets ein Ablaufdatum und ist jederzeit
|
||||
widerrufbar. Erfasst wird lediglich, wie oft und wann zuletzt er geöffnet
|
||||
wurde – ohne IP-Adresse und ohne Zeitreihe.
|
||||
</p>
|
||||
|
||||
<h3>3.8 Push-Benachrichtigungen</h3>
|
||||
<p>
|
||||
Nur wenn Sie sie ausdrücklich einschalten. Dabei wird eine vom Push-Dienst
|
||||
Ihres Browserherstellers vergebene Adresse gespeichert, zusammen mit dem
|
||||
Schlüsselmaterial zur Verschlüsselung und einer groben Gerätebezeichnung
|
||||
(z. B. „Chrome auf Android"). Der Inhalt der Nachricht ist
|
||||
Ende-zu-Ende-verschlüsselt und kann vom Push-Dienst nicht gelesen werden.
|
||||
Übermittelt wird lediglich der Name der geänderten Liste und wer sie
|
||||
geändert hat – keine Artikelbezeichnungen.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Empfänger:</strong> Je nach Browser <em>Google LLC</em> (Chrome,
|
||||
Android), <em>Apple Inc.</em> (Safari) oder die <em>Mozilla Foundation</em>
|
||||
(Firefox). Dabei können Daten in die USA übermittelt werden; die Anbieter
|
||||
stützen sich auf den EU-US Data Privacy Framework beziehungsweise auf
|
||||
Standardvertragsklauseln nach Art. 46 DSGVO.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Rechtsgrundlage:</strong> Art. 6 Abs. 1 lit. a DSGVO – Einwilligung.
|
||||
Sie können sie jederzeit unter „Einstellungen → Benachrichtigungen"
|
||||
widerrufen; die Anmeldung wird dann gelöscht.
|
||||
</p>
|
||||
|
||||
<h2>4. Externe Dienste</h2>
|
||||
|
||||
<h3>4.1 Hosting</h3>
|
||||
<p>
|
||||
Die Anwendung wird betrieben bei:
|
||||
</p>
|
||||
<p>
|
||||
[Name des Anbieters]<br>
|
||||
[Anschrift]<br>
|
||||
[Land]
|
||||
</p>
|
||||
<p>
|
||||
Der Anbieter verarbeitet die Daten ausschließlich weisungsgebunden. Es
|
||||
besteht ein Vertrag zur Auftragsverarbeitung nach Art. 28 DSGVO.
|
||||
[Falls der Serverstandort abweicht, hier angeben.]
|
||||
</p>
|
||||
|
||||
<h3>4.2 E-Mail-Versand</h3>
|
||||
<p>
|
||||
Für Bestätigungen, Einladungen und Hinweise wird ein SMTP-Server genutzt:
|
||||
[Name und Anschrift des Anbieters – bei einem selbst betriebenen Server:
|
||||
„Der Versand erfolgt über einen eigenen Server des Verantwortlichen; ein
|
||||
Dritter ist nicht beteiligt."]. Übermittelt werden Empfängeradresse,
|
||||
Betreff und Inhalt der Nachricht.
|
||||
</p>
|
||||
|
||||
<h3>4.3 Produktdatenbank (Strichcodes)</h3>
|
||||
<p>
|
||||
Beim Scannen eines Strichcodes wird die Ziffernfolge bei
|
||||
<em>Open Food Facts</em> nachgeschlagen, um Bezeichnung und Packungsgröße
|
||||
vorzuschlagen. Betreiber: Open Food Facts, 21 rue des Iles, 94100
|
||||
Saint-Maur-des-Fossés, Frankreich.
|
||||
</p>
|
||||
<p>
|
||||
Die Abfrage erfolgt <strong>durch den Server dieser Anwendung</strong>,
|
||||
nicht durch Ihr Gerät. Open Food Facts erfährt dadurch weder Ihre
|
||||
IP-Adresse noch, wer den Code gescannt hat. Übermittelt wird ausschließlich
|
||||
die Ziffernfolge. Ergebnisse werden zwischengespeichert, sodass derselbe
|
||||
Code höchstens einmal je Gültigkeitszeitraum abgefragt wird.
|
||||
[Falls abgeschaltet (<code>PRODUCT_LOOKUP=off</code>), diesen Abschnitt
|
||||
streichen.]
|
||||
</p>
|
||||
|
||||
<h3>4.4 Keine weiteren Dienste</h3>
|
||||
<p>
|
||||
Es werden keine Analysedienste, keine Werbenetzwerke, keine sozialen
|
||||
Netzwerke, keine Kartendienste und keine externen Schriftarten eingebunden.
|
||||
</p>
|
||||
|
||||
<h2>5. Empfänger innerhalb der Anwendung</h2>
|
||||
<p>
|
||||
Wenn Sie eine Liste teilen, sehen die eingeladenen Personen deren Inhalt,
|
||||
Ihren Anzeigenamen beziehungsweise Ihre E-Mail-Adresse sowie welche
|
||||
Einträge von Ihnen stammen. Der Eigentümer einer Liste sieht die
|
||||
E-Mail-Adressen aller Mitglieder. Personen mit Administratorrechten haben
|
||||
zur Verwaltung Zugriff auf die Liste aller Konten samt E-Mail-Adressen und
|
||||
Nutzungszeitpunkten; Listeninhalte sehen sie nur, soweit sie selbst
|
||||
Mitglied sind.
|
||||
</p>
|
||||
|
||||
<h2>6. Speicherdauer</h2>
|
||||
<table>
|
||||
<tr><th>Daten</th><th>Dauer</th></tr>
|
||||
<tr><td>Konto und Inhalte</td><td>Bis zur Löschung des Kontos</td></tr>
|
||||
<tr><td>Konten ohne Anmeldung</td><td>Deaktivierung nach [12] Monaten, Löschung [12] Monate danach</td></tr>
|
||||
<tr><td>Listen ohne Nutzung</td><td>Archivierung nach [12] Monaten, Löschung [12] Monate danach; der Eigentümer wird 7 Tage und 1 Tag vorher benachrichtigt</td></tr>
|
||||
<tr><td>Gelöschte Einträge und Listen</td><td>Endgültige Entfernung nach [30] Tagen – die Frist stellt sicher, dass auch länger offline gebliebene Geräte vom Löschen erfahren</td></tr>
|
||||
<tr><td>Sitzungen</td><td>[30] Tage, bei Abmeldung sofort</td></tr>
|
||||
<tr><td>Bestätigungs- und Einladungslinks</td><td>[1] Stunde (Passwort) bis [14] Tage (Einladung, Willkommensgruß); Adresswechsel [48] Stunden; Reste [30] Tage nach Ablauf entfernt</td></tr>
|
||||
<tr><td>Zähler der Missbrauchsabwehr</td><td>24 Stunden</td></tr>
|
||||
<tr><td>Zugriffsprotokolle</td><td>[7] Tage</td></tr>
|
||||
<tr><td>Erfasste Preise</td><td>Unbefristet – sie enthalten keinen Personenbezug (nur Markt, Artikel, Betrag, Zeitpunkt)</td></tr>
|
||||
<tr><td>Zwischenspeicher der Produktdatenbank</td><td>[180] Tage, danach erneute Abfrage</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
Beim Löschen eines Kontos gehen Listen, die mit anderen geteilt sind, an
|
||||
das dienstälteste andere Mitglied über; Listen ohne weitere Mitglieder
|
||||
werden gelöscht. Bei Einträgen anderer Personen wird der Hinweis auf die
|
||||
Urheberschaft entfernt, der Eintrag selbst bleibt bestehen.
|
||||
</p>
|
||||
|
||||
<h2>7. Ihre Rechte</h2>
|
||||
<p>Sie haben nach der DSGVO das Recht auf:</p>
|
||||
<ul>
|
||||
<li>Auskunft über die zu Ihnen gespeicherten Daten (Art. 15)</li>
|
||||
<li>Berichtigung unrichtiger Daten (Art. 16)</li>
|
||||
<li>Löschung (Art. 17)</li>
|
||||
<li>Einschränkung der Verarbeitung (Art. 18)</li>
|
||||
<li>Datenübertragbarkeit (Art. 20)</li>
|
||||
<li>Widerspruch gegen Verarbeitungen auf Grundlage berechtigter Interessen (Art. 21)</li>
|
||||
<li>Widerruf erteilter Einwilligungen mit Wirkung für die Zukunft (Art. 7 Abs. 3)</li>
|
||||
</ul>
|
||||
<p>
|
||||
Wenden Sie sich dafür an die oben genannte Adresse. Anzeigename und
|
||||
Passwort können Sie selbst unter „Einstellungen" ändern; für die Änderung
|
||||
der E-Mail-Adresse und die Löschung des Kontos wenden Sie sich an die
|
||||
Administration.
|
||||
</p>
|
||||
<p>
|
||||
Unabhängig davon steht Ihnen ein Beschwerderecht bei einer
|
||||
Datenschutz-Aufsichtsbehörde zu (Art. 77 DSGVO), etwa bei der für
|
||||
[Bundesland] zuständigen Behörde: [Name und Anschrift der Behörde].
|
||||
</p>
|
||||
|
||||
<h2>8. Erforderlichkeit der Angaben</h2>
|
||||
<p>
|
||||
Die Angabe der E-Mail-Adresse und eines Passworts ist zur Nutzung
|
||||
erforderlich; ohne sie kann kein Konto geführt werden. Alle übrigen
|
||||
Angaben sind freiwillig. Eine automatisierte Entscheidungsfindung
|
||||
einschließlich Profilbildung nach Art. 22 DSGVO findet nicht statt.
|
||||
</p>
|
||||
|
||||
<h2>9. Überprüfbarkeit der Fristen</h2>
|
||||
<p>
|
||||
Sämtliche in dieser Erklärung genannten Fristen sind in der Anwendung
|
||||
hinterlegt und lassen sich vom Betreiber unter „Einstellungen →
|
||||
Benutzerverwaltung → Fristen" einsehen und ändern. Ein Aufräumlauf setzt sie
|
||||
einmal täglich um. Weichen die hier genannten Zahlen von den dort
|
||||
eingestellten ab, gelten die eingestellten – bitte melden Sie eine solche
|
||||
Abweichung, damit dieser Text nachgezogen wird.
|
||||
</p>
|
||||
<p class="hint">
|
||||
Hinweis für den Betreiber: Nach jeder Änderung an den Fristen sind die mit
|
||||
<code>[…]</code> markierten Zahlen in dieser Erklärung anzupassen. Die
|
||||
Aufstellung unter „Fristen" zeigt die geltenden Werte.
|
||||
</p>
|
||||
|
||||
<h2>10. Änderungen</h2>
|
||||
<p>
|
||||
Diese Erklärung wird angepasst, wenn sich die Anwendung ändert. Maßgeblich
|
||||
ist die hier veröffentlichte Fassung; das Datum steht oben.
|
||||
</p>
|
||||
|
||||
<p class="switch"><a href="/impressum.html">Impressum</a> · <a href="/">Zurück zur App</a></p>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
130
web/html/impressum.html
Normal file
130
web/html/impressum.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<title>Impressum</title>
|
||||
<link rel="stylesheet" href="/app.css">
|
||||
<link rel="icon" href="/icons/favicon.ico" sizes="any">
|
||||
</head>
|
||||
<body class="legal">
|
||||
<main id="app">
|
||||
|
||||
<header class="app-bar">
|
||||
<a class="bar-back" href="/">‹ Zurück zur App</a>
|
||||
</header>
|
||||
|
||||
<article class="card legal-text">
|
||||
|
||||
<p class="draft">
|
||||
<strong>Entwurf.</strong> Alle mit <code>[…]</code> markierten Stellen
|
||||
müssen ausgefüllt werden; nicht zutreffende Abschnitte sind zu streichen.
|
||||
Dies ist keine Rechtsberatung – lassen Sie den Text vor der
|
||||
Veröffentlichung von jemandem prüfen, der dazu befugt ist.
|
||||
</p>
|
||||
|
||||
<h1>Impressum</h1>
|
||||
|
||||
<h2>Angaben gemäß § 5 DDG</h2>
|
||||
<p>
|
||||
[Vorname Nachname]<br>
|
||||
[ggf. Firmierung, z. B. „Muster GmbH"]<br>
|
||||
[Straße und Hausnummer]<br>
|
||||
[PLZ Ort]<br>
|
||||
[Land, falls nicht Deutschland]
|
||||
</p>
|
||||
|
||||
<h2>Kontakt</h2>
|
||||
<p>
|
||||
Telefon: [Telefonnummer]<br>
|
||||
E-Mail: [E-Mail-Adresse]
|
||||
</p>
|
||||
<p class="hint">
|
||||
Eine E-Mail-Adresse und eine zweite Möglichkeit zur unmittelbaren
|
||||
Kommunikation – üblicherweise eine Telefonnummer – sind nach § 5 Abs. 1
|
||||
Nr. 2 DDG verpflichtend. Ein bloßes Kontaktformular genügt nicht.
|
||||
</p>
|
||||
|
||||
<h2>Vertretungsberechtigt</h2>
|
||||
<p>
|
||||
[Nur bei juristischen Personen: Name der vertretungsberechtigten Person,
|
||||
z. B. Geschäftsführer. Bei Einzelpersonen streichen.]
|
||||
</p>
|
||||
|
||||
<h2>Registereintrag</h2>
|
||||
<p>
|
||||
[Nur falls vorhanden:]<br>
|
||||
Registergericht: [Amtsgericht]<br>
|
||||
Registernummer: [HRB …]
|
||||
</p>
|
||||
|
||||
<h2>Umsatzsteuer-Identifikationsnummer</h2>
|
||||
<p>
|
||||
[Nur falls vorhanden:] Umsatzsteuer-Identifikationsnummer gemäß § 27 a
|
||||
Umsatzsteuergesetz: [DE…]
|
||||
</p>
|
||||
|
||||
<h2>Aufsichtsbehörde</h2>
|
||||
<p>
|
||||
[Nur bei zulassungspflichtigen Tätigkeiten:] [Name und Anschrift der
|
||||
zuständigen Aufsichtsbehörde]
|
||||
</p>
|
||||
|
||||
<h2>Verantwortlich für den Inhalt</h2>
|
||||
<p>
|
||||
[Vorname Nachname]<br>
|
||||
[Anschrift wie oben]
|
||||
</p>
|
||||
<p class="hint">
|
||||
Die frühere Angabe „nach § 55 Abs. 2 RStV" ist überholt; der
|
||||
Rundfunkstaatsvertrag wurde 2020 durch den Medienstaatsvertrag abgelöst.
|
||||
Eine gesonderte Angabe nach § 18 Abs. 2 MStV ist nur bei journalistisch
|
||||
redaktionell gestalteten Angeboten erforderlich – für eine Einkaufsliste
|
||||
regelmäßig nicht. Bei Zweifeln kann die Angabe stehen bleiben; sie schadet
|
||||
nicht.
|
||||
</p>
|
||||
|
||||
<h2>Streitbeilegung</h2>
|
||||
<p>
|
||||
[Nur bei Angeboten gegenüber Verbrauchern im geschäftlichen Verkehr:]
|
||||
Wir sind weder bereit noch verpflichtet, an Streitbeilegungsverfahren vor
|
||||
einer Verbraucherschlichtungsstelle teilzunehmen.
|
||||
</p>
|
||||
<p class="hint">
|
||||
Die Plattform der EU-Kommission zur Online-Streitbeilegung wurde zum
|
||||
20. Juli 2025 eingestellt; ein Hinweis darauf ist nicht mehr erforderlich
|
||||
und sollte aus älteren Impressen entfernt werden. Wird die Anwendung rein
|
||||
privat und ohne Entgelt betrieben, entfällt dieser Abschnitt ganz.
|
||||
</p>
|
||||
|
||||
<h2>Haftung für Inhalte</h2>
|
||||
<p>
|
||||
Die Inhalte der Einkaufslisten werden ausschließlich von den Nutzerinnen
|
||||
und Nutzern erstellt. Der Betreiber überwacht sie nicht und ist nach
|
||||
§ 7 Abs. 2 DDG nicht verpflichtet, übermittelte oder gespeicherte fremde
|
||||
Informationen zu überwachen. Verpflichtungen zur Entfernung oder Sperrung
|
||||
bei Kenntnis einer konkreten Rechtsverletzung bleiben unberührt; ab
|
||||
Kenntnis wird der entsprechende Inhalt umgehend entfernt.
|
||||
</p>
|
||||
|
||||
<h2>Haftung für Links</h2>
|
||||
<p>
|
||||
Diese Anwendung verweist nicht auf fremde Seiten. Sollten Nutzerinnen und
|
||||
Nutzer Verweise in Listeneinträge aufnehmen, hat der Betreiber darauf keinen
|
||||
Einfluss und übernimmt für deren Inhalt keine Haftung.
|
||||
</p>
|
||||
|
||||
<h2>Urheberrecht</h2>
|
||||
<p>
|
||||
Der Quelltext dieser Anwendung steht unter [Lizenz angeben, z. B. „MIT-Lizenz"
|
||||
– oder: „Alle Rechte vorbehalten"]. Produktdaten zu Strichcodes stammen von
|
||||
Open Food Facts und stehen unter der Open Database License (ODbL).
|
||||
</p>
|
||||
|
||||
<p class="switch"><a href="/datenschutz.html">Datenschutzerklärung</a> · <a href="/">Zurück zur App</a></p>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -28,6 +28,15 @@
|
||||
<p class="error">Diese App benötigt JavaScript.</p>
|
||||
</noscript>
|
||||
|
||||
<!-- Außerhalb von #app: Die Ansichten tauschen nur den Inhalt von #app
|
||||
aus, die Fußzeile bleibt dadurch überall stehen - auch auf der
|
||||
Anmeldeseite und in der öffentlichen Listenansicht. -->
|
||||
<footer class="app-footer">
|
||||
<a href="/impressum.html">Impressum</a>
|
||||
<span aria-hidden="true">·</span>
|
||||
<a href="/datenschutz.html">Datenschutz</a>
|
||||
</footer>
|
||||
|
||||
<script type="module" src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -296,57 +296,66 @@ export async function adminView(root, { back }) {
|
||||
// Automatik
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
function automationCard() {
|
||||
const deactivate = el("input.months", {
|
||||
type: "number", min: "0", max: "600",
|
||||
value: String(config.auto_deactivate_months),
|
||||
});
|
||||
const remove = el("input.months", {
|
||||
type: "number", min: "0", max: "600",
|
||||
value: String(config.auto_delete_months),
|
||||
});
|
||||
const archive = el("input.months", {
|
||||
type: "number", min: "0", max: "600",
|
||||
value: String(config.auto_archive_months),
|
||||
});
|
||||
const archiveDelete = el("input.months", {
|
||||
type: "number", min: "0", max: "600",
|
||||
value: String(config.archive_delete_months),
|
||||
});
|
||||
/** Fristen aus dem Server: Beschriftung, Einheit und Grenzen kommen
|
||||
* von dort, damit nicht das eine steht und das andere gilt. */
|
||||
function durationsCard() {
|
||||
const fields = new Map();
|
||||
|
||||
const group = (title, keys, lead) => {
|
||||
const rows = config.durations.filter((d) => keys.includes(d.key));
|
||||
if (!rows.length) return null;
|
||||
|
||||
return el("div.duration-group", {},
|
||||
el("h3.device-heading", {}, title),
|
||||
lead ? el("p.sub", {}, lead) : null,
|
||||
rows.map((entry) => {
|
||||
const input = el("input.months", {
|
||||
type: "number",
|
||||
min: String(entry.minimum),
|
||||
max: String(entry.maximum),
|
||||
value: String(entry.value),
|
||||
title: `${entry.minimum} bis ${entry.maximum} ${entry.unit}`,
|
||||
});
|
||||
fields.set(entry.key, input);
|
||||
|
||||
return el("div.duration-row", {},
|
||||
el("div.duration-text", {},
|
||||
el("span.duration-label", {}, `${entry.label} … ${entry.unit}`),
|
||||
el("span.duration-note", {},
|
||||
entry.note,
|
||||
entry.zero_means ? ` · 0 = ${entry.zero_means}` : "",
|
||||
entry.value !== entry.default
|
||||
? ` · Vorgabe ${entry.default}` : "")),
|
||||
input);
|
||||
}));
|
||||
};
|
||||
|
||||
return el("section.card", {},
|
||||
el("h2", {}, "Automatische Bereinigung"),
|
||||
el("h2", {}, "Fristen"),
|
||||
el("p.lead", {},
|
||||
"Läuft einmal täglich. ",
|
||||
el("strong", {}, "0 bedeutet abgeschaltet"),
|
||||
", nicht „sofort“. Administratorkonten sind ausgenommen."),
|
||||
"Diese Werte gelten zur Laufzeit und werden auch für die " +
|
||||
"Datenschutzerklärung gebraucht. Der Aufräumlauf setzt sie " +
|
||||
"einmal täglich um."),
|
||||
|
||||
el("label", {}, "Deaktivieren nach … Monaten ohne Anmeldung"),
|
||||
deactivate,
|
||||
el("label", {}, "Löschen nach … Monaten Deaktivierung"),
|
||||
remove,
|
||||
|
||||
el("h3.device-heading", {}, "Listen"),
|
||||
el("label", {}, "Archivieren nach … Monaten ohne Öffnen"),
|
||||
archive,
|
||||
el("p.sub", {},
|
||||
"Gilt je Person: Die Liste verschwindet nur aus der Übersicht " +
|
||||
"derjenigen, die sie nicht geöffnet hat."),
|
||||
el("label", {}, "Löschen nach … Monaten im Archiv"),
|
||||
archiveDelete,
|
||||
el("p.sub", {},
|
||||
"Maßgeblich ist allein das Archivdatum des Eigentümers. Er wird " +
|
||||
"7 Tage und 1 Tag vorher per E-Mail gewarnt."),
|
||||
group("Konten",
|
||||
["auto_deactivate_months", "auto_delete_months"]),
|
||||
group("Listen",
|
||||
["auto_archive_months", "archive_delete_months"]),
|
||||
group("Aufbewahrung",
|
||||
["cleanup_deleted_days", "cleanup_ops_days", "product_cache_days"]),
|
||||
group("Sitzungen und Links",
|
||||
["session_days", "invite_days", "welcome_days", "verify_hours",
|
||||
"reset_hours", "email_change_hours"]),
|
||||
|
||||
el("button.primary", {
|
||||
type: "button",
|
||||
onclick: () => guarded(async () => {
|
||||
await put("/api/admin/settings", {
|
||||
auto_deactivate_months: Number(deactivate.value) || 0,
|
||||
auto_delete_months: Number(remove.value) || 0,
|
||||
auto_archive_months: Number(archive.value) || 0,
|
||||
archive_delete_months: Number(archiveDelete.value) || 0,
|
||||
});
|
||||
say("Einstellungen gespeichert.");
|
||||
const durations = {};
|
||||
for (const [key, input] of fields) {
|
||||
durations[key] = Number(input.value) || 0;
|
||||
}
|
||||
await put("/api/admin/settings", { durations });
|
||||
say("Fristen gespeichert.");
|
||||
}),
|
||||
}, "Speichern"),
|
||||
|
||||
@@ -354,7 +363,19 @@ export async function adminView(root, { back }) {
|
||||
el("button", {
|
||||
type: "button",
|
||||
onclick: () => guarded(() => post("/api/admin/cleanup")),
|
||||
}, "Jetzt aufräumen"))
|
||||
}, "Jetzt aufräumen")),
|
||||
|
||||
// Was hier nicht einstellbar ist, soll trotzdem sichtbar sein -
|
||||
// sonst wirkt die Übersicht vollständig, ohne es zu sein.
|
||||
Object.keys(config.fixed || {}).length
|
||||
? el("div", {},
|
||||
el("h3.device-heading", {}, "Fest eingestellt"),
|
||||
el("ul.fixed-list", {},
|
||||
Object.entries(config.fixed).map(([label, value]) =>
|
||||
el("li", {},
|
||||
el("span.name", {}, label),
|
||||
el("span.sub", {}, value)))))
|
||||
: null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -375,7 +396,7 @@ export async function adminView(root, { back }) {
|
||||
registrationToggle(),
|
||||
el("ul.admin-users", {}, users.map(userRow))),
|
||||
createCard(),
|
||||
automationCard()
|
||||
durationsCard()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
|
||||
// Bei jeder Änderung an den Dateien unten hochzählen. Der Wert entscheidet,
|
||||
// wann alte Zwischenspeicher verworfen werden.
|
||||
const VERSION = "v25";
|
||||
const VERSION = "v27";
|
||||
const SHELL_CACHE = `einkaufsapp-shell-${VERSION}`;
|
||||
|
||||
const SHELL = [
|
||||
"/",
|
||||
"/app.css",
|
||||
"/impressum.html",
|
||||
"/datenschutz.html",
|
||||
"/js/app.js",
|
||||
"/js/api.js",
|
||||
"/js/dom.js",
|
||||
|
||||
Reference in New Issue
Block a user