Files
WebAPP/database/seeders/SettingsSeeder.php
Rhino 2e24a40d68 Stand: SMTP-Test, Admin-Mail-Tab, Notifiable-Fix, Lazy-Quill
- Fix: Notifiable-Trait zum User-Model hinzugefuegt (behebt notify()-500er)
- Installer: SMTP-Verbindungstest mit EsmtpTransport + Ueberspringen-Link
- Admin: Neuer E-Mail-Tab mit SMTP-Konfiguration + Verbindungstest
- Admin: Lazy Quill-Initialisierung (nur sichtbare Locale wird geladen)
- Uebersetzungen: 17 neue Mail-Keys in allen 6 Sprachen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 07:30:37 +01:00

291 lines
20 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Database\Seeders;
use App\Models\Setting;
use Illuminate\Database\Seeder;
class SettingsSeeder extends Seeder
{
public function run(): void
{
$impressum = <<<'HTML'
<h3>Angaben gemäß § 5 TMG</h3>
<p><strong>[Vor- und Nachname]</strong><br>
[Straße und Hausnummer]<br>
[PLZ] [Ort]</p>
<h3>Kontakt</h3>
<p>E-Mail: [deine-email@beispiel.de]<br>
Telefon: [optional Telefonnummer]</p>
<h3>Verantwortlich für den Inhalt nach § 18 Abs. 2 MStV</h3>
<p>[Vor- und Nachname]<br>
[Anschrift wie oben]</p>
<h3>Haftungshinweis</h3>
<p>Diese WebApp ist ein privates, nicht-kommerzielles Projekt zur internen Organisation einer Kinder-Handballmannschaft. Die Inhalte sind nur für registrierte Mitglieder (Eltern und Trainer) bestimmt.</p>
<p>Trotz sorgfältiger Kontrolle übernehmen wir keine Haftung für die Inhalte externer Links. Für den Inhalt verlinkter Seiten sind ausschließlich deren Betreiber verantwortlich.</p>
HTML;
$datenschutz = <<<'HTML'
<h3 id="hinweis"><strong>Wichtiger Hinweis vorab</strong></h3>
<p><strong>Diese WebApp ist eine rein private, nicht-kommerzielle Vereins-Webseite.</strong> Sie dient ausschließlich der internen Koordination einer Kinder-Handballmannschaft also der Organisation von Trainings, Spielen, Turnieren, Catering und Zeitnehmer-Diensten.</p>
<p><strong>Es fließen keinerlei Daten an Dritte ab.</strong> Wir verkaufen, teilen oder übermitteln keine personenbezogenen Daten an externe Unternehmen, Werbetreibende, soziale Netzwerke oder sonstige Dritte. Es gibt kein Tracking, keine Analyse-Tools, keine Werbung und keine versteckten Datenflüsse. Alle Daten verbleiben ausschließlich auf unserem Server und werden nur für den beschriebenen Vereinszweck genutzt.</p>
<h3 id="verantwortlicher"><strong>1. Verantwortlicher</strong></h3>
<p>[Vor- und Nachname]<br>
[Straße und Hausnummer]<br>
[PLZ] [Ort]<br>
E-Mail: [deine-email@beispiel.de]</p>
<h3 id="webapp"><strong>2. Was ist diese WebApp?</strong></h3>
<p>Diese WebApp ist ein geschlossenes, privates System für Eltern und Trainer einer Kinder-Handballmannschaft. Der Zugang erfolgt ausschließlich über persönliche Einladungslinks es gibt keine offene Registrierung und keinen öffentlichen Zugang zu Inhalten. Die App ersetzt klassische Kommunikation über Messenger-Gruppen und bietet eine zentrale Plattform für:</p>
<ul>
<li>Terminplanung (Training, Spiele, Turniere, Besprechungen)</li>
<li>Zu- und Absagen der Spieler durch die Eltern</li>
<li>Catering-Koordination und Zeitnehmer-Organisation</li>
<li>Datei-Ablage (Regelwerke, Turnierinfos, etc.)</li>
<li>Kommentarfunktion für Absprachen</li>
</ul>
<h3 id="daten"><strong>3. Erhobene Daten</strong></h3>
<p>Bei der Nutzung werden folgende personenbezogene Daten verarbeitet nicht mehr und nicht weniger:</p>
<ul>
<li><strong>Registrierungsdaten:</strong> Name, E-Mail-Adresse, Passwort (verschlüsselt gespeichert als kryptographischer Hash das Passwort ist niemals im Klartext einsehbar, auch nicht für Administratoren)</li>
<li><strong>Spielerdaten:</strong> Vorname, Nachname, Geburtsjahr, Trikotnummer, Fotoerlaubnis, Teamzugehörigkeit (durch Administratoren gepflegt)</li>
<li><strong>Profilbilder:</strong> Optional hochgeladene Fotos für Benutzer und Spieler (nur sichtbar für eingeloggte Mitglieder)</li>
<li><strong>Zuordnungsdaten:</strong> Eltern-Kind-Beziehung zwischen Benutzerkonto und Spielern</li>
<li><strong>Veranstaltungsdaten:</strong> Teilnahme-Status (Zusage/Absage), Catering-Status und optionale Notizen, Zeitnehmer-Bereitschaft, Kommentare zu Veranstaltungen</li>
<li><strong>Hochgeladene Dateien:</strong> Dokumente, die Administratoren bereitstellen (PDF, Bilder, etc.)</li>
<li><strong>Technische Daten:</strong> IP-Adresse (in Server-Logs), Zeitpunkt des letzten Logins, gewählte Sprache</li>
</ul>
<h3 id="zweck"><strong>4. Zweck der Verarbeitung</strong></h3>
<p>Die Daten werden <strong>ausschließlich</strong> zu folgenden Zwecken verarbeitet:</p>
<ul>
<li>Verwaltung der Benutzerkonten und Authentifizierung</li>
<li>Koordination von Trainingsterminen, Spielen und sonstigen Veranstaltungen</li>
<li>Erfassung von Zu- und Absagen der Spieler</li>
<li>Organisation der Verpflegung (Catering) und Zeitnehmer-Dienste bei Veranstaltungen</li>
<li>Bereitstellung von Dokumenten und Dateien für die Mannschaft</li>
<li>Kommunikation über die Kommentarfunktion</li>
</ul>
<p>Es findet <strong>keine</strong> Auswertung, kein Profiling und keine automatisierte Entscheidungsfindung statt.</p>
<h3 id="rechtsgrundlage"><strong>5. Rechtsgrundlage</strong></h3>
<p>Die Verarbeitung erfolgt auf Grundlage von <strong>Art. 6 Abs. 1 lit. f DSGVO</strong> (berechtigtes Interesse). Das berechtigte Interesse liegt in der effizienten Organisation der Mannschaftsaktivitäten im Rahmen einer nicht-kommerziellen, vereinsähnlichen Struktur. Sie können dieser Verarbeitung jederzeit widersprechen.</p>
<h3 id="cookies"><strong>6. Cookies und Sitzungsdaten</strong></h3>
<p>Diese WebApp verwendet <strong>ausschließlich ein einziges, technisch notwendiges Session-Cookie</strong>. Dieses Cookie ist für die Anmeldefunktion zwingend erforderlich und enthält keine personenbezogenen Daten lediglich eine zufällige Sitzungs-ID, die beim Schließen des Browsers oder nach Ablauf der Sitzung automatisch gelöscht wird.</p>
<p>Es werden <strong>keine</strong> Tracking-, Analyse- oder Werbe-Cookies eingesetzt. Kein Google Analytics, kein Facebook Pixel, kein Matomo, keine Heatmaps nichts dergleichen. Ein Cookie-Banner ist daher nach aktueller Rechtslage nicht erforderlich (§ 25 Abs. 2 Nr. 2 TDDDG), da ausschließlich technisch unbedingt erforderliche Cookies verwendet werden.</p>
<h3 id="externe-dienste"><strong>7. Externe Dienste und technische Ressourcen</strong></h3>
<p>Zur Darstellung der Webseite werden einige technische Ressourcen (CSS-Stylesheets, JavaScript-Bibliotheken) von externen Servern geladen. Dabei kann Ihre IP-Adresse an diese Anbieter übermittelt werden. <strong>Dies dient ausschließlich der technischen Funktionsfähigkeit es werden dabei keine personenbezogenen Nutzungsdaten erhoben oder ausgewertet.</strong></p>
<p>Im Folgenden sind <strong>alle</strong> externen Dienste aufgeführt, mit denen diese WebApp kommuniziert. Es gibt keine weiteren versteckten Verbindungen.</p>
<p><strong>Content Delivery Networks (CDN):</strong></p>
<ul>
<li><strong>cdn.tailwindcss.com</strong> CSS-Framework für das Layout (Tailwind CSS). Beim Laden der Seite wird ein JavaScript-Compiler von diesem Server geladen, der das Styling generiert. Dabei wird Ihre IP-Adresse übermittelt. Betreiber: Tailwind Labs Inc., USA. Es werden keine Cookies gesetzt und keine Nutzerprofile erstellt.</li>
<li><strong>cdn.jsdelivr.net</strong> JavaScript-Bibliotheken: Alpine.js (reaktive UI-Elemente) und Quill.js (Texteditor im Admin-Bereich). Betreiber: Prospect One (Open-Source-CDN). Alle Dateien werden mit kryptographischer Integritätsprüfung (SRI) geladen eine Manipulation der Dateien auf dem CDN ist somit ausgeschlossen. Es werden keine Cookies gesetzt.</li>
<li><strong>unpkg.com</strong> Kartenbibliothek Leaflet.js (für die Anzeige von Veranstaltungsorten auf einer Karte). Betreiber: Cloudflare/UNPKG. Ebenfalls mit SRI-Integritätsprüfung abgesichert. Es werden keine Cookies gesetzt.</li>
</ul>
<p>Diese CDNs liefern ausschließlich statische Dateien (CSS, JavaScript) aus. <strong>Es fließen keine personenbezogenen Daten an diese Dienste außer der IP-Adresse im Rahmen des technisch notwendigen HTTP-Abrufs.</strong> Die Dateien werden von Ihrem Browser zwischengespeichert (Cache), sodass bei wiederholten Besuchen keine erneuten Abrufe stattfinden.</p>
<p><strong>OpenStreetMap (Kartenanzeige):</strong></p>
<p>Auf Veranstaltungs-Detailseiten wird eine interaktive Karte eingebunden. Dabei werden Kartenbilder (sogenannte „Tiles") von den Servern <em>tile.openstreetmap.org</em> der OpenStreetMap Foundation (OSMF) geladen. Hierbei wird Ihre IP-Adresse an die OSMF übermittelt. OpenStreetMap ist ein freies, nichtkommerzielles Projekt mit Sitz in Großbritannien. Es werden keine Tracking-Cookies gesetzt und keine Nutzerprofile erstellt. Die Karte wird nur auf Veranstaltungs-Detailseiten geladen, nicht auf anderen Seiten. Datenschutzrichtlinie: <a href="https://wiki.osmfoundation.org/wiki/Privacy_Policy" target="_blank" rel="noopener">osmfoundation.org/wiki/Privacy_Policy</a></p>
<p>Zusätzlich wird auf Veranstaltungs-Detailseiten ein Link zur Routenplanung über <em>openstreetmap.org</em> angeboten. Dieser Link öffnet sich in einem neuen Tab und wird <strong>erst durch Ihren aktiven Klick</strong> aufgerufen es findet kein automatischer Datentransfer statt.</p>
<p><strong>Photon / Komoot (Adress-Autocomplete):</strong></p>
<p>Im Administrationsbereich wird für die Adresssuche bei der Erstellung von Veranstaltungen und Orten der Photon-Geocoding-Dienst genutzt (<em>photon.komoot.io</em>). Dabei wird der eingegebene Suchbegriff zusammen mit Ihrer IP-Adresse an die Server von Komoot GmbH (Potsdam, Deutschland) übermittelt. Photon ist ein <strong>Open-Source-Projekt</strong>, das auf OpenStreetMap-Daten basiert. Es werden keine Cookies gesetzt und keine Nutzerprofile erstellt. Dieser Dienst wird <strong>ausschließlich im Administrationsbereich</strong> und <strong>nur bei aktiver Eingabe</strong> durch einen Administrator aufgerufen normale Benutzer lösen diesen Dienst nicht aus. Datenschutzrichtlinie: <a href="https://www.komoot.com/privacy" target="_blank" rel="noopener">komoot.com/privacy</a></p>
<p><strong>Nominatim (Server-seitige Adresssuche):</strong></p>
<p>Ergänzend zum Photon-Dienst wird im Administrationsbereich der Nominatim-Dienst der OpenStreetMap Foundation zur Adresssuche genutzt. Anders als bei Photon erfolgt dieser Abruf <strong>über unseren Server</strong> (nicht direkt aus Ihrem Browser). Dabei wird nur der Suchbegriff übermittelt, <strong>nicht Ihre IP-Adresse</strong>. Suchergebnisse werden 24 Stunden lang auf unserem Server zwischengespeichert, um unnötige Anfragen zu vermeiden. Auch dieser Dienst wird <strong>nur bei aktiver Eingabe durch einen Administrator</strong> aufgerufen.</p>
<p><strong>Übersicht: Welcher Dienst wird wann aufgerufen?</strong></p>
<ul>
<li><strong>Bei jedem Seitenaufruf:</strong> Tailwind CSS (cdn.tailwindcss.com), Alpine.js (cdn.jsdelivr.net) — nur beim ersten Besuch, danach aus dem Browser-Cache</li>
<li><strong>Nur auf Veranstaltungs-Detailseiten:</strong> Leaflet.js (unpkg.com), OpenStreetMap-Kacheln (tile.openstreetmap.org)</li>
<li><strong>Nur im Admin-Bereich bei Textbearbeitung:</strong> Quill.js (cdn.jsdelivr.net)</li>
<li><strong>Nur im Admin-Bereich bei Adresssuche:</strong> Photon (photon.komoot.io), Nominatim (nominatim.openstreetmap.org)</li>
<li><strong>Nur bei aktivem Klick durch den Benutzer:</strong> OpenStreetMap-Routenplanung (openstreetmap.org)</li>
</ul>
<h3 id="keine-weitergabe"><strong>8. Keine Weitergabe an Dritte</strong></h3>
<p><strong>Wir geben Ihre Daten nicht weiter.</strong> Nicht an Werbepartner, nicht an soziale Netzwerke, nicht an Datenbroker, nicht an andere Vereine und nicht an sonstige Dritte. Die unter Punkt 7 genannten externen Dienste erhalten <strong>ausschließlich Ihre IP-Adresse</strong> im Rahmen technisch notwendiger HTTP-Abrufe keine Namen, keine E-Mail-Adressen, keine Inhalte und keine sonstigen personenbezogenen Daten.</p>
<p>Die einzige Ausnahme wäre eine gesetzliche Verpflichtung zur Herausgabe (z. B. bei einer gerichtlichen Anordnung) dieser Fall ist bei einer internen Vereins-Koordinationsplattform praktisch nicht relevant.</p>
<h3 id="hosting"><strong>9. Hosting und Serverstandort</strong></h3>
<p>Diese WebApp wird auf einem Server in <strong>Deutschland</strong> betrieben (All-Inkl.com, Hauptstraße 68, 02742 Friedersdorf). Alle Ihre Daten (Benutzerkonten, Spielerdaten, Veranstaltungen, Dateien, Kommentare) verbleiben ausschließlich auf diesem Server in Deutschland.</p>
<p>Es findet <strong>kein Transfer</strong> personenbezogener Daten in Drittstaaten statt. Die einzigen Verbindungen zu Servern außerhalb Deutschlands sind die oben genannten CDN-Abrufe (bei denen lediglich Ihre IP-Adresse für den technischen Ladevorgang übermittelt wird) sowie die OpenStreetMap-Kartenkacheln. Bei keinem dieser Abrufe werden personenbezogene Inhalte (Namen, E-Mails, etc.) übertragen.</p>
<h3 id="speicherdauer"><strong>10. Speicherdauer</strong></h3>
<p>Personenbezogene Daten werden gespeichert, solange das Benutzerkonto aktiv ist bzw. solange Ihr Kind in der Mannschaft spielt. Bei Deaktivierung des Kontos durch einen Administrator werden die Daten aufbewahrt, der Zugang aber gesperrt. Eine vollständige Löschung aller Ihrer Daten kann jederzeit beim Verantwortlichen beantragt werden und wird zeitnah umgesetzt.</p>
<h3 id="rechte"><strong>11. Ihre Rechte</strong></h3>
<p>Sie haben gemäß DSGVO folgende Rechte:</p>
<ul>
<li><strong>Auskunft</strong> über Ihre gespeicherten Daten (Art. 15 DSGVO)</li>
<li><strong>Berichtigung</strong> unrichtiger Daten (Art. 16 DSGVO)</li>
<li><strong>Löschung</strong> Ihrer Daten (Art. 17 DSGVO)</li>
<li><strong>Einschränkung</strong> der Verarbeitung (Art. 18 DSGVO)</li>
<li><strong>Datenübertragbarkeit</strong> (Art. 20 DSGVO)</li>
<li><strong>Widerspruch</strong> gegen die Verarbeitung (Art. 21 DSGVO)</li>
</ul>
<p>Zur Ausübung Ihrer Rechte wenden Sie sich bitte formlos an den Verantwortlichen (siehe oben) per E-Mail genügt. Darüber hinaus steht Ihnen ein <strong>Beschwerderecht bei einer Aufsichtsbehörde</strong> zu (Art. 77 DSGVO).</p>
<h3 id="sicherheit"><strong>12. Datensicherheit</strong></h3>
<p>Wir setzen technische und organisatorische Maßnahmen ein, um Ihre Daten zu schützen:</p>
<ul>
<li>Verschlüsselte Übertragung aller Daten (HTTPS/TLS)</li>
<li>Passwörter werden ausschließlich als kryptographische Hashes gespeichert (BCrypt) selbst Administratoren können Ihr Passwort nicht einsehen</li>
<li>Schutz vor Brute-Force-Angriffen durch automatische Ratenbegrenzung</li>
<li>Schutz vor Cross-Site-Scripting (XSS) und Cross-Site-Request-Forgery (CSRF)</li>
<li>Content Security Policy (CSP) zur Einschränkung externer Inhalte nur die oben genannten Quellen sind erlaubt</li>
<li>Subresource Integrity (SRI) für alle CDN-Bibliotheken manipulierte Dateien werden vom Browser blockiert</li>
<li>HTTP Strict Transport Security (HSTS) zur erzwungenen Verschlüsselung</li>
<li>Zugang nur über persönliche Einladungslinks (keine offene Registrierung, keine Suchmaschinen-Indexierung)</li>
<li>Dokumente werden in einem geschützten Verzeichnis gespeichert und sind nur nach Anmeldung zugänglich</li>
<li>Server-Fingerprinting deaktiviert (keine Preisgabe von Software-Versionen)</li>
</ul>
<h3 id="zusammenfassung"><strong>13. Zusammenfassung</strong></h3>
<p>Kurz und knapp: <strong>Diese WebApp ist ein privates Werkzeug zur Mannschafts-Organisation nicht mehr und nicht weniger.</strong> Wir verdienen kein Geld damit, wir sammeln keine Daten zum Verkauf, wir tracken niemanden und wir geben nichts weiter. Ihre Daten gehören Ihnen und werden ausschließlich für den Vereinszweck genutzt.</p>
<p>Die einzigen externen Verbindungen dienen dem Laden von Layout-Bibliotheken und Kartenmaterial dabei wird ausschließlich Ihre IP-Adresse im Rahmen des normalen Internetverkehrs übermittelt, niemals Ihre personenbezogenen Inhalte.</p>
HTML;
$settings = [
[
'key' => 'app_name',
'label' => 'App-Name',
'type' => 'text',
'value' => 'Handball App',
],
[
'key' => 'impressum_html',
'label' => 'Impressum',
'type' => 'html',
'value' => $impressum,
],
[
'key' => 'datenschutz_html',
'label' => 'Datenschutzerklärung',
'type' => 'html',
'value' => $datenschutz,
],
];
foreach ($settings as $setting) {
$existing = Setting::where('key', $setting['key'])->first();
if ($existing) {
// Nur Metadaten aktualisieren, NICHT den Wert überschreiben
$existing->update(['label' => $setting['label'], 'type' => $setting['type']]);
} else {
$this->createSetting($setting);
}
}
// Slogan
$sloganSettings = [
[
'key' => 'app_slogan',
'label' => 'Slogan',
'type' => 'richtext',
'value' => '<p><em>Gemeinsam stark — auf und neben dem Spielfeld</em></p>',
],
[
'key' => 'app_favicon',
'label' => 'Favicon',
'type' => 'text',
'value' => null,
],
];
foreach ($sloganSettings as $setting) {
$existing = Setting::where('key', $setting['key'])->first();
if ($existing) {
$existing->update(['label' => $setting['label'], 'type' => $setting['type']]);
} else {
$this->createSetting($setting);
}
}
// Statistik-Sichtbarkeit
$statsEnabled = Setting::where('key', 'statistics_enabled')->first();
if ($statsEnabled) {
$statsEnabled->update(['label' => 'Statistik aktiviert', 'type' => 'number']);
} else {
$this->createSetting([
'key' => 'statistics_enabled',
'label' => 'Statistik aktiviert',
'type' => 'number',
'value' => '1',
]);
}
// Sichtbarkeits-Einstellungen (pro Feature pro Rolle)
$visibilitySettings = [
['key' => 'visibility_statistics_coach', 'label' => 'Statistik: Trainer', 'type' => 'number', 'value' => '1'],
['key' => 'visibility_statistics_parent_rep', 'label' => 'Statistik: Elternvertretung', 'type' => 'number', 'value' => '1'],
['key' => 'visibility_catering_history_coach', 'label' => 'Catering-Verlauf: Trainer', 'type' => 'number', 'value' => '1'],
['key' => 'visibility_catering_history_parent_rep', 'label' => 'Catering-Verlauf: Elternvertretung', 'type' => 'number', 'value' => '1'],
];
foreach ($visibilitySettings as $setting) {
$existing = Setting::where('key', $setting['key'])->first();
if ($existing) {
$existing->update(['label' => $setting['label'], 'type' => $setting['type']]);
} else {
$this->createSetting($setting);
}
}
// Lizenzschlüssel
$licenseKey = Setting::where('key', 'license_key')->first();
if ($licenseKey) {
$licenseKey->update(['label' => 'Lizenzschlüssel', 'type' => 'text']);
} else {
$this->createSetting([
'key' => 'license_key',
'label' => 'Lizenzschlüssel',
'type' => 'text',
'value' => null,
]);
}
// Event-Defaults für Mindestanforderungen
foreach (['home_game', 'away_game', 'training', 'tournament', 'meeting'] as $type) {
foreach (['players', 'catering', 'timekeepers'] as $field) {
$key = "default_min_{$field}_{$type}";
$existing = Setting::where('key', $key)->first();
if ($existing) {
$existing->update([
'label' => "Default Min. " . ucfirst($field) . " (" . ucfirst($type) . ")",
'type' => 'number',
]);
} else {
Setting::create([
'key' => $key,
'label' => "Default Min. " . ucfirst($field) . " (" . ucfirst($type) . ")",
'type' => 'number',
'value' => null,
]);
}
}
}
}
/**
* Setting erstellen mit expliziter key-Zuweisung (key nicht in $fillable).
*/
private function createSetting(array $data): Setting
{
$key = $data['key'];
unset($data['key']);
$setting = new Setting($data);
$setting->key = $key;
$setting->save();
return $setting;
}
}