This commit is contained in:
@@ -16,6 +16,7 @@ Aktueller Projektstand
|
|||||||
- Kinder koennen im Mitgliederbereich zusaetzlich nachtraeglich bearbeitet und geloescht werden.
|
- 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.
|
- 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.
|
- Profilbezogene sensible Angaben wie Vorname, Nachname, Telefonnummer, Beruf, Sprachen und Kurzvorstellung werden app-seitig verschluesselt gespeichert; vorhandene Klartextwerte werden bei der Nutzung schrittweise nachmigriert.
|
||||||
|
- Das Profil besitzt jetzt zusaetzlich eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie eine optionale Adress-Lokalisierung ueber die vorhandenen externen Karten-/Geocoding-Dienste.
|
||||||
- Alte statische Avatar-Presets und frühere Layer-Assets sind entfernt.
|
- 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.
|
- Ä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.
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
|
|||||||
- Kinder koennen im Mitgliederbereich angelegt, nachtraeglich bearbeitet und wieder geloescht werden.
|
- 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.
|
- 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.
|
- 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.
|
||||||
|
- 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.
|
||||||
- Im Profil kann der Nutzer aktuell Komponenten wie Augen, Augenbrauen, Mund, Brille, Haare, Bart und Ohrringe direkt per Vorschaubild auswählen.
|
- 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.
|
- 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.
|
- Beim Oeffnen des Avatar-Generators wird nur der aktive Style-/Komponenten-Tab geladen; weitere Varianten werden erst beim Wechsel nachgeladen.
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale
|
|||||||
- im Bereich `Kinder` koennen vorhandene Eintraege jetzt auch nachtraeglich bearbeitet und geloescht werden
|
- 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
|
- 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
|
- sensible Profildaten werden im Mitgliederbereich jetzt konsequent app-seitig verschluesselt gespeichert; bestehende Klartextwerte werden beim Lesen schrittweise in den verschluesselten Zustand ueberfuehrt
|
||||||
|
- das Profil unterstuetzt jetzt eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie optionaler Adress-Lokalisierung ueber die bereits vorhandenen Karten-/Geocoding-Dienste
|
||||||
- Debug-Floating-Button nur für `site_admin` im Debug-Modus
|
- Debug-Floating-Button nur für `site_admin` im Debug-Modus
|
||||||
|
|
||||||
## Technik
|
## Technik
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ $sectionLinks = [
|
|||||||
<dl class="account-kv">
|
<dl class="account-kv">
|
||||||
<div><dt>Name</dt><dd><?= htmlspecialchars(trim($profile['first_name'] . ' ' . $profile['last_name']), ENT_QUOTES) ?></dd></div>
|
<div><dt>Name</dt><dd><?= htmlspecialchars(trim($profile['first_name'] . ' ' . $profile['last_name']), ENT_QUOTES) ?></dd></div>
|
||||||
<div><dt>Anzeigename</dt><dd><?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?></dd></div>
|
<div><dt>Anzeigename</dt><dd><?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?></dd></div>
|
||||||
<div><dt>Ort</dt><dd><?= htmlspecialchars(trim($profile['city'] . ' ' . $profile['zip']), ENT_QUOTES) ?></dd></div>
|
<div><dt>Adresse</dt><dd><?= htmlspecialchars(trim(implode(', ', array_filter([(string)($profile['street'] ?? ''), trim((string)$profile['zip'] . ' ' . (string)$profile['city']), (string)($profile['region'] ?? '')]))), ENT_QUOTES) ?></dd></div>
|
||||||
<div><dt>E-Mail</dt><dd><?= htmlspecialchars($profile['email'], ENT_QUOTES) ?></dd></div>
|
<div><dt>E-Mail</dt><dd><?= htmlspecialchars($profile['email'], ENT_QUOTES) ?></dd></div>
|
||||||
<div><dt>Telefon</dt><dd><?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?></dd></div>
|
<div><dt>Telefon</dt><dd><?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?></dd></div>
|
||||||
<div><dt>Beruf</dt><dd><?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?></dd></div>
|
<div><dt>Beruf</dt><dd><?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?></dd></div>
|
||||||
@@ -486,6 +486,9 @@ $sectionLinks = [
|
|||||||
</div>
|
</div>
|
||||||
<form method="post" class="stack gap-12">
|
<form method="post" class="stack gap-12">
|
||||||
<input type="hidden" name="action" value="profile">
|
<input type="hidden" name="action" value="profile">
|
||||||
|
<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="pLng" name="lng" value="<?= htmlspecialchars((string)($profile['lng'] ?? ''), ENT_QUOTES) ?>">
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="stack gap-6">
|
<div class="stack gap-6">
|
||||||
<label class="label" for="pName">Anzeigename</label>
|
<label class="label" for="pName">Anzeigename</label>
|
||||||
@@ -498,12 +501,12 @@ $sectionLinks = [
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="stack gap-6">
|
<div class="stack gap-6">
|
||||||
<label class="label" for="pLast">Nachname</label>
|
<label class="label" for="pStreet">Straße / Hausnummer</label>
|
||||||
<input id="pLast" name="last_name" class="input" value="<?= htmlspecialchars($profile['last_name'], ENT_QUOTES) ?>">
|
<input id="pStreet" name="street" class="input" value="<?= htmlspecialchars((string)($profile['street'] ?? ''), ENT_QUOTES) ?>" placeholder="z. B. Musterstraße 12">
|
||||||
</div>
|
</div>
|
||||||
<div class="stack gap-6">
|
<div class="stack gap-6">
|
||||||
<label class="label" for="pCity">Ort</label>
|
<label class="label" for="pLast">Nachname</label>
|
||||||
<input id="pCity" name="city" class="input" value="<?= htmlspecialchars($profile['city'], ENT_QUOTES) ?>">
|
<input id="pLast" name="last_name" class="input" value="<?= htmlspecialchars($profile['last_name'], ENT_QUOTES) ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
@@ -511,11 +514,25 @@ $sectionLinks = [
|
|||||||
<label class="label" for="pZip">PLZ</label>
|
<label class="label" for="pZip">PLZ</label>
|
||||||
<input id="pZip" name="zip" class="input" value="<?= htmlspecialchars($profile['zip'], ENT_QUOTES) ?>">
|
<input id="pZip" name="zip" class="input" value="<?= htmlspecialchars($profile['zip'], ENT_QUOTES) ?>">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="stack gap-6">
|
||||||
|
<label class="label" for="pCity">Ort</label>
|
||||||
|
<input id="pCity" name="city" class="input" value="<?= htmlspecialchars($profile['city'], ENT_QUOTES) ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-grid">
|
||||||
|
<div class="stack gap-6">
|
||||||
|
<label class="label" for="pRegionView">Region / Bezirk</label>
|
||||||
|
<input id="pRegionView" class="input" value="<?= htmlspecialchars((string)($profile['region'] ?? ''), ENT_QUOTES) ?>" placeholder="wird bei Lokalisierung ergänzt">
|
||||||
|
</div>
|
||||||
<div class="stack gap-6">
|
<div class="stack gap-6">
|
||||||
<label class="label" for="pPhone">Telefon (mobil)</label>
|
<label class="label" for="pPhone">Telefon (mobil)</label>
|
||||||
<input id="pPhone" name="contact_phone" class="input" value="<?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?>">
|
<input id="pPhone" name="contact_phone" class="input" value="<?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex gap-12" style="flex-wrap:wrap;">
|
||||||
|
<button class="btn ghost" type="button" id="btnProfileLocate">Adresse lokalisieren</button>
|
||||||
|
<p class="muted small" style="margin:0;">Verwendet dieselben Karten-/Adressdienste wie bei Events und nur nach Einwilligung in externe Dienste.</p>
|
||||||
|
</div>
|
||||||
<div class="stack gap-6">
|
<div class="stack gap-6">
|
||||||
<label class="label">Sprachen (Mehrfachauswahl)</label>
|
<label class="label">Sprachen (Mehrfachauswahl)</label>
|
||||||
<div class="chips" style="flex-wrap: wrap;">
|
<div class="chips" style="flex-wrap: wrap;">
|
||||||
@@ -728,6 +745,14 @@ $sectionLinks = [
|
|||||||
const zipInput = document.getElementById('evZip');
|
const zipInput = document.getElementById('evZip');
|
||||||
const cityInput = document.getElementById('evCity');
|
const cityInput = document.getElementById('evCity');
|
||||||
const regionInput = document.getElementById('evRegion');
|
const regionInput = document.getElementById('evRegion');
|
||||||
|
const profileLocateButton = document.getElementById('btnProfileLocate');
|
||||||
|
const profileStreetInput = document.getElementById('pStreet');
|
||||||
|
const profileZipInput = document.getElementById('pZip');
|
||||||
|
const profileCityInput = document.getElementById('pCity');
|
||||||
|
const profileRegionInput = document.getElementById('pRegion');
|
||||||
|
const profileRegionViewInput = document.getElementById('pRegionView');
|
||||||
|
const profileLatInput = document.getElementById('pLat');
|
||||||
|
const profileLngInput = document.getElementById('pLng');
|
||||||
let map, marker;
|
let map, marker;
|
||||||
|
|
||||||
function ensureLeaflet(callback) {
|
function ensureLeaflet(callback) {
|
||||||
@@ -818,6 +843,28 @@ $sectionLinks = [
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function geocodeAddressData(query, onSuccess) {
|
||||||
|
if (!window.PKTConsent || !window.PKTConsent.has('external_services')) {
|
||||||
|
alert('Für Karten und Adresssuche bitte zuerst die externen Dienste in den Cookie-Einstellungen erlauben.');
|
||||||
|
window.PKTConsent?.openPreferences?.();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch('https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&addressdetails=1&q=' + encodeURIComponent(query), {
|
||||||
|
headers: { 'Accept-Language': 'de', 'User-Agent': 'papa-kind-treff/1.0' },
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (Array.isArray(data) && data[0]) {
|
||||||
|
onSuccess?.(data[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alert('Adresse konnte nicht eindeutig lokalisiert werden.');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert('Adresssuche aktuell nicht erreichbar.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initMap() {
|
function initMap() {
|
||||||
if (map) { map.invalidateSize(); return; }
|
if (map) { map.invalidateSize(); return; }
|
||||||
map = L.map(mapContainer).setView([51.1657, 10.4515], 6);
|
map = L.map(mapContainer).setView([51.1657, 10.4515], 6);
|
||||||
@@ -863,6 +910,62 @@ $sectionLinks = [
|
|||||||
geocodeAndPlace(parts.join(', '));
|
geocodeAndPlace(parts.join(', '));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(function(){
|
||||||
|
const syncProfileRegionView = () => {
|
||||||
|
if (profileRegionViewInput && profileRegionInput) {
|
||||||
|
profileRegionInput.value = profileRegionViewInput.value.trim();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearProfileCoordinates = () => {
|
||||||
|
if (profileLatInput) profileLatInput.value = '';
|
||||||
|
if (profileLngInput) profileLngInput.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
[profileStreetInput, profileZipInput, profileCityInput].forEach((input) => {
|
||||||
|
input?.addEventListener('input', clearProfileCoordinates);
|
||||||
|
});
|
||||||
|
|
||||||
|
profileRegionViewInput?.addEventListener('input', () => {
|
||||||
|
syncProfileRegionView();
|
||||||
|
clearProfileCoordinates();
|
||||||
|
});
|
||||||
|
|
||||||
|
profileLocateButton?.addEventListener('click', () => {
|
||||||
|
const parts = [
|
||||||
|
profileStreetInput?.value || '',
|
||||||
|
profileZipInput?.value || '',
|
||||||
|
profileCityInput?.value || '',
|
||||||
|
profileRegionViewInput?.value || '',
|
||||||
|
].map(v => v.trim()).filter(Boolean);
|
||||||
|
|
||||||
|
if (!parts.length) {
|
||||||
|
alert('Bitte zuerst mindestens einen Teil der Adresse eingeben.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
geocodeAddressData(parts.join(', '), (result) => {
|
||||||
|
const address = result.address || {};
|
||||||
|
const street = [address.road || '', address.house_number || ''].filter(Boolean).join(' ').trim();
|
||||||
|
const region = address.suburb || address.state || address.county || '';
|
||||||
|
const city = address.city || address.town || address.village || '';
|
||||||
|
|
||||||
|
if (profileStreetInput && street) profileStreetInput.value = street;
|
||||||
|
if (profileZipInput && address.postcode) profileZipInput.value = address.postcode;
|
||||||
|
if (profileCityInput && city) profileCityInput.value = city;
|
||||||
|
if (profileRegionViewInput) profileRegionViewInput.value = region;
|
||||||
|
if (profileRegionInput) profileRegionInput.value = region;
|
||||||
|
|
||||||
|
const lat = parseFloat(result.lat);
|
||||||
|
const lng = parseFloat(result.lon);
|
||||||
|
if (profileLatInput) profileLatInput.value = Number.isNaN(lat) ? '' : lat.toFixed(7);
|
||||||
|
if (profileLngInput) profileLngInput.value = Number.isNaN(lng) ? '' : lng.toFixed(7);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
syncProfileRegionView();
|
||||||
|
})();
|
||||||
|
|
||||||
<?php if ($editing): ?>
|
<?php if ($editing): ?>
|
||||||
(function(){
|
(function(){
|
||||||
const modal = document.getElementById('modalEvent');
|
const modal = document.getElementById('modalEvent');
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ $clientCookie = $config->cookiePrefix() . 'client';
|
|||||||
<p>
|
<p>
|
||||||
Wenn du ein Konto anlegst oder den Mitgliederbereich nutzt, verarbeiten wir die dafür notwendigen Daten,
|
Wenn du ein Konto anlegst oder den Mitgliederbereich nutzt, verarbeiten wir die dafür notwendigen Daten,
|
||||||
insbesondere E-Mail-Adresse, Passwort-Hash, Verifikationsstatus sowie die von dir gepflegten Profilangaben.
|
insbesondere E-Mail-Adresse, Passwort-Hash, Verifikationsstatus sowie die von dir gepflegten Profilangaben.
|
||||||
Dazu können Anzeigename, Name, Ort, Sprachen, Kurzbeschreibung, optionale Kinderangaben,
|
Dazu können Anzeigename, Name, Adresse, Ort, Sprachen, Kurzbeschreibung, optionale Kinderangaben,
|
||||||
eigene Events, Event-Teilnahmen und Community-Inhalte gehören.
|
eigene Events, Event-Teilnahmen und Community-Inhalte gehören.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -137,6 +137,11 @@ $clientCookie = $config->cookiePrefix() . 'client';
|
|||||||
Je nach Auswahl wird dieser nur für den aktuellen Besuch oder darüber hinaus lokal gespeichert,
|
Je nach Auswahl wird dieser nur für den aktuellen Besuch oder darüber hinaus lokal gespeichert,
|
||||||
damit dir passende Treffen in deiner Nähe angezeigt werden können.
|
damit dir passende Treffen in deiner Nähe angezeigt werden können.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Zusätzlich kannst du im Mitgliederbereich deine Profiladresse über die gleichen externen Adressdienste
|
||||||
|
lokalisieren lassen, die auch für Event-Adressen genutzt werden. Dabei werden die von dir eingegebenen
|
||||||
|
Adressbestandteile an den jeweiligen Geocoding-Dienst übermittelt.
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Für Karten- und Adressfunktionen werden derzeit externe Dienste genutzt:
|
Für Karten- und Adressfunktionen werden derzeit externe Dienste genutzt:
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ CREATE TABLE user_profiles (
|
|||||||
display_name VARCHAR(120) NOT NULL,
|
display_name VARCHAR(120) NOT NULL,
|
||||||
first_name VARBINARY(512) NULL,
|
first_name VARBINARY(512) NULL,
|
||||||
last_name VARBINARY(512) NULL,
|
last_name VARBINARY(512) NULL,
|
||||||
|
street VARBINARY(512) NULL,
|
||||||
share_level ENUM('basic','papa','papa_contact') NOT NULL DEFAULT 'basic',
|
share_level ENUM('basic','papa','papa_contact') NOT NULL DEFAULT 'basic',
|
||||||
children_visibility ENUM('hidden','age_only','details') NOT NULL DEFAULT 'hidden',
|
children_visibility ENUM('hidden','age_only','details') NOT NULL DEFAULT 'hidden',
|
||||||
zip CHAR(5) NULL,
|
zip CHAR(5) NULL,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ final class AccountPages
|
|||||||
private const ENCRYPTED_PROFILE_FIELDS = [
|
private const ENCRYPTED_PROFILE_FIELDS = [
|
||||||
'first_name',
|
'first_name',
|
||||||
'last_name',
|
'last_name',
|
||||||
|
'street',
|
||||||
'contact_phone',
|
'contact_phone',
|
||||||
'profession',
|
'profession',
|
||||||
'languages',
|
'languages',
|
||||||
@@ -185,6 +186,7 @@ final class AccountPages
|
|||||||
}
|
}
|
||||||
$firstNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['first_name']));
|
$firstNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['first_name']));
|
||||||
$lastNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['last_name']));
|
$lastNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['last_name']));
|
||||||
|
$streetEnc = self::encryptOptionalProfileField($crypto, trim((string)($_POST['street'] ?? '')));
|
||||||
$phoneEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['contact_phone']));
|
$phoneEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['contact_phone']));
|
||||||
$professionEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['profession']));
|
$professionEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['profession']));
|
||||||
$languagesEnc = self::encryptOptionalProfileField($crypto, trim((string)$languages));
|
$languagesEnc = self::encryptOptionalProfileField($crypto, trim((string)$languages));
|
||||||
@@ -193,13 +195,17 @@ final class AccountPages
|
|||||||
if ($profileSettings) {
|
if ($profileSettings) {
|
||||||
$profileSettings->ensureSchema();
|
$profileSettings->ensureSchema();
|
||||||
}
|
}
|
||||||
$stmt = $pdo?->prepare('UPDATE user_profiles SET display_name=:name, first_name=:fname, last_name=:lname, zip=:zip, city=:city, profession=:prof, languages=:langs, about=:about, contact_phone=:phone, location_tracking_preference=:locationPref, updated_at=NOW() WHERE user_id=:id');
|
$stmt = $pdo?->prepare('UPDATE user_profiles SET display_name=:name, first_name=:fname, last_name=:lname, street=:street, zip=:zip, city=:city, region=:region, lat=:lat, lng=:lng, profession=:prof, languages=:langs, about=:about, contact_phone=:phone, location_tracking_preference=:locationPref, updated_at=NOW() WHERE user_id=:id');
|
||||||
$stmt?->execute([
|
$stmt?->execute([
|
||||||
'name' => trim((string)$_POST['display_name']),
|
'name' => trim((string)$_POST['display_name']),
|
||||||
'fname' => $firstNameEnc,
|
'fname' => $firstNameEnc,
|
||||||
'lname' => $lastNameEnc,
|
'lname' => $lastNameEnc,
|
||||||
|
'street' => $streetEnc,
|
||||||
'zip' => trim((string)$_POST['zip']),
|
'zip' => trim((string)$_POST['zip']),
|
||||||
'city' => trim((string)$_POST['city']),
|
'city' => trim((string)$_POST['city']),
|
||||||
|
'region' => trim((string)($_POST['region'] ?? '')),
|
||||||
|
'lat' => isset($_POST['lat']) && $_POST['lat'] !== '' ? (float)$_POST['lat'] : null,
|
||||||
|
'lng' => isset($_POST['lng']) && $_POST['lng'] !== '' ? (float)$_POST['lng'] : null,
|
||||||
'prof' => $professionEnc,
|
'prof' => $professionEnc,
|
||||||
'langs' => $languagesEnc,
|
'langs' => $languagesEnc,
|
||||||
'about' => $aboutEnc,
|
'about' => $aboutEnc,
|
||||||
@@ -402,6 +408,10 @@ final class AccountPages
|
|||||||
'last_name' => '',
|
'last_name' => '',
|
||||||
'zip' => '',
|
'zip' => '',
|
||||||
'city' => '',
|
'city' => '',
|
||||||
|
'region' => '',
|
||||||
|
'lat' => null,
|
||||||
|
'lng' => null,
|
||||||
|
'street' => '',
|
||||||
'profession' => '',
|
'profession' => '',
|
||||||
'languages' => '',
|
'languages' => '',
|
||||||
'about' => '',
|
'about' => '',
|
||||||
@@ -427,7 +437,7 @@ final class AccountPages
|
|||||||
static fn(string $column): string => 'p.' . $column,
|
static fn(string $column): string => 'p.' . $column,
|
||||||
AvatarManager::allProfileColumns()
|
AvatarManager::allProfileColumns()
|
||||||
));
|
));
|
||||||
$stmt = $pdo?->prepare("SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.zip, p.city, p.profession, p.languages, p.about, p.contact_phone, p.location_tracking_preference, $avatarColumns FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1");
|
$stmt = $pdo?->prepare("SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.street, p.zip, p.city, p.region, p.lat, p.lng, p.profession, p.languages, p.about, p.contact_phone, p.location_tracking_preference, $avatarColumns FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1");
|
||||||
$stmt?->execute(['id' => $userId]);
|
$stmt?->execute(['id' => $userId]);
|
||||||
$row = $stmt?->fetch(\PDO::FETCH_ASSOC);
|
$row = $stmt?->fetch(\PDO::FETCH_ASSOC);
|
||||||
if ($row) {
|
if ($row) {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ final class ProfileSettings
|
|||||||
$columnDefinitions = [
|
$columnDefinitions = [
|
||||||
'first_name' => 'VARBINARY(512) NULL',
|
'first_name' => 'VARBINARY(512) NULL',
|
||||||
'last_name' => 'VARBINARY(512) NULL',
|
'last_name' => 'VARBINARY(512) NULL',
|
||||||
|
'street' => 'VARBINARY(512) NULL',
|
||||||
'contact_phone' => 'VARBINARY(512) NULL',
|
'contact_phone' => 'VARBINARY(512) NULL',
|
||||||
'contact_email' => 'VARBINARY(512) NULL',
|
'contact_email' => 'VARBINARY(512) NULL',
|
||||||
'profession' => 'VARBINARY(512) NULL',
|
'profession' => 'VARBINARY(512) NULL',
|
||||||
@@ -70,6 +71,11 @@ final class ProfileSettings
|
|||||||
|
|
||||||
foreach ($columnDefinitions as $column => $definition) {
|
foreach ($columnDefinitions as $column => $definition) {
|
||||||
if (!$this->hasColumn('user_profiles', $column)) {
|
if (!$this->hasColumn('user_profiles', $column)) {
|
||||||
|
if ($column === 'street') {
|
||||||
|
$this->pdo->exec('ALTER TABLE user_profiles ADD COLUMN street VARBINARY(512) NULL AFTER last_name');
|
||||||
|
$this->columnCache['user_profiles.street'] = true;
|
||||||
|
$this->columnTypeCache['user_profiles.street'] = 'varbinary';
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user