yxcyxc
All checks were successful
Deploy / deploy (push) Successful in 1m3s

This commit is contained in:
2026-08-03 23:27:35 +02:00
parent faf614010d
commit fc34e87622
17 changed files with 326 additions and 294 deletions

101
.projektstructure.txt Executable file → Normal file
View File

@@ -1,99 +1,8 @@
Anweisung: Projektstruktur (Basis-Template) wie in „papa-kind-treff“ Projektstruktur Verweis
Aktueller Projektstand Die kanonische interne Strukturdokumentation wurde verschoben nach:
- Die Hauptnavigation lautet aktuell `Home`, `Event Suche`, `Community`.
- Papa-Kind-Treff nutzt im Mitgliederbereich eine Mehr-Style-Avatar-Architektur auf Basis von DiceBear.
- Die zentrale Avatar-Verwaltung liegt in `src/App/Avatar/AvatarManager.php`.
- Die aktuell aktiven Styles liegen in `src/App/Avatar/Lorelei.php` und `src/App/Avatar/Croodles.php`.
- Die Avatar-Konfiguration liegt in `config/avatar.php`.
- Der Avatar-Proxy liegt unter `public/api/avatar.php`.
- Die visuelle Avatar-Konfiguration liegt im Dashboard-Modal unter `partials/landing/account/dashboard.php` und rendert Komponenten-Vorschauen live nach.
- Das Avatar-Modal ist auf Browserhoehe optimiert: kompakter Kopfbereich, fixe Komponenten-Navigation, scrollende Variantenliste und kleine Farbfelder mit `Standard`-Reset pro Komponente.
- Der Generator speichert `avatar_style`, `avatar_seed` und `avatar_config_json` generisch im Profil.
- Der Generator zeigt pro Style die von DiceBear unterstützten Farbfelder direkt in den passenden Komponentenabschnitten und speichert diese im gleichen Config-JSON.
- Gespeicherte Benutzer-Avatare werden lokal unter `public/assets/avatars/users/ptk-<userId>.svg` abgelegt und bei Änderungen überschrieben.
- Der Generator besitzt einen Button `Random Profilbild`, der Seed und Varianten des aktiven Styles zufällig neu setzt.
- Beim Oeffnen des Modals werden nur die Bilder des aktiven Styles und Tabs geladen; weitere Tabs laden ihre Varianten erst beim Wechsel nach.
- Kinder koennen im Mitgliederbereich zusaetzlich nachtraeglich bearbeitet und geloescht werden.
- Wenn bei einem Kind ein Geburtsdatum hinterlegt ist, wird das Alter automatisch daraus berechnet und spaeter bei Bedarf jaehrlich aktualisiert; ohne Geburtsdatum bleibt das manuell gepflegte Alter massgeblich.
- Profilbezogene sensible Angaben wie Vorname, Nachname, Telefonnummer, Beruf, Sprachen und Kurzvorstellung werden app-seitig verschluesselt gespeichert; vorhandene Klartextwerte werden bei der Nutzung schrittweise nachmigriert.
- Auch die Konto-E-Mail wird jetzt app-seitig verschluesselt gespeichert; fuer Login, Verifizierung, Passwort-Reset und Dublettenpruefung dient nur ein separater HMAC-Lookup-Hash.
- Der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist auf der Datenschutz-Seite jetzt zusaetzlich deutlich hervorgehoben.
- Das Profil besitzt jetzt zusaetzlich eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie eine optionale Adress-Lokalisierung ueber die vorhandenen externen Karten-/Geocoding-Dienste.
- Die Profiladresse unterstuetzt jetzt ausserdem eine Auswahl moeglicher Adress-Treffer, die Uebernahme aus dem Browser-Standort und einen kurzen Hinweisdialog zur lokalen Relevanz fuer Events, Termine und Treffen.
- Der Profil-Editor im Mitgliederbereich ist jetzt kompakter aufgebaut, in ausklappbare Bereiche gegliedert und enthaelt die bearbeitbare Konto-E-Mail direkt im Formular.
- Fuer Umkreis- und Distanzfunktionen bleiben nur `lat`/`lng` technisch im Klartext; die eigentliche Strasse/Hausnummer ist verschluesselt.
- Alte statische Avatar-Presets und frühere Layer-Assets sind entfernt.
- Änderungen an Funktionen mit Cookies, LocalStorage, SessionStorage, Geolocation, Tracking oder Drittanbietern müssen immer auch in Datenschutz-/Cookie-Hinweisen und im Consent-Flow berücksichtigt werden.
Ziel - `Internal/de/PROJECT_STRUCTURE.md`
- Erstelle ein neues Projekt bzw. aktualisiere das aktuelle mit exakt der gleichen Grundstruktur wie in „papa-kind-treff“. - `Internal/en/PROJECT_STRUCTURE.md`
- Fokus auf Hauptordner und deren Zweck; keine projektspezifischen Sonderordner (z. B. Community) anlegen.
- Inhalte können minimal sein, aber alle Pfade müssen existieren.
- In Ordnern, die noch keine Dateien beinhalten, muss eine leere Datei mit dem Namen .gitkeep erstellt werden.
- Bestehende Dateien nicht überschreiben; wenn nötig, Inhalte behutsam an die neue Logik anpassen.
Verzeichnisstruktur (Pflichtordner) Bitte diese Dateien als Referenz verwenden und bei Änderungen dort pflegen.
- api/
- config/
- debug/
- partials/
- public/
- src/
- tools/
- README.md
- schema.sql
- .gitlab-ci.yml
Details je Ordner
config/
- Enthält alle Konfigurationen.
- Muss Subordner für Umgebungen haben: z. B. prod/ und staging/.
- In den Umgebungsordnern liegen Basis-Konfigurationen (z. B. db.php, settings.php, emailtemplates.php, domaindata.php).
- Hinweis: Die Umgebungs-Subordner werden beim Deployment in den root-Config kopiert; daher ist hier keine weitere Unterscheidung nötig.
- Falls Dateien fehlen, lege sie mit minimalem Basisinhalt an (z. B. PHP-Array/Kommentar), ohne vorhandene Inhalte zu überschreiben.
api/
- Schnittstellen/Endpunkte.
- Kann zunächst leer bleiben; dann .gitkeep anlegen.
debug/
- Debug-Hilfen, Logs oder Debug-Skripte.
- Wenn leer: .gitkeep.
partials/
- Nur die Unterscheidung in:
- landing/
- structure/
- landing/: Platz für seitenbezogene Teilausschnitte.
- structure/: Layout-Grundbausteine (z. B. layout_start.php, layout_end.php, nav.php, matomo.php) als Beispieldaten.
public/
- Webroot.
- Muss assets/ enthalten mit Unterordnern: bilder/, fonts/, js/, css/ (Dateien optional).
- Muss index.php enthalten mit Basis-Logic (z. B. Entry-Point/Router/Bootstrap).
src/
- Backend/Business-Logik und Kernklassen.
- Enthält App- oder Domain-Code (z. B. Auth, Database, Mailer, Config, Request, Assets usw.).
- Lege eine kurze Beschreibung an, wofür src gedacht ist (Kommentar oder README im Ordner).
tools/
- Entwicklungs-/Wartungs-Tools, Skripte oder Utilities.
- Inhaltlich analog zu src gedacht, aber für interne Werkzeuge.
Datei-Inhalte (minimal, aber vorhanden)
- .gitkeep: leer.
- README.md, schema.sql, .gitlab-ci.yml: leer oder mit kurzem Platzhaltertext.
- PHP-Dateien: leer oder mit kurzem Kommentar, z. B. "<?php // TODO".
- .htaccess (falls genutzt): leer oder minimaler Platzhalter.
Zusätzliche Regeln
- Keine projektspezifischen Sonderordner anlegen.
- Pfade und Dateinamen exakt, Groß-/Kleinschreibung beachten.
- Struktur ist wichtiger als Inhalt.
Ausgabeformat
- Erzeuge die Ordner und Dateien exakt wie oben.
- Liefere optional eine kurze Zusammenfassung der angelegten/angepassten Struktur.

22
Internal/README.md Normal file
View File

@@ -0,0 +1,22 @@
# Internal Documentation
Updated: 2026-08-03
Canonical internal project documentation lives in this directory.
## Directory Structure
- `de/README.md`
- `de/PROJECT_CONTEXT.md`
- `de/PROJECT_STRUCTURE.md`
- `en/README.md`
- `en/PROJECT_CONTEXT.md`
- `en/PROJECT_STRUCTURE.md`
## Maintenance Rule
- German and English documentation must be maintained as fully parallel references.
- Changes to project behavior, structure, wording, privacy, security, or product decisions must be reflected in both language sets.
- The root `README.md` remains in the project root as the mandatory start document for new chats.
## Scope Rule
- Public-facing website copy does not belong here.
- `Internal/` is only for internal project, product, architecture, and maintenance documentation.

View File

@@ -0,0 +1,34 @@
# Papa-Kind-Treff Projektkontext
Stand: 2026-08-03
## Kurzbeschreibung
Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind lokale Events, Termine und Treffen, ein Community-Forum, Mitgliederprofile mit optionalen Kinderinfos und ein Moderations-/Adminbereich.
## Wichtige Produktentscheidungen
- Die Hauptnavigation ist bewusst knapp gehalten: `Home`, `Event Suche`, `Community`.
- `Events` ist der Primärbegriff im Produkt.
- `Termine` und `Treffen` werden ergänzend in SEO- und Erklärungstexten genutzt.
- Die Community ist hierarchisch aufgebaut: Kategorien -> Boards -> Threads -> Posts.
- Moderation ist vom normalen Community-Frontend getrennt.
- Standortfunktionen sind relevant für lokale Events, Termine und Treffen.
## Konto / Mitgliederbereich
- Profil-Menü aktuell: `Profil`, `Kinder`, `Events`, `Community`, `Einstellungen`, `Abmelden`
- Kinder können angelegt, bearbeitet und gelöscht werden.
- Wenn ein Geburtsdatum gesetzt ist, wird das Alter automatisch berechnet und später bei Bedarf aktualisiert.
- Die Konto-E-Mail wird app-seitig verschlüsselt gespeichert und über einen separaten HMAC-Lookup-Hash adressiert.
- Profiladresse kann per Suche oder Browser-Standort übernommen werden.
## Technischer Rahmen
- eigener Front-Controller in `public/index.php`
- Templates unter `partials/`
- Geschäftslogik unter `src/App/`
- Globales Frontend vor allem über `public/assets/js/app.js` und `public/assets/css/app.css`
## Pflegehinweis
Bei Änderungen immer auch die parallelen Dateien in `Internal/en/` prüfen und inhaltlich gleichziehen.
## Dokumentationsprinzip
- `Internal/de/` und `Internal/en/` werden bewusst parallel gepflegt.
- Die Root-`README.md` dient als Start-Doku für neue Chats und muss bei grundlegenden Änderungen ebenfalls mitgeprüft werden.

View File

@@ -0,0 +1,29 @@
# Papa-Kind-Treff Projektstruktur
Stand: 2026-08-03
## Projektordner
- `config/`: Konfigurationen, inklusive `prod/` und `staging/`
- `partials/`: Templates, aufgeteilt in `landing/` und `structure/`
- `public/`: Webroot mit Assets und Seiten-Entry-Points
- `src/`: Backend-/Business-Logik und Kernklassen
- `debug/`: Debug-Hilfen und Logs
- `api/`: technische Endpunkte
- `Internal/`: interne Projektdokumentation in Deutsch und Englisch
## Aktuelle Strukturhinweise
- Avatar-Verwaltung: `src/App/Avatar/AvatarManager.php`
- aktive Styles: `src/App/Avatar/Lorelei.php`, `src/App/Avatar/Croodles.php`
- Profil-/Kinder-/Event-Logik im Mitgliederbereich: `partials/landing/account/dashboard.php`
- Consent- und rechtliche Texte: `public/page/datenschutz.php`
## Dokumentationsregel
Kanonische interne Dokumentation:
- `Internal/de/README.md`
- `Internal/de/PROJECT_CONTEXT.md`
- `Internal/de/PROJECT_STRUCTURE.md`
- entsprechende englische Spiegel unter `Internal/en/`
Zusätzlich:
- `README.md` im Hauptordner bleibt als Start-Doku für neue Chats bestehen.
- Änderungen an der internen Struktur müssen immer in Deutsch und Englisch parallel dokumentiert werden.

53
Internal/de/README.md Normal file
View File

@@ -0,0 +1,53 @@
# Papa-Kind-Treff
Stand: 2026-08-03
Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale Events, Termine und Treffen, Community-Austausch und einen geschützten Mitgliederbereich.
## Produktumfang
- lokale Events, Termine und Treffen finden
- Community/Forum mit Kategorien, Boards, Threads und Antworten
- Mitgliederbereich für Profil, optionale Kinderinfos und eigene Events
- Community-Moderation mit Rollenmodell
## Aktueller Stand
- Startseite mit Hero, Event-Suche, Event-Karussell, Community-Vorschau und Mitgliederbereichs-Block
- Community mit Board-Navigation und separater Thread-Ansicht
- Community-Admin-Bereich für Bewerbungen, Meldungen, Rollen und Migration
- standortbasierte Sortierung für die neuesten Events
- Mitgliederbereich mit linker Bereichsnavigation
- Profil-Menü mit `Profil`, `Kinder`, `Events`, `Community`, `Einstellungen`
- Konto-E-Mails sowie sensible Profilfelder werden app-seitig verschlüsselt gespeichert
- Kinder können angelegt, bearbeitet und gelöscht werden
- Profiladresse mit verschlüsselter Straße/Hausnummer, Adresssuche, Browser-Übernahme und Validierung
## Wording-Regel
- Primärbegriff im Produkt: `Events`
- Ergänzende SEO-/Erklärbegriffe: `Termine`, `Treffen`
- Navigation, Buttons und Kern-UI verwenden konsequent `Events`
## Technik
- Einstieg: `public/index.php`
- Templates: `partials/`
- App-Logik: `src/App/`
- Assets: `public/assets/`
- Datenbankschema: `schema.sql`
## Datenschutz und Sicherheit
- Passwort-Hashes über Argon2id
- Konto-E-Mails verschlüsselt plus separater HMAC-Lookup-Hash
- Profilfelder wie Vorname, Nachname, Straße/Hausnummer, Telefonnummer, Beruf, Sprachen und Kurzvorstellung verschlüsselt
- Standortkoordinaten (`lat`, `lng`) bleiben für Distanz- und Umkreissuchen technisch im Klartext
- nicht notwendige Analyse- und Drittanbieterfunktionen erst nach Einwilligung aktiv
## Dokumentationspflicht bei Änderungen
Bei jeder Änderung im Projekt mitprüfen und bei Bedarf aktualisieren:
- `Internal/de/README.md`
- `Internal/de/PROJECT_CONTEXT.md`
- `Internal/de/PROJECT_STRUCTURE.md`
- die parallelen englischen Dateien unter `Internal/en/`
## Parallelpflege DE / EN
- Die Dokumentation in `Internal/de/` und `Internal/en/` ist inhaltlich vollständig parallel zu halten.
- Deutsch und Englisch sind beide verbindliche interne Referenzen.
- Die Root-`README.md` im Hauptordner bleibt der feste Einstieg für neue Chats und verweist auf diese Struktur.

View File

@@ -0,0 +1,31 @@
# Papa-Kind-Treff Project Context
Updated: 2026-08-03
## Summary
Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events, appointments, and meetups, a community forum, member profiles with optional child information, and a moderation/admin area.
## Key Product Decisions
- Main navigation remains intentionally compact: `Home`, `Event Search`, `Community`.
- `Events` is the primary product term across the UI.
- Secondary wording is used only in SEO and explanatory copy.
- The community is hierarchical: categories -> boards -> threads -> posts.
- Moderation is separated from the normal community frontend.
- Location features are important for local discovery.
## Account / Member Area
- Profile menu currently contains `Profile`, `Children`, `Events`, `Community`, `Settings`, `Logout`.
- Children can be created, edited, and deleted.
- If a birth date is set, age is calculated automatically and updated later when needed.
- Account email is encrypted application-side and addressed through a separate HMAC lookup hash.
- Profile address can be completed via address search or browser-based location import.
## Technical Frame
- custom front controller in `public/index.php`
- templates under `partials/`
- business logic under `src/App/`
- global frontend mainly via `public/assets/js/app.js` and `public/assets/css/app.css`
## Maintenance Note
- Whenever one of these files changes, the matching files in `Internal/de/` and `Internal/en/` must be kept aligned.
- The root `README.md` acts as the startup document for new chats and should be reviewed alongside major context changes.

View File

@@ -0,0 +1,23 @@
# Papa-Kind-Treff Project Structure
Updated: 2026-08-03
## Main Directories
- `config/`: configuration, including `prod/` and `staging/`
- `partials/`: templates split into `landing/` and `structure/`
- `public/`: webroot, assets, and page entry points
- `src/`: backend and business logic
- `debug/`: debugging helpers and logs
- `api/`: technical endpoints
- `Internal/`: internal documentation in German and English
## Current Structure Notes
- avatar management: `src/App/Avatar/AvatarManager.php`
- active avatar styles: `src/App/Avatar/Lorelei.php`, `src/App/Avatar/Croodles.php`
- profile/children/event logic inside member area: `partials/landing/account/dashboard.php`
- consent and legal texts: `public/page/datenschutz.php`
## Documentation Rule
- canonical internal documentation lives in `Internal/de/` and `Internal/en/`
- `README.md` in the project root remains as the startup document for new chats
- structural changes must always be documented in German and English in parallel

52
Internal/en/README.md Normal file
View File

@@ -0,0 +1,52 @@
# Papa-Kind-Treff
Updated: 2026-08-03
Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, appointments, and meetups, community exchange, and a protected member area.
## Scope
- discover local events, appointments, and meetups
- community/forum with categories, boards, threads, and replies
- member area for profile, optional child information, and own events
- community moderation with a role model
## Current State
- landing page with hero, event search, event carousel, community preview, and member-area block
- community with board navigation and separate thread view
- community admin area for applications, reports, roles, and migrations
- location-based ordering for the newest events
- member area with left-side section navigation
- profile menu with `Profile`, `Children`, `Events`, `Community`, `Settings`
- account emails and sensitive profile fields are stored encrypted application-side
- children can be created, edited, and deleted
- profile address supports encrypted street/house number, address search, browser import, and validation
## Core Wording Rule
- Primary product term: `Events`
- Secondary SEO/explanatory terms: `Appointments`, `Meetups`
## Tech
- entry point: `public/index.php`
- templates: `partials/`
- application logic: `src/App/`
- assets: `public/assets/`
- database schema: `schema.sql`
## Privacy and Security
- password hashes via Argon2id
- account emails are encrypted and additionally addressed through a separate HMAC lookup hash
- profile fields such as first name, last name, street/house number, phone number, profession, languages, and short bio are encrypted
- location coordinates (`lat`, `lng`) remain plaintext where technically required for distance and radius search
- non-essential analytics and third-party services are only active after consent
## Documentation Rule
Whenever project behavior or structure changes, review and update:
- `Internal/de/README.md`
- `Internal/de/PROJECT_CONTEXT.md`
- `Internal/de/PROJECT_STRUCTURE.md`
- matching English files under `Internal/en/`
## Parallel DE / EN Maintenance
- Documentation in `Internal/de/` and `Internal/en/` must remain fully parallel in content.
- German and English are both binding internal references.
- The root `README.md` remains the fixed start document for new chats and must be reviewed when core project assumptions change.

View File

@@ -1,78 +1,8 @@
# Papa-Kind-Treff Chat-Kontext # Projektkontext Verweis
Stand: 2026-07-31 Die kanonische interne Kontextdokumentation wurde verschoben nach:
## Kurzbeschreibung - `Internal/de/PROJECT_CONTEXT.md`
Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind lokale Events, Termine und Treffen, ein Community-Forum, Mitgliederprofile mit optionalen Kinderinfos und ein Moderations-/Adminbereich. - `Internal/en/PROJECT_CONTEXT.md`
## Technischer Rahmen Bitte diese Dateien als Referenz verwenden und bei Änderungen dort pflegen.
- Kein großes Framework, sondern eigener Front-Controller in `public/index.php`
- Templates unter `partials/`
- Geschäftslogik unter `src/App/`
- MySQL/MariaDB-Schema in `schema.sql`
- Globales Frontend vor allem über `public/assets/js/app.js` und `public/assets/css/app.css`
- Avatar-Manager liegt zentral unter `src/App/Avatar/AvatarManager.php`
- Die aktiven Styles `Lorelei` und `Croodles` liegen getrennt unter `src/App/Avatar/Lorelei.php` und `src/App/Avatar/Croodles.php`
- Die Avatar-Konfiguration liegt in `config/avatar.php`
- In `config/avatar.php` sind pro Style auch Beispielpaletten, Komponentenlisten und Beispielprofile dokumentiert
- Der Avatar-Endpunkt ist als direkte Datei unter `public/api/avatar.php` angebunden und laeuft bewusst ohne vollen App-Bootstrap.
## Wichtige Produktentscheidungen
- Die Ansprache im Frontend soll immer den aktuellen Nutzer direkt ansprechen.
- Die Hauptnavigation ist bewusst knapp gehalten: `Home`, `Event Suche`, `Community`.
- `Events` sind aktuell kein eigener Hauptpunkt mehr; sie werden über Startseite und Suche erschlossen.
- Die Community ist hierarchisch aufgebaut: Kategorien -> Boards -> Threads -> Posts.
- Moderation ist getrennt vom normalen Community-Frontend in einem eigenen Adminbereich.
- Standortfunktionen sind relevant für lokale Events, Termine und Treffen und werden künftig weiter ausgebaut.
- Profilbilder werden aktuell über die Mehr-Style-Architektur serverseitig erzeugt; aktiv sind derzeit `lorelei` und `croodles`.
- Beim Style `croodles` startet die Basisfarbe standardmaessig weiss; der Hintergrund ist standardmaessig nicht vorbelegt.
- Stil, Seed und Varianten werden generisch im Profil gespeichert; das fertige SVG wird lokal pro Benutzer unter `ptk-<userId>.svg` abgelegt.
- Der Avatar-Editor bietet visuelle Komponenten-Vorschauen statt reiner Text-Dropdowns, laedt nur den aktiven Style/Tab nach, zeigt die Farbfelder direkt im passenden Komponentenabschnitt an und haelt Kopfbereich sowie Komponentenleiste im Modal fixiert.
## Navigation / Konto
- Eingeloggte Nutzer sehen rechts ein Profil-Menü statt einzelner `Dashboard`-/`Logout`-Buttons.
- Das Profil-Menü enthält aktuell `Profil`, `Kinder`, `Events`, `Community`, `Einstellungen`, `Abmelden`.
- Der Mitgliederbereich selbst ist als Layout mit linker Bereichsnavigation aufgebaut.
- Kinder koennen im Mitgliederbereich angelegt, nachtraeglich bearbeitet und wieder geloescht werden.
- Wenn ein Geburtsdatum gesetzt ist, wird das Alter im Mitgliederbereich automatisch berechnet und spaeter bei Bedarf anhand des Datums aktualisiert; ohne Geburtsdatum gilt der manuelle Alterswert.
- Sensible Profildaten wie Vorname, Nachname, Telefonnummer, Beruf, Sprachen und Kurzvorstellung werden app-seitig verschluesselt gespeichert; bestehende Klartextwerte werden bei Nutzung schrittweise in den verschluesselten Zustand ueberfuehrt.
- Die Konto-E-Mail wird ebenfalls app-seitig verschluesselt gespeichert; fuer Login, Verifizierung, Passwort-Reset und Dublettenpruefung existiert nur ein separater HMAC-Lookup-Hash.
- Der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist auf der Datenschutz-Seite jetzt zusaetzlich deutlich hervorgehoben.
- Im Profil koennen jetzt auch Strasse/Hausnummer, PLZ, Ort und Region gepflegt werden; fuer die Adress-Lokalisierung werden die bereits vorhandenen externen Karten-/Geocoding-Dienste wiederverwendet.
- Die Profiladresse bietet jetzt zusaetzlich eine Trefferauswahl fuer die Suche, eine Uebernahme aus dem Browser-Standort und einen kurzen Hinweisdialog zur Relevanz fuer lokale Event-, Termin- und Treff-Hinweise.
- Der Profil-Editor ist jetzt kompakter gegliedert, nutzt ausklappbare Bereiche fuer Basisdaten, Adresse, Sprache/Beruf und Kurzvorstellung und enthaelt die bearbeitbare Konto-E-Mail direkt im Formular.
- Im Profil kann der Nutzer aktuell Komponenten wie Augen, Augenbrauen, Mund, Brille, Haare, Bart und Ohrringe direkt per Vorschaubild auswählen.
- Der Button `Random Profilbild` setzt Seed und Varianten des aktiven Styles zufaellig neu und aktualisiert die Vorschau sofort.
- Beim Oeffnen des Avatar-Generators wird nur der aktive Style-/Komponenten-Tab geladen; weitere Varianten werden erst beim Wechsel nachgeladen.
- Debug ist kein Menüpunkt und erscheint nur als Floating-Käfer für `site_admin` bei aktivem Debug-Modus.
## Community und Rechte
- dekorative Ränge über Punkte
- Moderationsrollen:
- `forum_admin`
- `site_admin`
- `owner`
- Bewerbungen für Moderation sind vorgesehen bzw. umgesetzt.
## Recht / Consent
- Nicht notwendige Funktionen laufen nicht automatisch los.
- Es gibt einen Consent-Manager mit Kategorien:
- `analytics`
- `external_services`
- Matomo wird nur nach Analyse-Einwilligung geladen.
- Externe Karten-/Standortdienste und lokale Standortspeicherung hängen an `external_services`.
- Zusätzlich gibt es eine Standortpräferenz im Bereich `Einstellungen`:
- `disabled`
- `prompt`
- `enabled`
- Im Einstellungsbereich werden außerdem Browser-Grenzen erklärt: Die Browser-Freigabe selbst kann nicht von der Website gesetzt oder aufgehoben werden.
- Es gibt eine eigene Seite `/datenschutz` für Datenschutz- und Cookie-Hinweise sowie einen Footer-Link auf die Consent-Einstellungen.
- Fuer Distanz- und Umkreissuchen bleiben nur die erforderlichen Standortkoordinaten im Klartext gespeichert; die eigentliche Strasse/Hausnummer ist verschluesselt.
## Doku-Regel
Bei jeder künftigen Änderung müssen mindestens diese Dateien mitgeprüft werden:
- `README.md`
- `.projektstructure.txt`
- `PROJECT_CONTEXT.md`
Wenn Cookies, LocalStorage, SessionStorage, Tracking, Geolocation oder Drittanbieter neu hinzukommen oder geändert werden, müssen zusätzlich Consent und rechtliche Hinweise angepasst werden.

121
README.md
View File

@@ -1,96 +1,45 @@
# Papa-Kind-Treff # Papa-Kind-Treff Start-Doku
Stand: 2026-07-31 Stand: 2026-08-03
Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale Events, Termine und Treffen, Community-Austausch und einen geschützten Mitgliederbereich. Diese Datei ist die zentrale Start-Doku für neue Chats und den schnellen Projekteinstieg.
## Produktumfang ## Für neue Chats
- lokale Events, Termine und Treffen finden Bitte immer zuerst diese Datei lesen und danach die kanonische interne Dokumentation unter `Internal/` vollständig prüfen.
- Community/Forum mit Kategorien, Boards, Threads und Antworten
- Mitgliederbereich für Profil, optionale Kinderinfos und eigene Events
- Community-Moderation mit Rollenmodell
## Aktueller Stand Verbindliche deutsche Referenz:
- Startseite mit Hero, Suche, Event-Karussell, Community-Vorschau und Mitgliederbereichs-Block - `Internal/de/README.md`
- Community mit Board-Navigation und separater Thread-Ansicht - `Internal/de/PROJECT_CONTEXT.md`
- Community-Admin-Bereich für Bewerbungen, Meldungen, Rollen und Migration - `Internal/de/PROJECT_STRUCTURE.md`
- Impressum, Datenschutz-&-Cookies-Seite und Über-uns
- standortbasierte Sortierung für die neuesten Events
- eigener Einstellungsbereich für Standortfreigabe, Browser-Hinweise und Consent-Verwaltung
- modernes Avatar-System mit zentraler Mehr-Style-Architektur auf Basis von DiceBear
- aktive Avatar-Styles aktuell: `lorelei` und `croodles`; weitere Styles können über `config/avatar.php` freigeschaltet werden
- Profilbild-Generator mit visueller Komponenten-Auswahl, generischem Style-/Seed-/Config-Speichermodell, Style-Wechsel und komponentenbezogenen Farbfeldern pro Style; im Modal bleiben Kopfbereich und Komponentenleiste fixiert, waehrend nur die Variantenliste scrollt
- beim Style `croodles` startet die Basisfarbe standardmaessig weiss; der Hintergrund ist standardmaessig leer/ohne Vorgabe
- jedes gespeicherte Profilbild wird als lokale Benutzer-SVG unter `public/assets/avatars/users/ptk-<userId>.svg` abgelegt
- `config/avatar.php` enthaelt jetzt pro Style auch explizite Beispielpaletten, Farbgruppen pro Komponente und Beispielprofile als Einbindungsreferenz
- Consent-Manager für Analyse und externe Dienste
- Navigation aktuell mit `Home`, `Event Suche`, `Community`
- eingeloggte Nutzer sehen rechts ein Profil-Menü mit Direktlinks zu `Profil`, `Kinder`, `Events`, `Community`, `Einstellungen`
- der Mitgliederbereich ist als Seitenlayout mit linker Bereichsnavigation aufgebaut
- im Bereich `Kinder` koennen vorhandene Eintraege jetzt auch nachtraeglich bearbeitet und geloescht werden
- bei Kindern mit Geburtsdatum wird das Alter automatisch berechnet und bei Aufruf des Mitgliederbereichs bei Bedarf jaehrlich nachgezogen; ohne Geburtsdatum gilt das manuell gepflegte Alter
- sensible Profildaten werden im Mitgliederbereich jetzt konsequent app-seitig verschluesselt gespeichert; bestehende Klartextwerte werden beim Lesen schrittweise in den verschluesselten Zustand ueberfuehrt
- auch Konto-E-Mails werden jetzt app-seitig verschluesselt gespeichert; fuer Login, Verifizierung, Passwort-Reset und Dublettenpruefung wird nur ein separater HMAC-Lookup-Hash verwendet
- der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist jetzt auch auf der Datenschutz-Seite deutlich hervorgehoben
- das Profil unterstuetzt jetzt eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie optionaler Adress-Lokalisierung ueber die bereits vorhandenen Karten-/Geocoding-Dienste
- bei der Profiladresse gibt es jetzt eine Trefferauswahl fuer Adresssuche, eine Uebernahme aus dem Browser-Standort sowie einen mobilen Hinweisdialog, warum die Adresse fuer lokale Events, Termine und Treffen relevant ist
- der Profil-Editor im Mitgliederbereich ist jetzt kompakter gruppiert, nutzt ausklappbare Abschnitte und enthaelt die bearbeitbare Konto-E-Mail wieder direkt im Formular
- Debug-Floating-Button nur für `site_admin` im Debug-Modus
## Technik Parallele englische Referenz:
- Einstieg: `public/index.php` - `Internal/en/README.md`
- Templates: `partials/` - `Internal/en/PROJECT_CONTEXT.md`
- App-Logik: `src/App/` - `Internal/en/PROJECT_STRUCTURE.md`
- Assets: `public/assets/`
- Datenbankschema: `schema.sql`
## Rollen ## Dokumentationsregel
- `forum_admin` - Die Dokumentation unter `Internal/de/` und `Internal/en/` ist vollständig parallel zu pflegen.
- `site_admin` - Änderungen an Projektstand, Struktur, Wording, Datenschutz, Sicherheit oder Produktverhalten müssen immer in Deutsch und Englisch nachgezogen werden.
- `owner` - Die Root-`README.md` bleibt bewusst im Hauptordner, damit sie in neuen Chats direkt als Einstieg genannt werden kann.
Die Rechteverteilung und Community-Logik sind in `src/App/CommunityAccess.php` abgebildet. ## Kurzüberblick Projekt
Die zentrale Avatar-Verwaltung liegt in `src/App/Avatar/AvatarManager.php`. Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf:
Die aktuell aktiven Styles liegen in `src/App/Avatar/Lorelei.php` und `src/App/Avatar/Croodles.php`. - lokale Events, Termine und Treffen
Die Style-/Storage-Konfiguration liegt in `config/avatar.php`. - Community-Austausch
Der direkte Avatar-Endpunkt liegt in `public/api/avatar.php`. - geschützten Mitgliederbereich mit Profil, Kinderinfos und eigenen Events
Die visuelle Avatar-Auswahl sitzt im Dashboard-Modal in `partials/landing/account/dashboard.php`.
Beim Oeffnen des Generators werden nur die Varianten des aktuell aktiven Styles und Tabs geladen; weitere Varianten werden erst beim Wechsel nachgeladen.
Farbfelder werden pro Style direkt im Generator gepflegt und zusammen mit Stil und Seed im Profil gespeichert.
## Consent und rechtlich relevante Integrationen ## Wichtige aktuelle Produktregeln
Der aktuelle Code nutzt oder kann nutzen: - Primärbegriff im Produkt: `Events`
- notwendige Session-/Client-Cookies - `Termine` und `Treffen` ergänzend in SEO- und Erklärungstexten
- Matomo (`analytics`) - Hauptnavigation aktuell: `Home`, `Event Suche`, `Community`
- Browser-Geolocation (`external_services`) - Profil-Menü aktuell: `Profil`, `Kinder`, `Events`, `Community`, `Einstellungen`, `Abmelden`
- lokale Standortspeicherung via Cookie, `localStorage`, `sessionStorage` (`external_services`)
- Leaflet von `unpkg.com` (`external_services`)
- OpenStreetMap Nominatim (`external_services`)
- DiceBear fuer serverseitig erzeugte Profilbilder; das Ergebnis wird lokal als Benutzer-SVG gespeichert
Zur Datensicherheit: ## Datenschutz und Sicherheit
- Passwort-Hashes laufen ueber Argon2id. - sensible Profilfelder werden app-seitig verschlüsselt gespeichert
- Konto-E-Mails werden verschluesselt gespeichert und nur ueber einen separaten HMAC-Lookup-Hash adressiert. - Konto-E-Mails werden verschlüsselt gespeichert und zusätzlich über einen separaten HMAC-Lookup-Hash adressiert
- Profilfelder wie Vorname, Nachname, Strasse/Hausnummer, Telefonnummer, Beruf, Sprachen und Kurzvorstellung werden app-seitig verschluesselt gespeichert. - Standortkoordinaten bleiben technisch im Klartext, soweit sie für Distanz- und Umkreissuchen erforderlich sind
- Standortkoordinaten (`lat`, `lng`) bleiben fuer Distanz- und Umkreissuchen technisch im Klartext gespeichert. - Änderungen an Cookies, LocalStorage, SessionStorage, Geolocation, Tracking oder Drittanbietern erfordern immer auch die Prüfung und Aktualisierung von Consent und rechtlichen Hinweisen
Wichtig: ## Hinweis zur internen Ablage
- Nicht notwendige Analyse- und Drittanbieterfunktionen dürfen erst nach Einwilligung aktiv werden. - Endnutzertexte, Landingpages und UI gehören nicht nach `Internal/`
- Änderungen an Cookies, Tracking oder Drittanbietern müssen immer auch in Consent und rechtlichen Hinweisen nachgezogen werden. - `Internal/` ist ausschließlich für interne Projekt- und Systemdokumentation gedacht
## Dokumentationspflicht bei Änderungen
Bei jeder Änderung im Projekt immer mitprüfen und bei Bedarf aktualisieren:
- `README.md`
- `.projektstructure.txt`
- `PROJECT_CONTEXT.md`
Zusätzlich bei neuen Cookies, Tracking-Mechanismen oder Drittanbietern:
- Consent-Manager anpassen
- rechtliche Hinweise/Datenschutztexte ergänzen
- technische Opt-in-/Opt-out-Logik prüfen
## Dateien für neue Chats
Für spätere Chats als Einstieg besonders wichtig:
- `README.md`
- `.projektstructure.txt`
- `PROJECT_CONTEXT.md`

View File

@@ -203,7 +203,7 @@ $sectionLinks = [
<section class="account-panel" id="events"> <section class="account-panel" id="events">
<div class="account-panel__head"> <div class="account-panel__head">
<h2>Deine Events</h2> <h2>Deine Events</h2>
<p class="muted">Erstelle, bearbeite und verwalte deine eigenen Events und finde passende Termine fuer gemeinsame Treffen.</p> <p class="muted">Erstelle, bearbeite und verwalte deine eigenen Events und finde passende Termine für gemeinsame Treffen.</p>
</div> </div>
<div class="account-panel__body"> <div class="account-panel__body">
<div class="flex gap-12" style="margin:0 0 16px 0; flex-wrap: wrap;"> <div class="flex gap-12" style="margin:0 0 16px 0; flex-wrap: wrap;">
@@ -509,7 +509,7 @@ $sectionLinks = [
<input type="hidden" id="pRegion" name="region" value="<?= htmlspecialchars((string)($profile['region'] ?? ''), ENT_QUOTES) ?>"> <input type="hidden" id="pRegion" name="region" value="<?= htmlspecialchars((string)($profile['region'] ?? ''), ENT_QUOTES) ?>">
<input type="hidden" id="pLat" name="lat" value="<?= htmlspecialchars((string)($profile['lat'] ?? ''), ENT_QUOTES) ?>"> <input type="hidden" id="pLat" name="lat" value="<?= htmlspecialchars((string)($profile['lat'] ?? ''), ENT_QUOTES) ?>">
<input type="hidden" id="pLng" name="lng" value="<?= htmlspecialchars((string)($profile['lng'] ?? ''), ENT_QUOTES) ?>"> <input type="hidden" id="pLng" name="lng" value="<?= htmlspecialchars((string)($profile['lng'] ?? ''), ENT_QUOTES) ?>">
<p class="muted small" style="margin:0;">Persoenliche Angaben werden app-seitig verschluesselt gespeichert. Nur fuer Login und Kontofunktionen wird die E-Mail intern ueber einen separaten Lookup-Hash adressiert.</p> <p class="muted small" style="margin:0;">Persönliche Angaben werden app-seitig verschlüsselt gespeichert. Nur für Login und Kontofunktionen wird die E-Mail intern über einen separaten Lookup-Hash adressiert.</p>
<details class="profile-edit-section" open> <details class="profile-edit-section" open>
<summary>Basisdaten</summary> <summary>Basisdaten</summary>
<div class="profile-edit-section__body"> <div class="profile-edit-section__body">
@@ -571,11 +571,11 @@ $sectionLinks = [
<div id="profileAddressStatus" class="muted small" style="margin:0;"></div> <div id="profileAddressStatus" class="muted small" style="margin:0;"></div>
<div id="profileAddressSelectedLabel" class="muted small" style="margin:0;<?= empty($profile['street']) && empty($profile['city']) ? ' display:none;' : '' ?>"> <div id="profileAddressSelectedLabel" class="muted small" style="margin:0;<?= empty($profile['street']) && empty($profile['city']) ? ' display:none;' : '' ?>">
<?php if (!empty($profile['street']) || !empty($profile['city'])): ?> <?php if (!empty($profile['street']) || !empty($profile['city'])): ?>
Gewaehlte Adresse: <?= htmlspecialchars(trim(implode(', ', array_filter([(string)($profile['street'] ?? ''), trim((string)$profile['zip'] . ' ' . (string)$profile['city']), (string)($profile['region'] ?? '')]))), ENT_QUOTES) ?> Gewählte Adresse: <?= htmlspecialchars(trim(implode(', ', array_filter([(string)($profile['street'] ?? ''), trim((string)$profile['zip'] . ' ' . (string)$profile['city']), (string)($profile['region'] ?? '')]))), ENT_QUOTES) ?>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div id="profileAddressNeedsValidation" class="muted small" style="margin:0; display:none;"> <div id="profileAddressNeedsValidation" class="muted small" style="margin:0; display:none;">
Die Adresse wurde manuell geaendert. Vor dem Speichern bitte erneut suchen oder die Browser-Adresse uebernehmen. Die Adresse wurde manuell geändert. Vor dem Speichern bitte erneut suchen oder die Browser-Adresse übernehmen.
</div> </div>
</div> </div>
</details> </details>
@@ -596,7 +596,7 @@ $sectionLinks = [
</label> </label>
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<label class="label" for="pLangCustom">Weitere Sprachen (Kommagetrennt)</label> <label class="label" for="pLangCustom">Weitere Sprachen (kommagetrennt)</label>
<input id="pLangCustom" name="languages[]" class="input" placeholder="z. B. Italienisch, Niederländisch"> <input id="pLangCustom" name="languages[]" class="input" placeholder="z. B. Italienisch, Niederländisch">
</div> </div>
<div class="stack gap-6"> <div class="stack gap-6">
@@ -630,8 +630,8 @@ $sectionLinks = [
</div> </div>
<div class="stack gap-12"> <div class="stack gap-12">
<p class="muted" style="margin:0;">Deine Adresse hilft uns, dir passende Events, Termine und Treffen in deiner Umgebung gezielter anzuzeigen.</p> <p class="muted" style="margin:0;">Deine Adresse hilft uns, dir passende Events, Termine und Treffen in deiner Umgebung gezielter anzuzeigen.</p>
<p class="muted" style="margin:0;">Wenn keine Adresse hinterlegt ist, koennen wir lokale Bedarfe und passende Angebote deutlich schlechter einschaetzen.</p> <p class="muted" style="margin:0;">Wenn keine Adresse hinterlegt ist, können wir lokale Bedarfe und passende Angebote deutlich schlechter einschätzen.</p>
<p class="muted small" style="margin:0;">Die Adresse wird nur fuer diese Plattform-Funktionen genutzt. Strasse und Hausnummer werden verschluesselt gespeichert.</p> <p class="muted small" style="margin:0;">Die Adresse wird nur für diese Plattform-Funktionen genutzt. Straße und Hausnummer werden verschlüsselt gespeichert.</p>
<div class="flex gap-12"> <div class="flex gap-12">
<button class="btn" type="button" data-modal-close>Verstanden</button> <button class="btn" type="button" data-modal-close>Verstanden</button>
</div> </div>
@@ -646,7 +646,7 @@ $sectionLinks = [
<button class="btn ghost" type="button" data-modal-close>✕</button> <button class="btn ghost" type="button" data-modal-close>✕</button>
</div> </div>
<div class="stack gap-12"> <div class="stack gap-12">
<p class="muted small" style="margin:0;">Waehle den passenden Treffer fuer dein Profil.</p> <p class="muted small" style="margin:0;">Wähle den passenden Treffer für dein Profil.</p>
<div id="profileAddressResults" class="stack gap-12"></div> <div id="profileAddressResults" class="stack gap-12"></div>
<div class="flex gap-12"> <div class="flex gap-12">
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button> <button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
@@ -689,7 +689,7 @@ $sectionLinks = [
<div class="stack gap-6"> <div class="stack gap-6">
<label class="label" for="cAge">Alter (Jahre)</label> <label class="label" for="cAge">Alter (Jahre)</label>
<input id="cAge" name="age_years" class="input" type="number" min="0" max="18" value="<?= htmlspecialchars((string)($editChild['age_years'] ?? ''), ENT_QUOTES) ?>"> <input id="cAge" name="age_years" class="input" type="number" min="0" max="18" value="<?= htmlspecialchars((string)($editChild['age_years'] ?? ''), ENT_QUOTES) ?>">
<p class="muted small" style="margin:0;">Mit Geburtsdatum wird das Alter automatisch berechnet und jaehrlich aktualisiert. Ohne Geburtsdatum gilt der manuelle Wert.</p> <p class="muted small" style="margin:0;">Mit Geburtsdatum wird das Alter automatisch berechnet und jährlich aktualisiert. Ohne Geburtsdatum gilt der manuelle Wert.</p>
</div> </div>
</div> </div>
<div class="stack gap-6"> <div class="stack gap-6">
@@ -1044,7 +1044,7 @@ $sectionLinks = [
profileAddressSelectedLabel.style.display = 'none'; profileAddressSelectedLabel.style.display = 'none';
return; return;
} }
profileAddressSelectedLabel.textContent = `Gewaehlte Adresse: ${label}`; profileAddressSelectedLabel.textContent = `Gewählte Adresse: ${label}`;
profileAddressSelectedLabel.style.display = ''; profileAddressSelectedLabel.style.display = '';
}; };
@@ -1084,13 +1084,13 @@ $sectionLinks = [
renderSelectedAddress(normalized.label || [normalized.street, `${normalized.zip} ${normalized.city}`.trim(), normalized.region].filter(Boolean).join(', ')); renderSelectedAddress(normalized.label || [normalized.street, `${normalized.zip} ${normalized.city}`.trim(), normalized.region].filter(Boolean).join(', '));
setNeedsValidation(false); setNeedsValidation(false);
setProfileStatus('Adresse uebernommen.'); setProfileStatus('Adresse übernommen.');
}; };
const openProfileResultsModal = (results) => { const openProfileResultsModal = (results) => {
if (!profileAddressResults || !profileAddressResultsModal) return; if (!profileAddressResults || !profileAddressResultsModal) return;
profileAddressResults.innerHTML = ''; profileAddressResults.innerHTML = '';
setProfileStatus('Bitte waehle den passenden Adress-Treffer aus.'); setProfileStatus('Bitte wähle den passenden Adress-Treffer aus.');
results.forEach((result) => { results.forEach((result) => {
const normalized = normalizeResult(result); const normalized = normalizeResult(result);
@@ -1158,7 +1158,7 @@ $sectionLinks = [
return; return;
} }
if (!navigator.geolocation) { if (!navigator.geolocation) {
alert('Dieser Browser unterstuetzt keine Geolokalisierung.'); alert('Dieser Browser unterstützt keine Geolokalisierung.');
return; return;
} }
@@ -1168,7 +1168,7 @@ $sectionLinks = [
const lng = position.coords.longitude; const lng = position.coords.longitude;
reverseGeocodeProfileAddress(lat, lng, (result) => { reverseGeocodeProfileAddress(lat, lng, (result) => {
applyProfileAddress(result); applyProfileAddress(result);
setProfileStatus('Adresse aus dem Browser-Standort uebernommen.'); setProfileStatus('Adresse aus dem Browser-Standort übernommen.');
}); });
}, () => { }, () => {
setProfileStatus(''); setProfileStatus('');

View File

@@ -124,7 +124,7 @@ try {
<section class="container section" id="events"> <section class="container section" id="events">
<div class="section__intro"> <div class="section__intro">
<h2>Die neuesten Events</h2> <h2>Die neuesten Events</h2>
<p>Neue Events, Termine und Treffen fuer Vaeter und Kinder in deiner Region.</p> <p>Neue Events, Termine und Treffen für Väter und Kinder in deiner Region.</p>
</div> </div>
<div class="slider"> <div class="slider">
<button class="btn ghost slider__nav" type="button" data-slider-prev aria-label="Zurück"></button> <button class="btn ghost slider__nav" type="button" data-slider-prev aria-label="Zurück"></button>
@@ -167,7 +167,7 @@ try {
<h3 class="mt-0">Kinder (optional)</h3> <h3 class="mt-0">Kinder (optional)</h3>
<ul class="list"> <ul class="list">
<li>Alter, Geburtsdatum oder kurze Hinweise hinterlegen, wenn es dir bei der Planung hilft.</li> <li>Alter, Geburtsdatum oder kurze Hinweise hinterlegen, wenn es dir bei der Planung hilft.</li>
<li>Treffen besser einschätzen, wenn Aktivitäten zum Alter deiner Kinder passen sollen.</li> <li>Events und Treffen besser einschätzen, wenn Aktivitäten zum Alter deiner Kinder passen sollen.</li>
<li>Nur für dich sichtbar.</li> <li>Nur für dich sichtbar.</li>
</ul> </ul>
</div> </div>

View File

@@ -46,17 +46,9 @@ if ($pdo && ($q !== '' || $loc !== '' || ($lat !== null && $lng !== null))) {
<div class="container"> <div class="container">
<h1>Events suchen</h1> <h1>Events suchen</h1>
<p class="muted" style="max-width:72ch; margin:10px 0 0;"> <p class="muted" style="max-width:72ch; margin:10px 0 0;">
Finde lokale Vater-Kind-Events, Termine, Papa-Treffen, Familienaktionen und gemeinsame Freizeitangebote in deiner Naehe. Finde lokale Vater-Kind-Events, Termine, Papa-Treffen, Familienaktionen und gemeinsame Freizeitangebote in deiner Nähe.
Mit der Event Suche kannst du nach Ort, PLZ, Titel oder Beschreibung passende Events und Treffen schnell filtern. Mit der Event Suche kannst du nach Ort, PLZ, Titel oder Beschreibung passende Events und Treffen schnell filtern.
</p> </p>
<p class="muted" style="max-width:72ch; margin:8px 0 0;">
Du moechtest selbst ein Event oder Treffen anlegen?
<?php if ($isLoggedIn): ?>
<a href="/dashboard?section=events">Zur Event-Erstellung</a>.
<?php else: ?>
<a href="/login">Einloggen</a> oder <a href="/register">registrieren</a>, um eigene Events und Treffen zu erstellen.
<?php endif; ?>
</p>
<form method="get" class="form-grid single" style="margin: 14px 0; gap:12px; align-items:end;" id="searchForm"> <form method="get" class="form-grid single" style="margin: 14px 0; gap:12px; align-items:end;" id="searchForm">
<div class="stack gap-6"> <div class="stack gap-6">
<label class="label" for="q">Suchbegriff (Titel, Ort, Beschreibung)</label> <label class="label" for="q">Suchbegriff (Titel, Ort, Beschreibung)</label>
@@ -123,6 +115,14 @@ if ($pdo && ($q !== '' || $loc !== '' || ($lat !== null && $lng !== null))) {
<?php endforeach; ?> <?php endforeach; ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<p class="muted" style="max-width:72ch; margin:16px 0 0;">
Nicht das passende gefunden?
<?php if ($isLoggedIn): ?>
<a href="/dashboard?section=events">Erstell doch dein eigenes Event</a>.
<?php else: ?>
<a href="/login">Logg dich ein</a> oder <a href="/register">registriere dich</a>, um eigene Events und Treffen anzulegen.
<?php endif; ?>
</p>
<?php endif; ?> <?php endif; ?>
</div> </div>
</main> </main>

View File

@@ -17,10 +17,10 @@ $clientCookie = $config->cookiePrefix() . 'client';
</p> </p>
<div class="note" style="margin:18px 0 22px;"> <div class="note" style="margin:18px 0 22px;">
<strong>Wichtiger Hinweis zum Datenschutz:</strong> <strong>Wichtiger Hinweis zum Datenschutz:</strong>
Personenbezogene und sensible Profilangaben werden bei Papa-Kind-Treff app-seitig verschluesselt gespeichert. Personenbezogene und sensible Profilangaben werden bei Papa-Kind-Treff app-seitig verschlüsselt gespeichert.
Das betrifft insbesondere auch die Konto-E-Mail sowie Profilangaben wie Vorname, Nachname, Strasse/Hausnummer, Das betrifft insbesondere auch die Konto-E-Mail sowie Profilangaben wie Vorname, Nachname, Straße/Hausnummer,
Telefonnummer, Beruf, Sprachen, Kurzvorstellung und einzelne Kinderdaten. Fuer Login, Verifizierung und Telefonnummer, Beruf, Sprachen, Kurzvorstellung und einzelne Kinderdaten. Für Login, Verifizierung und
Passwort-Reset wird die E-Mail technisch nur ueber einen separaten HMAC-Lookup-Hash adressiert. Passwort-Reset wird die E-Mail technisch nur über einen separaten HMAC-Lookup-Hash adressiert.
</div> </div>
<section> <section>
@@ -59,8 +59,8 @@ $clientCookie = $config->cookiePrefix() . 'client';
eigene Events, Event-Teilnahmen und Community-Inhalte gehören. eigene Events, Event-Teilnahmen und Community-Inhalte gehören.
</p> </p>
<p> <p>
Soweit es sich um sensible oder besonders persoenliche Profildaten handelt, werden diese innerhalb der Soweit es sich um sensible oder besonders persönliche Profildaten handelt, werden diese innerhalb der
Anwendung verschluesselt gespeichert und verarbeitet. Anwendung verschlüsselt gespeichert und verarbeitet.
</p> </p>
<p> <p>
Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO, soweit die Verarbeitung für die Durchführung des Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO, soweit die Verarbeitung für die Durchführung des
@@ -90,8 +90,8 @@ $clientCookie = $config->cookiePrefix() . 'client';
Das dient dem Schutz besonders sensibler Angaben innerhalb der Plattform. Das dient dem Schutz besonders sensibler Angaben innerhalb der Plattform.
</p> </p>
<p> <p>
Die verschluesselte Speicherung ist damit ein grundsaetzlicher Bestandteil des Mitgliederbereichs und nicht nur Die verschlüsselte Speicherung ist damit ein grundsätzlicher Bestandteil des Mitgliederbereichs und nicht nur
eine optionale Zusatzmassnahme. eine optionale Zusatzmaßnahme.
</p> </p>
</section> </section>

View File

@@ -214,7 +214,7 @@ final class AccountPages
if ($resolvedAddress === null) { if ($resolvedAddress === null) {
$searchResults = self::geocodeAddressCandidates($street, $zip, $city, $region, 5); $searchResults = self::geocodeAddressCandidates($street, $zip, $city, $region, 5);
if (!$searchResults) { if (!$searchResults) {
throw new \RuntimeException('Die Profiladresse konnte nicht validiert werden. Bitte waehle eine Adresse aus der Suche oder ueber den Browser-Standort.'); throw new \RuntimeException('Die Profiladresse konnte nicht validiert werden. Bitte wähle eine Adresse aus der Suche oder über den Browser-Standort.');
} }
$resolvedAddress = $searchResults[0]; $resolvedAddress = $searchResults[0];
} }
@@ -637,7 +637,7 @@ final class AccountPages
return $crypto; return $crypto;
} }
throw new \RuntimeException($context . '-Daten koennen derzeit nicht sicher verarbeitet werden. Bitte spaeter erneut versuchen.'); throw new \RuntimeException($context . '-Daten können derzeit nicht sicher verarbeitet werden. Bitte später erneut versuchen.');
} }
private static function encryptOptionalProfileField(Crypto $crypto, string $value): string private static function encryptOptionalProfileField(Crypto $crypto, string $value): string

View File

@@ -287,7 +287,7 @@ final class Lorelei
for ($index = 1; $index <= 9; $index++) { for ($index = 1; $index <= 9; $index++) {
$key = sprintf('sad%02d', $index); $key = sprintf('sad%02d', $index);
$options[$key] = 'Zurueckhaltend ' . $index; $options[$key] = 'Zurückhaltend ' . $index;
} }
return $options; return $options;

View File

@@ -226,7 +226,7 @@ final class UserEmailStore
$dataKey = trim((string)(getenv('DATA_KEY') ?: '')); $dataKey = trim((string)(getenv('DATA_KEY') ?: ''));
if ($dataKey === '') { if ($dataKey === '') {
throw new \RuntimeException('EMAIL_LOOKUP_KEY oder DATA_KEY wird fuer sichere E-Mail-Speicherung benoetigt.'); throw new \RuntimeException('EMAIL_LOOKUP_KEY oder DATA_KEY wird für sichere E-Mail-Speicherung benötigt.');
} }
if (str_starts_with($dataKey, 'base64:')) { if (str_starts_with($dataKey, 'base64:')) {