diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
index 577e2ca..d216b84 100644
--- a/.gitea/workflows/deploy.yml
+++ b/.gitea/workflows/deploy.yml
@@ -95,6 +95,8 @@ jobs:
[ -f "$f" ] && cp "$f" .ci_config_deploy/
done
+ [ -f "${CONFIG_BASE_DIR}/.htaccess" ] && cp "${CONFIG_BASE_DIR}/.htaccess" .ci_config_deploy/
+
cp -R ${CONFIG_ENV_DIR}/. .ci_config_deploy/
echo "🔁 config → ${TARGET_PATH}${CONFIG_BASE_DIR}/"
@@ -105,11 +107,11 @@ jobs:
set ftp:ssl-protect-data true;
set ssl:verify-certificate no;
lcd .ci_config_deploy;
- mirror -R --delete --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
+ mirror -R --delete --exclude secrets.local.php --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
bye
" || exit 1
else
echo "⚠️ Config-Deploy übersprungen: ${CONFIG_BASE_DIR} oder ${CONFIG_ENV_DIR} fehlt"
fi
- echo "✅ Deploy abgeschlossen"
\ No newline at end of file
+ echo "✅ Deploy abgeschlossen"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2b101f8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+# Local server secrets
+/config/secrets.local.php
+
+# Local environment files
+/.env
+/.env.*
+!/.env.example
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index e812a15..97d46b3 100755
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -90,6 +90,7 @@ deploy:staging:
cp "$f" .ci_config_deploy/
fi
done
+ [ -f "${CONFIG_BASE_DIR}/.htaccess" ] && cp "${CONFIG_BASE_DIR}/.htaccess" .ci_config_deploy/
fi
if [ -d "${CONFIG_ENV_DIR}" ]; then
@@ -104,7 +105,7 @@ deploy:staging:
set ftp:ssl-protect-data true;
set ssl:verify-certificate no;
lcd .ci_config_deploy;
- mirror -R --delete --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
+ mirror -R --delete --exclude secrets.local.php --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
bye
" || { echo "❌ Upload für gemischtes Config-Verzeichnis fehlgeschlagen."; exit 1; }
@@ -197,6 +198,7 @@ deploy:production:
cp "$f" .ci_config_deploy/
fi
done
+ [ -f "${CONFIG_BASE_DIR}/.htaccess" ] && cp "${CONFIG_BASE_DIR}/.htaccess" .ci_config_deploy/
fi
if [ -d "${CONFIG_ENV_DIR}" ]; then
@@ -211,7 +213,7 @@ deploy:production:
set ftp:ssl-protect-data true;
set ssl:verify-certificate no;
lcd .ci_config_deploy;
- mirror -R --delete --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
+ mirror -R --delete --exclude secrets.local.php --exclude .gitkeep ./ ${TARGET_PATH}${CONFIG_BASE_DIR}/;
bye
" || { echo "❌ Upload für gemischtes Config-Verzeichnis fehlgeschlagen."; exit 1; }
@@ -224,4 +226,3 @@ deploy:production:
only:
- main
# when: manual
-
diff --git a/Internal/db/schema.sql b/Internal/db/schema.sql
index 008cce9..0d87348 100755
--- a/Internal/db/schema.sql
+++ b/Internal/db/schema.sql
@@ -154,9 +154,10 @@ CREATE TABLE listing_places (
place_kind VARCHAR(80) NULL,
opening_hours_note TEXT NULL,
opening_hours_json LONGTEXT NULL,
- provider_hint ENUM('manual','osm','google') NOT NULL DEFAULT 'manual',
+ provider_hint ENUM('manual','osm','google','azure') NOT NULL DEFAULT 'manual',
external_place_id VARCHAR(190) NULL,
google_place_id VARCHAR(190) NULL,
+ provider_links_json LONGTEXT NULL,
rating_value DECIMAL(3,2) NULL,
rating_count INT UNSIGNED NULL,
status ENUM('draft','published','archived') NOT NULL DEFAULT 'published',
diff --git a/Internal/de/PROJECT_CONTEXT.md b/Internal/de/PROJECT_CONTEXT.md
index 2561be1..3e157d9 100644
--- a/Internal/de/PROJECT_CONTEXT.md
+++ b/Internal/de/PROJECT_CONTEXT.md
@@ -66,6 +66,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
- Bestehende gespeicherte Community-Level erhalten diese beiden Rechte im Fallback automatisch ab 750 Punkten, solange sie nicht explizit anders gespeichert wurden.
## Technischer Rahmen
+- Externe API-Schlüssel werden ausschließlich über Server-Umgebungsvariablen oder die lokale, nicht versionierte Datei `config/secrets.local.php` außerhalb des Webroots bereitgestellt.
- eigener Front-Controller in `public/index.php`
- Templates unter `partials/`
- Geschäftslogik unter `src/App/`
@@ -74,7 +75,9 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
## Neue Ausbaustufe: Termin-/Ort-System
- Neue Tabellenbasis: `listing_places`, `listings`, `listing_occurrences`, `listing_prices`, `listing_benefits`, `listing_categories`
- Ziel: spätere Unterscheidung zwischen eigenem Event, Partner-Angebot, allgemeinem Ort und redaktionellem Termin
-- Google ist noch nicht aktiv integriert; das System wird nur vorbereitend über Settings und Datenfelder darauf ausgelegt
+- In der Admin-Freigabe werden aktivierte Ortsanbieter serverseitig parallel durchsucht: OpenStreetMap/Nominatim, Google Places und Azure Maps. Ein Admin wählt einen Treffer gezielt aus; je Ort können Verknüpfungen mehrerer Anbieter gespeichert werden. Google-Gesamtwertung und Bewertungsanzahl werden erst nach der ausgewählten Google-Verknüpfung geladen.
+- Die Admin-Ansicht für Orte und Veranstaltungen hat die Unterpunkte `Freigaben`, `Bestehende Ortsbearbeitung` mit Suchfeld und `Fehlende Angaben`. Letzterer filtert veröffentlichte Einträge nach Verknüpfungen, die bei mindestens einem aktuell aktivierten Anbieter fehlen.
+- Eine manuelle Admin-Suche überträgt Name und Adressdaten des offenen Eintrags an die aktivierten Ortsanbieter; Datenschutz- und Consent-Hinweise müssen vor dem produktiven Aktivieren geprüft werden.
## Vorgemerkte To-dos
- Kalender-Integration großer Anbieter soll später zusätzlich zum ICS-Feed kommen, zuerst vor allem für Google und Microsoft/Outlook.
diff --git a/Internal/de/PROJECT_STRUCTURE.md b/Internal/de/PROJECT_STRUCTURE.md
index 1c5cf1d..547ef59 100644
--- a/Internal/de/PROJECT_STRUCTURE.md
+++ b/Internal/de/PROJECT_STRUCTURE.md
@@ -4,6 +4,8 @@ Stand: 2026-08-10
## Projektordner
- `config/`: Konfigurationen, inklusive `prod/` und `staging/`
+- `config/secrets.local.php`: lokale, nicht versionierte Server-Geheimnisse außerhalb des Webroots; Vorlage: `config/secrets.local.php.example`
+- `config/.htaccess`: zusätzliche Webserver-Sperre für den gesamten Konfigurationsordner; beide Deployment-Workflows schließen `secrets.local.php` explizit vom synchronisierenden Löschen aus.
- `partials/`: Templates, aufgeteilt in `landing/` und `structure/`
- `public/`: Webroot mit Assets und Seiten-Entry-Points
- `src/`: Backend-/Business-Logik und Kernklassen
@@ -30,6 +32,8 @@ Stand: 2026-08-10
- Preis-, Bild- und Ortseingabe für Orte und Veranstaltungen sowie reduzierte Event-Maske: `partials/landing/account/dashboard.php` plus Speicherung in `src/App/ListingCatalog.php`
- Freigaben sowie Änderungs- und Löschanfragen für Orte und Veranstaltungen: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
- Direktbearbeitung noch nicht veröffentlichter Orts- und Veranstaltungsvorschläge sowie Rechteauflösung für deren Freigabe: `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
+- Server-seitige Ortsanbieter-Suche und sichere Trefferauswahl für die Admin-Freigabe: `src/App/PlaceProviderLookup.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
+- Admin-Listen für bestehende Ortsbearbeitung und fehlende Anbieter-Verknüpfungen: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
- Rollenvergabe per Benutzersuche im User Management: `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`, `src/App/CommunityAccess.php`
- Legacy-Eigen-Events wurden erweitert in `src/App/AccountPages.php` und `Internal/db/schema.sql` um Kategorie- und Bildfelder
- Kategorien-Prüfung und Zusammenführung für Seiten-Admins liegen ebenfalls in `partials/landing/account/dashboard.php` mit Logik in `src/App/ListingCatalog.php`
diff --git a/Internal/de/README.md b/Internal/de/README.md
index 202a584..40910e9 100644
--- a/Internal/de/README.md
+++ b/Internal/de/README.md
@@ -51,6 +51,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale
- neue Basisklassen: `src/App/SystemSettings.php`, `src/App/ListingCatalog.php`
## Datenschutz und Sicherheit
+- Server-Geheimnisse können lokal über `config/secrets.local.php` außerhalb des Webroots geladen werden; die Datei wird nicht versioniert und bei Deployments nicht gelöscht.
- Passwort-Hashes über Argon2id
- Konto-E-Mails verschlüsselt plus separater HMAC-Lookup-Hash
- persönliche Kalender-Feed-Tokens verschlüsselt plus separater SHA-256-Lookup-Hash
diff --git a/Internal/en/PROJECT_CONTEXT.md b/Internal/en/PROJECT_CONTEXT.md
index a754387..b3727f8 100644
--- a/Internal/en/PROJECT_CONTEXT.md
+++ b/Internal/en/PROJECT_CONTEXT.md
@@ -66,6 +66,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events
- Existing stored community levels receive these two rights automatically in the fallback from 750 points upward, unless they were explicitly saved differently.
## Technical Frame
+- External API keys are provided only through server environment variables or the local, unversioned `config/secrets.local.php` file outside the webroot.
- custom front controller in `public/index.php`
- templates under `partials/`
- business logic under `src/App/`
@@ -74,7 +75,9 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events
## New Expansion Stage: Listings and Places
- New table base: `listing_places`, `listings`, `listing_occurrences`, `listing_prices`, `listing_benefits`, `listing_categories`
- Goal: later distinguish between own event, partner offer, general place, and editorial event entry
-- Google is not integrated productively yet; the system is only being prepared for it via settings and data fields
+- In admin approvals, enabled place providers are searched server-side in parallel: OpenStreetMap/Nominatim, Google Places, and Azure Maps. An admin deliberately selects a match; links to multiple providers can be saved for one place. Google aggregate rating and rating count are loaded only after the selected Google link.
+- The admin view for places and events has the sub-items `Approvals`, `Existing place editing` with a search field, and `Missing information`. The latter filters published entries by links missing from at least one currently enabled provider.
+- A manual admin lookup sends the open entry's name and address data to enabled place providers; privacy and consent notices must be reviewed before enabling this in production.
## Deferred To-dos
- Large calendar-provider integrations should be added later in addition to the ICS feed, starting primarily with Google and Microsoft/Outlook.
diff --git a/Internal/en/PROJECT_STRUCTURE.md b/Internal/en/PROJECT_STRUCTURE.md
index 8adb2a4..a94a0dc 100644
--- a/Internal/en/PROJECT_STRUCTURE.md
+++ b/Internal/en/PROJECT_STRUCTURE.md
@@ -4,6 +4,8 @@ Updated: 2026-08-10
## Main Directories
- `config/`: configuration, including `prod/` and `staging/`
+- `config/secrets.local.php`: local, unversioned server secrets outside the webroot; template: `config/secrets.local.php.example`
+- `config/.htaccess`: additional web-server protection for the entire configuration directory; both deployment workflows explicitly exclude `secrets.local.php` from synchronizing deletion.
- `partials/`: templates split into `landing/` and `structure/`
- `public/`: webroot, assets, and page entry points
- `src/`: backend and business logic
@@ -30,6 +32,8 @@ Updated: 2026-08-10
- pricing, image upload, and place/address input for places and event-style entries plus a reduced own-event form: `partials/landing/account/dashboard.php` with persistence in `src/App/ListingCatalog.php`
- approvals as well as change and deletion requests for places and event-style entries: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
- direct editing for not-yet-published place and event-style suggestions plus approval-right resolution: `src/App/CommunityAccess.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
+- server-side place-provider lookup and secure match selection for admin approvals: `src/App/PlaceProviderLookup.php`, `src/App/AccountPages.php`, `src/App/ListingCatalog.php`, `partials/landing/account/dashboard.php`
+- admin lists for existing place editing and missing provider links: `src/App/ListingCatalog.php`, `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`
- role assignment via user search in User Management: `src/App/AccountPages.php`, `partials/landing/account/dashboard.php`, `src/App/CommunityAccess.php`
- legacy own events were extended in `src/App/AccountPages.php` and `Internal/db/schema.sql` with category and image fields
diff --git a/Internal/en/README.md b/Internal/en/README.md
index c9e4f83..d7b1b4c 100644
--- a/Internal/en/README.md
+++ b/Internal/en/README.md
@@ -50,6 +50,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, app
- new base classes: `src/App/SystemSettings.php`, `src/App/ListingCatalog.php`
## Privacy and Security
+- Server secrets can be loaded locally through `config/secrets.local.php` outside the webroot; the file is not versioned and is not deleted by deployments.
- password hashes via Argon2id
- account emails are encrypted and additionally addressed through a separate HMAC lookup hash
- personal calendar-feed tokens are stored encrypted and additionally resolved through a separate SHA-256 lookup hash
diff --git a/README.md b/README.md
index 1f08ba9..dafd991 100644
--- a/README.md
+++ b/README.md
@@ -67,10 +67,15 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf:
- Im Mitgliederbereich `Events` gibt es jetzt zusätzlich einen ICS-Download und einen persönlichen abonnierbaren Kalender-Feed für alle eigenen Events und Event-Teilnahmen
- Der Bereich `Events` ist im Mitgliederbereich jetzt zusätzlich in die Tabs `Meine Events`, `Kalendersynchronisation` und `Abgelaufene Events` gegliedert
- Vorschläge für neue Orte und Veranstaltungen werden schon vor der Freigabe als mögliche Ortsauswahl für Events berücksichtigt, solange sie nicht abgelehnt oder archiviert wurden
+- In der Admin-Freigabe können berechtigte Nutzer offene Orte und Veranstaltungen serverseitig bei aktivierten Anbietern (OpenStreetMap, Google Places, Azure Maps) suchen und einen Treffer gezielt verknüpfen; Google-Gesamtwertung und Bewertungsanzahl werden erst nach einer bewussten Google-Verknüpfung geladen.
+- Die Admin-Ansicht für Orte und Veranstaltungen gliedert sich in `Freigaben`, `Bestehende Ortsbearbeitung` mit Suchfeld und `Fehlende Angaben`; Letztere listet veröffentlichte Einträge ohne Verknüpfung zu mindestens einem aktuell aktivierten Anbieter.
- Für später vorgemerkt: direkte Kalender-Anbindung großer Anbieter wie Google und Microsoft/Outlook per OAuth, zusätzlich zum bestehenden ICS-Feed
- Interne Projektdateien und das Datenbankschema liegen nicht mehr im Root, sondern unter `Internal/`
## Datenschutz und Sicherheit
+- Server-Geheimnisse werden optional aus `config/secrets.local.php` außerhalb des Webroots geladen; die Datei ist lokal, wird nicht versioniert und bei Deployments nicht gelöscht.
+- Schlüssel für Ortsanbieter bleiben ausschließlich serverseitig. Die Auswahl von Suchtreffern erfolgt über kurzzeitig in der Server-Session abgelegte Treffer, nicht über vom Browser übermittelte Provider-IDs.
+- Das Aktivieren von Ortsanbietern muss mit den Datenschutz- und Consent-Hinweisen abgeglichen werden, weil eine manuelle Admin-Suche Name und Adressdaten eines offenen Eintrags an den gewählten Anbieter überträgt.
- sensible Profilfelder werden app-seitig verschlüsselt gespeichert
- Konto-E-Mails werden verschlüsselt gespeichert und zusätzlich über einen separaten HMAC-Lookup-Hash adressiert
- persönliche Kalender-Feed-Tokens werden verschlüsselt gespeichert und zusätzlich über einen separaten SHA-256-Lookup-Hash adressiert
diff --git a/config/.htaccess b/config/.htaccess
new file mode 100644
index 0000000..40ec0c6
--- /dev/null
+++ b/config/.htaccess
@@ -0,0 +1,7 @@
+
+ Require all denied
+
+
+
+ Deny from all
+
diff --git a/config/config.php b/config/config.php
index cdc97a2..fbc6d87 100755
--- a/config/config.php
+++ b/config/config.php
@@ -1,6 +1,12 @@
-
Orte & Veranstaltungen freigeben
Nur noch nicht freigegebene Orte und Veranstaltungen prüfen und bei Bedarf vor der Freigabe bearbeiten.
+
+
Orte & Veranstaltungen
+
Freigaben bearbeiten sowie externe Ortsdaten für bestehende Einträge ergänzen oder aktualisieren.
Keine offenen Orte oder Veranstaltungen zur Freigabe.
+
+
+
Gezeigt werden veröffentlichte Einträge, denen mindestens einer der aktuell aktivierten Anbieter fehlt: = $activePlaceProviders ? htmlspecialchars(implode(', ', array_map('strtoupper', $activePlaceProviders)), ENT_QUOTES) : 'keine aktiven Anbieter' ?>.
+
+
Keine offenen Orte oder Veranstaltungen zur Freigabe.
+
Keine passenden bestehenden Orte oder Veranstaltungen gefunden.
@@ -1000,20 +1038,35 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
Orts- und Bewertungsdienste
+
+
So bleibt eine kostenfreie Basis aktiv, während Google später zusätzlich vorbereitet werden kann.
+
Bei einer manuellen Admin-Suche werden Name und Adressdaten des offenen Eintrags an die hier aktivierten Anbieter übertragen. Datenschutzhinweise und gegebenenfalls Consent-Text bitte vor dem produktiven Aktivieren prüfen.