This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
<nav class="site-footer__links" aria-label="Footer">
|
<nav class="site-footer__links" aria-label="Footer">
|
||||||
<a href="/impressum">Impressum</a>
|
<a href="/impressum">Impressum</a>
|
||||||
<a href="/ueber-uns">Über uns</a>
|
<a href="/ueber-uns">Über uns</a>
|
||||||
|
<button class="site-footer__link-button" type="button" data-consent-open>Cookie-Einstellungen</button>
|
||||||
</nav>
|
</nav>
|
||||||
<p class="site-footer__copy">© Copyright <?= date('Y') ?> - Lars Gebhardt-Kusche</p>
|
<p class="site-footer__copy">© Copyright <?= date('Y') ?> - Lars Gebhardt-Kusche</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,8 +70,93 @@ if ($debugEnabled) {
|
|||||||
<?php asset_scripts('header'); ?>
|
<?php asset_scripts('header'); ?>
|
||||||
</head>
|
</head>
|
||||||
<body data-auth="<?= isset($_SESSION['user_id']) ? '1' : '0' ?>" data-child-gender="<?= htmlspecialchars($childGender, ENT_QUOTES) ?>" data-location-preference="<?= htmlspecialchars($locationTrackingPreference, ENT_QUOTES) ?>">
|
<body data-auth="<?= isset($_SESSION['user_id']) ? '1' : '0' ?>" data-child-gender="<?= htmlspecialchars($childGender, ENT_QUOTES) ?>" data-location-preference="<?= htmlspecialchars($locationTrackingPreference, ENT_QUOTES) ?>">
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const storageKey = 'pkt_cookie_consent';
|
||||||
|
const read = () => {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(storageKey);
|
||||||
|
return raw ? JSON.parse(raw) : null;
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.PKTConsent = window.PKTConsent || {
|
||||||
|
storageKey,
|
||||||
|
get() {
|
||||||
|
return read();
|
||||||
|
},
|
||||||
|
has(category) {
|
||||||
|
if (category === 'necessary') return true;
|
||||||
|
const consent = read();
|
||||||
|
return Boolean(consent && consent[category] === true);
|
||||||
|
},
|
||||||
|
openPreferences() {
|
||||||
|
document.dispatchEvent(new CustomEvent('pkt:open-consent'));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<?php tpl('matomo', 'structure'); ?>
|
<?php tpl('matomo', 'structure'); ?>
|
||||||
<?php tpl('nav', 'structure'); ?>
|
<?php tpl('nav', 'structure'); ?>
|
||||||
|
<div class="cookie-consent" id="cookieConsentBanner" hidden>
|
||||||
|
<div class="cookie-consent__inner">
|
||||||
|
<div class="cookie-consent__copy">
|
||||||
|
<strong>Cookies, Analyse und externe Dienste</strong>
|
||||||
|
<p>Notwendige Cookies für Sitzung und Login sind immer aktiv. Analyse mit Matomo sowie Karten-, Standort- und Geocoding-Funktionen werden erst nach deiner Auswahl geladen.</p>
|
||||||
|
</div>
|
||||||
|
<div class="cookie-consent__actions">
|
||||||
|
<button class="btn ghost" type="button" data-consent-necessary>Nur notwendige</button>
|
||||||
|
<button class="btn ghost" type="button" data-consent-open>Auswahl</button>
|
||||||
|
<button class="btn" type="button" data-consent-accept-all>Alle akzeptieren</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal" id="cookieConsentModal">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="head flex between center-y">
|
||||||
|
<h3 style="margin:0;">Cookie- und Diensteinstellungen</h3>
|
||||||
|
<button class="btn ghost" type="button" data-consent-close>✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="stack gap-12" style="margin-top:12px;">
|
||||||
|
<p class="muted" style="margin:0;">Du kannst nicht notwendige Funktionen freiwillig aktivieren oder später wieder widerrufen.</p>
|
||||||
|
<div class="card">
|
||||||
|
<strong>Notwendig</strong>
|
||||||
|
<p class="muted small">Sitzung, Login und Sicherheitsfunktionen. Immer aktiv.</p>
|
||||||
|
</div>
|
||||||
|
<label class="card" style="display:block; cursor:pointer;">
|
||||||
|
<input type="checkbox" id="consentAnalytics" style="margin-right:8px;">
|
||||||
|
<strong>Analyse mit Matomo</strong>
|
||||||
|
<p class="muted small" style="margin:8px 0 0;">Reichweitenmessung und Nutzungsanalyse.</p>
|
||||||
|
</label>
|
||||||
|
<label class="card" style="display:block; cursor:pointer;">
|
||||||
|
<input type="checkbox" id="consentExternalServices" style="margin-right:8px;">
|
||||||
|
<strong>Externe Dienste, Karten und Standort</strong>
|
||||||
|
<p class="muted small" style="margin:8px 0 0;">Browser-Geolocation, lokale Standortspeicherung, Leaflet von `unpkg.com` und Geocoding/Reverse-Geocoding über OpenStreetMap Nominatim.</p>
|
||||||
|
</label>
|
||||||
|
<div class="flex gap-12" style="flex-wrap:wrap;">
|
||||||
|
<button class="btn ghost" type="button" data-consent-necessary>Nur notwendige</button>
|
||||||
|
<button class="btn" type="button" data-consent-save>Auswahl speichern</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal" id="locationPreferenceModal">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="head flex between center-y">
|
||||||
|
<h3 style="margin:0;">Standortfreigabe</h3>
|
||||||
|
<button class="btn ghost" type="button" data-location-pref-close>✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="stack gap-12" style="margin-top:12px;">
|
||||||
|
<p class="muted" style="margin:0;">Damit wir dir passende Treffen in deiner Nähe zeigen können, dürfen wir deinen Standort verwenden. Wie soll das gehandhabt werden?</p>
|
||||||
|
<div class="stack gap-10">
|
||||||
|
<button class="btn" type="button" data-location-pref-choice="session">Nur für diesen Besuch</button>
|
||||||
|
<button class="btn ghost" type="button" data-location-pref-choice="enabled">Immer erlauben</button>
|
||||||
|
<button class="btn ghost" type="button" data-location-pref-choice="disabled">Niemals</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php if ($debugEnabled): ?>
|
<?php if ($debugEnabled): ?>
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,40 +1,15 @@
|
|||||||
<?php if (!defined('MATOMO_SITE_ID')) return; ?>
|
<?php if (!defined('MATOMO_SITE_ID')) return; ?>
|
||||||
<?php if (!defined('MATOMO_ENABLED') || !MATOMO_ENABLED) return; ?>
|
<?php if (!defined('MATOMO_ENABLED') || !MATOMO_ENABLED) return; ?>
|
||||||
<?php
|
<?php
|
||||||
$primaryDomain = app_primary_domain();
|
$primaryDomain = app_primary_domain();
|
||||||
$fakecheckDomain = app_fakecheck_domain();
|
$fakecheckDomain = app_fakecheck_domain();
|
||||||
|
|
||||||
// Build allowed domains for tracking (hosts only, no paths)
|
|
||||||
$matomoDomains = array_values(array_unique(array_filter([
|
$matomoDomains = array_values(array_unique(array_filter([
|
||||||
$primaryDomain ? '*.' . $primaryDomain : null,
|
$primaryDomain ? '*.' . $primaryDomain : null,
|
||||||
($fakecheckDomain && $fakecheckDomain !== $primaryDomain) ? '*.' . $fakecheckDomain : null,
|
($fakecheckDomain && $fakecheckDomain !== $primaryDomain) ? '*.' . $fakecheckDomain : null,
|
||||||
])));
|
])));
|
||||||
?>
|
?>
|
||||||
|
<script type="application/json" id="matomoConfig"><?= json_encode([
|
||||||
<!-- Matomo -->
|
'url' => rtrim(MATOMO_URL, '/') . '/',
|
||||||
<script>
|
'siteId' => (string) MATOMO_SITE_ID,
|
||||||
var _paq = window._paq = window._paq || [];
|
'domains' => $matomoDomains,
|
||||||
_paq.push(["setDomains", <?= json_encode($matomoDomains, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>]);
|
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?></script>
|
||||||
_paq.push(['trackPageView']);
|
|
||||||
_paq.push(['enableLinkTracking']);
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var u = "<?= rtrim(MATOMO_URL, '/') ?>/";
|
|
||||||
_paq.push(['setTrackerUrl', u + 'matomo.php']);
|
|
||||||
_paq.push(['setSiteId', '<?= MATOMO_SITE_ID ?>']);
|
|
||||||
|
|
||||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
|
||||||
g.async=true;
|
|
||||||
g.src=u + 'matomo.js';
|
|
||||||
s.parentNode.insertBefore(g,s);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<noscript>
|
|
||||||
<p>
|
|
||||||
<img referrerpolicy="no-referrer-when-downgrade"
|
|
||||||
src="<?= rtrim(MATOMO_URL,'/') ?>/matomo.php?idsite=<?= MATOMO_SITE_ID ?>&rec=1"
|
|
||||||
style="border:0;" alt="" />
|
|
||||||
</p>
|
|
||||||
</noscript>
|
|
||||||
<!-- End Matomo -->
|
|
||||||
|
|||||||
@@ -137,7 +137,14 @@ body {
|
|||||||
.site-footer__inner { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 24px 16px 30px; }
|
.site-footer__inner { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 24px 16px 30px; }
|
||||||
.site-footer__links { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; font-weight: 600; }
|
.site-footer__links { display: flex; gap: 18px; flex-wrap: wrap; justify-content: center; font-weight: 600; }
|
||||||
.site-footer__links a:hover { color: var(--color-primary); }
|
.site-footer__links a:hover { color: var(--color-primary); }
|
||||||
|
.site-footer__link-button { border: 0; background: transparent; padding: 0; font: inherit; color: var(--color-text); cursor: pointer; }
|
||||||
|
.site-footer__link-button:hover { color: var(--color-primary); }
|
||||||
.site-footer__copy { margin: 0; color: var(--color-muted); font-size: 14px; text-align: center; }
|
.site-footer__copy { margin: 0; color: var(--color-muted); font-size: 14px; text-align: center; }
|
||||||
|
.cookie-consent { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 220; }
|
||||||
|
.cookie-consent[hidden] { display: none; }
|
||||||
|
.cookie-consent__inner { max-width: 1120px; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 18px; align-items: center; padding: 18px 20px; background: rgba(255,255,255,0.98); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: 0 18px 40px rgba(0,0,0,0.16); backdrop-filter: blur(10px); }
|
||||||
|
.cookie-consent__copy p { margin: 6px 0 0; color: var(--color-muted); }
|
||||||
|
.cookie-consent__actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
|
||||||
.legal-page { min-height: 40vh; }
|
.legal-page { min-height: 40vh; }
|
||||||
.content-card { background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 24px; box-shadow: var(--shadow-card); }
|
.content-card { background: #fff; border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 24px; box-shadow: var(--shadow-card); }
|
||||||
.content-card--narrow { max-width: 860px; margin: 0 auto; }
|
.content-card--narrow { max-width: 860px; margin: 0 auto; }
|
||||||
@@ -279,6 +286,8 @@ body {
|
|||||||
.forum-admin-item { padding-left: 16px; padding-right: 16px; }
|
.forum-admin-item { padding-left: 16px; padding-right: 16px; }
|
||||||
.forum-post__body,
|
.forum-post__body,
|
||||||
.forum-reply-form { padding: 18px 16px; }
|
.forum-reply-form { padding: 18px 16px; }
|
||||||
|
.cookie-consent__inner { grid-template-columns: 1fr; }
|
||||||
|
.cookie-consent__actions { justify-content: stretch; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auth & Dashboard */
|
/* Auth & Dashboard */
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const threads = Array.isArray(window.__threads) ? window.__threads : [];
|
const threads = Array.isArray(window.__threads) ? window.__threads : [];
|
||||||
const EVENTS_RADIUS_KM = 20;
|
const EVENTS_RADIUS_KM = 20;
|
||||||
const LOCATION_STORAGE_KEY = 'pkt_user_location';
|
const LOCATION_STORAGE_KEY = 'pkt_user_location';
|
||||||
|
const LOCATION_SESSION_STORAGE_KEY = 'pkt_user_location_session';
|
||||||
|
const LOCATION_SESSION_PREFERENCE_KEY = 'pkt_user_location_session_preference';
|
||||||
|
|
||||||
const el = {
|
const el = {
|
||||||
sliderTrack: document.getElementById('eventSlider'),
|
sliderTrack: document.getElementById('eventSlider'),
|
||||||
@@ -75,7 +77,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
quickLng: document.getElementById('qsLng'),
|
quickLng: document.getElementById('qsLng'),
|
||||||
quickGeo: document.getElementById('quickGeo'),
|
quickGeo: document.getElementById('quickGeo'),
|
||||||
eventsSection: document.getElementById('events'),
|
eventsSection: document.getElementById('events'),
|
||||||
|
locationPreferenceModal: document.getElementById('locationPreferenceModal'),
|
||||||
};
|
};
|
||||||
|
let effectiveLocationPreference = locationPreference;
|
||||||
|
|
||||||
const fmtDate = (iso) => {
|
const fmtDate = (iso) => {
|
||||||
const d = new Date(iso);
|
const d = new Date(iso);
|
||||||
@@ -98,7 +102,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
?.slice(prefix.length) || '';
|
?.slice(prefix.length) || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistLocation = (location) => {
|
const persistLocation = (location, mode = 'persistent') => {
|
||||||
if (!location || !Number.isFinite(location.lat) || !Number.isFinite(location.lng)) return;
|
if (!location || !Number.isFinite(location.lat) || !Number.isFinite(location.lng)) return;
|
||||||
const payload = {
|
const payload = {
|
||||||
lat: Number(location.lat.toFixed(6)),
|
lat: Number(location.lat.toFixed(6)),
|
||||||
@@ -106,12 +110,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
label: location.label || 'Mein Standort',
|
label: location.label || 'Mein Standort',
|
||||||
savedAt: new Date().toISOString(),
|
savedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
try {
|
if (mode === 'session') {
|
||||||
window.localStorage.setItem(LOCATION_STORAGE_KEY, JSON.stringify(payload));
|
try {
|
||||||
} catch (err) {
|
window.sessionStorage.setItem(LOCATION_SESSION_STORAGE_KEY, JSON.stringify(payload));
|
||||||
// ignore
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(LOCATION_STORAGE_KEY, JSON.stringify(payload));
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
setCookie('pkt_user_location', JSON.stringify(payload), 30);
|
||||||
}
|
}
|
||||||
setCookie('pkt_user_location', JSON.stringify(payload), 30);
|
|
||||||
if (el.quickLat) el.quickLat.value = payload.lat.toFixed(6);
|
if (el.quickLat) el.quickLat.value = payload.lat.toFixed(6);
|
||||||
if (el.quickLng) el.quickLng.value = payload.lng.toFixed(6);
|
if (el.quickLng) el.quickLng.value = payload.lng.toFixed(6);
|
||||||
if (el.quickLoc && (!el.quickLoc.value || el.quickLoc.value === 'Mein Standort')) {
|
if (el.quickLoc && (!el.quickLoc.value || el.quickLoc.value === 'Mein Standort')) {
|
||||||
@@ -125,6 +137,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
window.sessionStorage.removeItem(LOCATION_SESSION_STORAGE_KEY);
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
document.cookie = 'pkt_user_location=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; SameSite=Lax';
|
document.cookie = 'pkt_user_location=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; SameSite=Lax';
|
||||||
if (el.quickLat) el.quickLat.value = '';
|
if (el.quickLat) el.quickLat.value = '';
|
||||||
if (el.quickLng) el.quickLng.value = '';
|
if (el.quickLng) el.quickLng.value = '';
|
||||||
@@ -133,6 +150,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
const getStoredLocation = () => {
|
const getStoredLocation = () => {
|
||||||
const candidates = [];
|
const candidates = [];
|
||||||
|
try {
|
||||||
|
const session = window.sessionStorage.getItem(LOCATION_SESSION_STORAGE_KEY);
|
||||||
|
if (session) candidates.push(session);
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const local = window.localStorage.getItem(LOCATION_STORAGE_KEY);
|
const local = window.localStorage.getItem(LOCATION_STORAGE_KEY);
|
||||||
if (local) candidates.push(local);
|
if (local) candidates.push(local);
|
||||||
@@ -333,7 +356,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
lng: pos.coords.longitude,
|
lng: pos.coords.longitude,
|
||||||
label: 'Mein Standort',
|
label: 'Mein Standort',
|
||||||
};
|
};
|
||||||
persistLocation(location);
|
persistLocation(location, options.persistMode || 'persistent');
|
||||||
resolve(location);
|
resolve(location);
|
||||||
},
|
},
|
||||||
() => resolve(null),
|
() => resolve(null),
|
||||||
@@ -345,19 +368,133 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const closeLocationPreferenceModal = () => {
|
||||||
|
el.locationPreferenceModal?.classList.remove('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-location-pref-close]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', closeLocationPreferenceModal);
|
||||||
|
});
|
||||||
|
el.locationPreferenceModal?.addEventListener('click', (event) => {
|
||||||
|
if (event.target === el.locationPreferenceModal) {
|
||||||
|
closeLocationPreferenceModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveLocationPreference = async (preference) => {
|
||||||
|
effectiveLocationPreference = preference;
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem('pkt_location_preference_guest', preference);
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const body = new URLSearchParams({ preference });
|
||||||
|
const response = await fetch('/api/location-preference', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
},
|
||||||
|
body: body.toString(),
|
||||||
|
});
|
||||||
|
return response.ok;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSessionPreference = () => {
|
||||||
|
try {
|
||||||
|
return window.sessionStorage.getItem(LOCATION_SESSION_PREFERENCE_KEY);
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setSessionPreference = (value) => {
|
||||||
|
try {
|
||||||
|
if (!value) {
|
||||||
|
window.sessionStorage.removeItem(LOCATION_SESSION_PREFERENCE_KEY);
|
||||||
|
} else {
|
||||||
|
window.sessionStorage.setItem(LOCATION_SESSION_PREFERENCE_KEY, value);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolvePromptChoice = () => new Promise((resolve) => {
|
||||||
|
if (!el.locationPreferenceModal) {
|
||||||
|
resolve('disabled');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttons = el.locationPreferenceModal.querySelectorAll('[data-location-pref-choice]');
|
||||||
|
const cleanup = () => {
|
||||||
|
buttons.forEach(button => button.removeEventListener('click', handleClick));
|
||||||
|
document.removeEventListener('keydown', handleKeydown);
|
||||||
|
};
|
||||||
|
const finish = (value) => {
|
||||||
|
cleanup();
|
||||||
|
closeLocationPreferenceModal();
|
||||||
|
resolve(value);
|
||||||
|
};
|
||||||
|
const handleClick = (event) => {
|
||||||
|
const value = event.currentTarget.getAttribute('data-location-pref-choice') || 'disabled';
|
||||||
|
finish(value);
|
||||||
|
};
|
||||||
|
const handleKeydown = (event) => {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
finish('disabled');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
buttons.forEach(button => button.addEventListener('click', handleClick));
|
||||||
|
document.addEventListener('keydown', handleKeydown);
|
||||||
|
el.locationPreferenceModal.classList.add('open');
|
||||||
|
});
|
||||||
|
|
||||||
const ensureEventsLocation = async () => {
|
const ensureEventsLocation = async () => {
|
||||||
if (locationPreference === 'disabled') {
|
const sessionPreference = getSessionPreference();
|
||||||
|
if (effectiveLocationPreference === 'disabled') {
|
||||||
clearStoredLocation();
|
clearStoredLocation();
|
||||||
renderSlider(null);
|
renderSlider(null);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stored = applyStoredLocationToForm();
|
const stored = applyStoredLocationToForm();
|
||||||
if (stored && locationPreference !== 'enabled') {
|
if (stored && effectiveLocationPreference !== 'enabled' && sessionPreference !== 'session') {
|
||||||
renderSlider(stored);
|
renderSlider(stored);
|
||||||
return stored;
|
return stored;
|
||||||
}
|
}
|
||||||
const location = await requestLocation();
|
|
||||||
|
if (effectiveLocationPreference === 'prompt') {
|
||||||
|
const choice = sessionPreference || await resolvePromptChoice();
|
||||||
|
if (choice === 'session') {
|
||||||
|
setSessionPreference('session');
|
||||||
|
const location = await requestLocation({ persistMode: 'session' });
|
||||||
|
renderSlider(location || stored);
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
if (choice === 'disabled') {
|
||||||
|
await saveLocationPreference('disabled');
|
||||||
|
clearStoredLocation();
|
||||||
|
renderSlider(null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (choice === 'enabled') {
|
||||||
|
await saveLocationPreference('enabled');
|
||||||
|
effectiveLocationPreference = 'enabled';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const location = await requestLocation({
|
||||||
|
persistMode: effectiveLocationPreference === 'enabled' ? 'persistent' : 'session',
|
||||||
|
});
|
||||||
renderSlider(location || stored);
|
renderSlider(location || stored);
|
||||||
return location;
|
return location;
|
||||||
};
|
};
|
||||||
@@ -384,7 +521,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
lat: pos.coords.latitude,
|
lat: pos.coords.latitude,
|
||||||
lng: pos.coords.longitude,
|
lng: pos.coords.longitude,
|
||||||
label: 'Mein Standort',
|
label: 'Mein Standort',
|
||||||
});
|
}, effectiveLocationPreference === 'enabled' ? 'persistent' : 'session');
|
||||||
renderSlider(getStoredLocation());
|
renderSlider(getStoredLocation());
|
||||||
},
|
},
|
||||||
() => alert('Standort konnte nicht ermittelt werden.')
|
() => alert('Standort konnte nicht ermittelt werden.')
|
||||||
@@ -426,14 +563,25 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialLocation = locationPreference === 'disabled' ? null : applyStoredLocationToForm();
|
try {
|
||||||
if (locationPreference === 'disabled') {
|
if (!isLoggedIn) {
|
||||||
|
const guestPreference = window.localStorage.getItem('pkt_location_preference_guest');
|
||||||
|
if (guestPreference && ['disabled', 'prompt', 'enabled'].includes(guestPreference)) {
|
||||||
|
effectiveLocationPreference = guestPreference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialLocation = effectiveLocationPreference === 'disabled' ? null : applyStoredLocationToForm();
|
||||||
|
if (effectiveLocationPreference === 'disabled') {
|
||||||
clearStoredLocation();
|
clearStoredLocation();
|
||||||
}
|
}
|
||||||
renderSlider(initialLocation);
|
renderSlider(initialLocation);
|
||||||
renderThreadSlider();
|
renderThreadSlider();
|
||||||
|
|
||||||
if (locationPreference === 'enabled' && window.location.pathname === '/') {
|
if (effectiveLocationPreference === 'enabled' && window.location.pathname === '/') {
|
||||||
ensureEventsLocation();
|
ensureEventsLocation();
|
||||||
} else if (window.location.hash === '#events' || window.location.hash === '#quicksearch') {
|
} else if (window.location.hash === '#events' || window.location.hash === '#quicksearch') {
|
||||||
ensureEventsLocation();
|
ensureEventsLocation();
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ $targetReal = realpath($target);
|
|||||||
if ($targetReal && str_starts_with($targetReal, realpath(__DIR__ . '/page/retool'))) {
|
if ($targetReal && str_starts_with($targetReal, realpath(__DIR__ . '/page/retool'))) {
|
||||||
$skipLayout = true;
|
$skipLayout = true;
|
||||||
}
|
}
|
||||||
|
if ($targetReal && str_starts_with($targetReal, realpath(__DIR__ . '/page/api'))) {
|
||||||
|
$skipLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// Ausgabe
|
// Ausgabe
|
||||||
|
|||||||
38
public/page/api/location-preference.php
Normal file
38
public/page/api/location-preference.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'method_not_allowed']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'unauthorized']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$preference = (string)($_POST['preference'] ?? '');
|
||||||
|
if (!in_array($preference, ['disabled', 'prompt', 'enabled'], true)) {
|
||||||
|
http_response_code(422);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'invalid_preference']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = app()->pdo();
|
||||||
|
if (!$pdo) {
|
||||||
|
throw new RuntimeException('db_unavailable');
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = new \App\ProfileSettings($pdo);
|
||||||
|
$settings->updateLocationTrackingPreference((int)$_SESSION['user_id'], $preference);
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true, 'preference' => $preference]);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['ok' => false, 'error' => 'save_failed']);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user