404 lines
16 KiB
PHP
404 lines
16 KiB
PHP
<?php
|
||
require_once __DIR__ . '/auth_helpers.php';
|
||
$title = isset($pageTitle) && is_string($pageTitle) && $pageTitle !== ''
|
||
? $pageTitle
|
||
: 'Skyview & AstroTools — Astronomiemuseum der Sternwarte Sonneberg';
|
||
|
||
$bodyClassAttr = '';
|
||
if (isset($bodyClass) && is_string($bodyClass) && trim($bodyClass) !== '') {
|
||
$bodyClassAttr = ' class="' . htmlspecialchars(trim($bodyClass), ENT_QUOTES, 'UTF-8') . '"';
|
||
}
|
||
|
||
$headerIntroPre = isset($headerIntroPre) && is_string($headerIntroPre) ? trim($headerIntroPre) : '';
|
||
$headerIntroTitle = isset($headerIntroTitle) && is_string($headerIntroTitle) ? trim($headerIntroTitle) : '';
|
||
$headerIntroSub = isset($headerIntroSub) && is_string($headerIntroSub) ? trim($headerIntroSub) : '';
|
||
$showHeaderIntro = $headerIntroPre !== '' || $headerIntroTitle !== '' || $headerIntroSub !== '';
|
||
$publicBasePath = isset($publicBasePath) && is_string($publicBasePath) ? rtrim($publicBasePath, '/') . '/' : '';
|
||
$pdoHeader = isset($pdo) ? $pdo : null;
|
||
$hasFullAdminAccess = $loggedIn && currentUserIsAdminLike();
|
||
$canAccessCurrentPage = $hasFullAdminAccess;
|
||
$pendingApprovals = 0;
|
||
$publicScriptName = currentPublicPageKey();
|
||
$publicAccessAllowlist = publicAccessAllowlist();
|
||
$manageablePublicPages = manageablePublicPages();
|
||
|
||
if ($loggedIn && $pdoHeader === null) {
|
||
$cfgHeader = require __DIR__ . '/../config/database.php';
|
||
$pdoHeader = new PDO(
|
||
sprintf('mysql:host=%s;dbname=%s;charset=%s', $cfgHeader['host'], $cfgHeader['dbname'], $cfgHeader['charset'] ?? 'utf8mb4'),
|
||
$cfgHeader['user'],
|
||
$cfgHeader['pass'],
|
||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
|
||
);
|
||
}
|
||
|
||
if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCurrentPage) {
|
||
if (!$loggedIn) {
|
||
header('Location: ' . $publicBasePath . 'login.php');
|
||
exit;
|
||
}
|
||
|
||
$userHasPagePermission = false;
|
||
if ($pdoHeader !== null && isset($manageablePublicPages[$publicScriptName], $_SESSION['user_id'])) {
|
||
try {
|
||
$stmtPagePermission = $pdoHeader->prepare("
|
||
SELECT 1
|
||
FROM `app_user_page_permissions`
|
||
WHERE `user_id` = :user_id
|
||
AND `page_key` = :page_key
|
||
LIMIT 1
|
||
");
|
||
$stmtPagePermission->execute([
|
||
':user_id' => (int) $_SESSION['user_id'],
|
||
':page_key' => $publicScriptName,
|
||
]);
|
||
$userHasPagePermission = (bool) $stmtPagePermission->fetchColumn();
|
||
} catch (Throwable $e) {
|
||
$userHasPagePermission = false;
|
||
}
|
||
}
|
||
|
||
if ($userHasPagePermission) {
|
||
$canAccessCurrentPage = true;
|
||
}
|
||
}
|
||
|
||
if (!in_array($publicScriptName, $publicAccessAllowlist, true) && !$canAccessCurrentPage) {
|
||
if (!$loggedIn) {
|
||
header('Location: ' . $publicBasePath . 'login.php');
|
||
exit;
|
||
}
|
||
|
||
http_response_code(403);
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Zugriff verweigert</title>
|
||
<link rel="stylesheet" href="<?= htmlspecialchars($publicBasePath . 'css/style.css', ENT_QUOTES, 'UTF-8') ?>">
|
||
</head>
|
||
<body>
|
||
<main class="container" style="padding-top: 4rem; padding-bottom: 4rem;">
|
||
<section class="card">
|
||
<h1>Zugriff verweigert</h1>
|
||
<p>Diese Seite ist aktuell nur fuer Benutzer mit der Rolle <strong>admin</strong> oder <strong>master</strong> freigegeben.</p>
|
||
<p><a class="btn btn-primary" href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>">Abmelden</a></p>
|
||
</section>
|
||
</main>
|
||
</body>
|
||
</html>
|
||
<?php
|
||
exit;
|
||
}
|
||
// Standard-Standort des eingeloggten Benutzers laden
|
||
$currentLocation = null;
|
||
if (!empty($defaultLocation)) {
|
||
// Seite hat den Standort bereits geladen – direkt übernehmen
|
||
$currentLocation = $defaultLocation;
|
||
} elseif ($loggedIn && isset($_SESSION['user_id'])) {
|
||
// Eigene DB-Verbindung aufbauen falls die Seite keine bereitstellt
|
||
$stmtLoc = $pdoHeader->prepare(
|
||
'SELECT id, name, latitude, longitude, elevation, timezone
|
||
FROM app_user_locations
|
||
WHERE user_id = ? AND is_default = 1
|
||
LIMIT 1'
|
||
);
|
||
$stmtLoc->execute([(int)$_SESSION['user_id']]);
|
||
$currentLocation = $stmtLoc->fetch() ?: null;
|
||
}
|
||
|
||
if ($hasFullAdminAccess && $pdoHeader !== null) {
|
||
$stmtPendingUsers = $pdoHeader->query('SELECT COUNT(*) FROM app_users WHERE is_active = 0');
|
||
$pendingApprovals = (int)$stmtPendingUsers->fetchColumn();
|
||
}
|
||
|
||
$menuGroups = [
|
||
[
|
||
'title' => 'Himmel & Planeten',
|
||
'links' => [
|
||
['href' => 'moonphase.php', 'label' => 'Mondphase'],
|
||
['href' => 'planetensichtbarkeit.php', 'label' => 'Planetensichtbarkeit'],
|
||
['href' => 'tcrb_lightcurve.php', 'label' => 'T CrB Lichtkurve'],
|
||
['href' => 'solarsystem.php', 'label' => 'Sonnensystem'],
|
||
['href' => 'jupitersystem.php', 'label' => 'Jupitersystem'],
|
||
['href' => 'tagbogen.php', 'label' => 'Tagbogen'],
|
||
['href' => 'sternenhimmel.php', 'label' => 'Sternenhimmel'],
|
||
['href' => 'meteorshowers.php', 'label' => 'Meteorstroeme'],
|
||
['href' => 'geocron/index.php', 'label' => 'Geocron'],
|
||
['href' => 'geocron3d.php', 'label' => 'Geocron 3D'],
|
||
],
|
||
],
|
||
[
|
||
'title' => 'Finsternisse & Bedeckungen',
|
||
'links' => [
|
||
['href' => 'mondfinsternis.php', 'label' => 'Mondfinsternis'],
|
||
['href' => 'sonnenfinsternis.php', 'label' => 'Sonnenfinsternis'],
|
||
['href' => 'bedeckung.php', 'label' => 'Bedeckungsphänomene'],
|
||
['href' => 'sternbedeckungen.php', 'label' => 'Mond-Stern-Bedeckungen'],
|
||
],
|
||
],
|
||
];
|
||
|
||
$adminLinks = [
|
||
['href' => 'admin_users.php', 'label' => 'Benutzerverwaltung'],
|
||
['href' => 'monatsvorhersage.php', 'label' => 'Monatsvorhersage'],
|
||
['href' => 'telegram/broadcast_test.php', 'label' => 'Telegram-Test'],
|
||
];
|
||
$accountLinks = [
|
||
['href' => 'settings.php', 'label' => 'Einstellungen'],
|
||
['href' => 'my_favorites.php', 'label' => 'Favoriten'],
|
||
['href' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
|
||
];
|
||
|
||
if ($loggedIn && !$hasFullAdminAccess && $pdoHeader !== null) {
|
||
$allowedPageKeys = [];
|
||
try {
|
||
$stmtAllowedPages = $pdoHeader->prepare("
|
||
SELECT `page_key`
|
||
FROM `app_user_page_permissions`
|
||
WHERE `user_id` = :user_id
|
||
");
|
||
$stmtAllowedPages->execute([
|
||
':user_id' => (int) ($_SESSION['user_id'] ?? 0),
|
||
]);
|
||
foreach ($stmtAllowedPages as $allowedPageRow) {
|
||
$pageKey = trim((string) ($allowedPageRow['page_key'] ?? ''));
|
||
if ($pageKey !== '') {
|
||
$allowedPageKeys[$pageKey] = true;
|
||
}
|
||
}
|
||
} catch (Throwable $e) {
|
||
$allowedPageKeys = [];
|
||
}
|
||
|
||
foreach ($menuGroups as &$group) {
|
||
$group['links'] = array_values(array_filter(
|
||
$group['links'],
|
||
static function (array $link) use ($allowedPageKeys): bool {
|
||
$href = trim((string) ($link['href'] ?? ''));
|
||
return $href !== '' && isset($allowedPageKeys[$href]);
|
||
}
|
||
));
|
||
}
|
||
unset($group);
|
||
|
||
$menuGroups = array_values(array_filter(
|
||
$menuGroups,
|
||
static fn (array $group): bool => !empty($group['links'])
|
||
));
|
||
|
||
$accountLinks = array_values(array_filter(
|
||
$accountLinks,
|
||
static function (array $link) use ($allowedPageKeys): bool {
|
||
$href = trim((string) ($link['href'] ?? ''));
|
||
return $href !== '' && isset($allowedPageKeys[$href]);
|
||
}
|
||
));
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title><?= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') ?></title>
|
||
<link rel="stylesheet" href="<?= htmlspecialchars($publicBasePath . 'css/style.css', ENT_QUOTES, 'UTF-8') ?>">
|
||
</head>
|
||
<body<?= $bodyClassAttr ?>>
|
||
|
||
<!-- Sternenhimmel -->
|
||
<canvas id="starfield"></canvas>
|
||
<script>
|
||
(function () {
|
||
const canvas = document.getElementById('starfield');
|
||
const ctx = canvas.getContext('2d');
|
||
let stars = [];
|
||
|
||
// Bilder vorladen
|
||
const imgRound = new Image();
|
||
const imgRays = new Image();
|
||
imgRound.src = <?= json_encode($publicBasePath . 'images/star16x16.png', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||
imgRays.src = <?= json_encode($publicBasePath . 'images/star16x16_rays.png', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||
|
||
function resize() {
|
||
canvas.width = window.innerWidth;
|
||
canvas.height = window.innerHeight;
|
||
}
|
||
|
||
function generateStars() {
|
||
stars = [];
|
||
const count = Math.floor((canvas.width * canvas.height) / 2800);
|
||
for (let i = 0; i < count; i++) {
|
||
const r = Math.random();
|
||
// Klassen: 80% klein-rund, 17% mittel-rund, 3% mit Strahlen
|
||
const hasRays = r > 0.97;
|
||
const size = hasRays ? (Math.random() * 6 + 8) // 8–14 px
|
||
: r > 0.80 ? (Math.random() * 4 + 5) // 5–9 px
|
||
: (Math.random() * 3 + 2); // 2–5 px
|
||
stars.push({
|
||
x: Math.random() * canvas.width,
|
||
y: Math.random() * canvas.height,
|
||
size,
|
||
img: hasRays ? imgRays : imgRound,
|
||
base: hasRays ? Math.random() * 0.20 + 0.12
|
||
: Math.random() * 0.18 + 0.04,
|
||
amp: Math.random() * 0.08 + 0.01,
|
||
phase: Math.random() * Math.PI * 2,
|
||
speed: Math.random() * 0.0003 + 0.00008,
|
||
});
|
||
}
|
||
}
|
||
|
||
let t = 0;
|
||
function draw() {
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
t++;
|
||
for (const s of stars) {
|
||
const a = s.base + s.amp * Math.sin(s.phase + t * s.speed * Math.PI * 2);
|
||
ctx.globalAlpha = a;
|
||
ctx.drawImage(s.img, s.x - s.size / 2, s.y - s.size / 2, s.size, s.size);
|
||
}
|
||
ctx.globalAlpha = 1;
|
||
requestAnimationFrame(draw);
|
||
}
|
||
|
||
// Erst zeichnen wenn beide Bilder geladen sind
|
||
let loaded = 0;
|
||
function onLoad() { if (++loaded === 2) { resize(); generateStars(); draw(); } }
|
||
imgRound.onload = onLoad;
|
||
imgRays.onload = onLoad;
|
||
|
||
window.addEventListener('resize', () => { resize(); generateStars(); });
|
||
})();
|
||
</script>
|
||
|
||
<header>
|
||
<div class="container">
|
||
<div class="header-inner">
|
||
<div class="header-brand">
|
||
<a href="<?= htmlspecialchars($publicBasePath . 'index.php', ENT_QUOTES, 'UTF-8') ?>">Skyview & AstroTools</a>
|
||
<span class="header-sub"><a href="https://www.astronomiemuseum.de" target="_blank" rel="noopener">Astronomiemuseum der Sternwarte Sonneberg</a></span>
|
||
</div>
|
||
<?php if ($hasFullAdminAccess && $pendingApprovals > 0): ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . 'admin_users.php', ENT_QUOTES, 'UTF-8') ?>" class="admin-alert" aria-live="polite">
|
||
<span class="admin-alert-icon" aria-hidden="true">!</span>
|
||
<span class="admin-alert-text">Achtung: <?= $pendingApprovals ?> <?= $pendingApprovals === 1 ? 'Benutzer wartet' : 'Benutzer warten' ?> auf Freischaltung</span>
|
||
</a>
|
||
<?php endif; ?>
|
||
<div class="header-menu">
|
||
<button type="button" class="menu-toggle" id="menuToggle" aria-expanded="false" aria-controls="headerMenuPanel" aria-label="Menue oeffnen">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</button>
|
||
<nav class="menu-panel" id="headerMenuPanel" hidden>
|
||
<?php if ($loggedIn): ?>
|
||
<div class="menu-cols">
|
||
<?php foreach ($menuGroups as $group): ?>
|
||
<div class="menu-group">
|
||
<div class="menu-title"><?= $group['title'] ?></div>
|
||
<?php foreach ($group['links'] as $link): ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . $link['href'], ENT_QUOTES, 'UTF-8') ?>">
|
||
<?= htmlspecialchars($link['label'], ENT_QUOTES, 'UTF-8') ?>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<div class="menu-divider"></div>
|
||
<?php endif; ?>
|
||
<div class="menu-bottom">
|
||
<div class="menu-group">
|
||
<div class="menu-title">Konto</div>
|
||
<?php if ($loggedIn): ?>
|
||
<?php foreach ($accountLinks as $accountLink): ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . $accountLink['href'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($accountLink['label'], ENT_QUOTES, 'UTF-8') ?></a>
|
||
<?php endforeach; ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . 'logout.php', ENT_QUOTES, 'UTF-8') ?>" class="nav-logout">Abmelden</a>
|
||
<?php else: ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . 'login.php', ENT_QUOTES, 'UTF-8') ?>">Anmelden</a>
|
||
<a href="<?= htmlspecialchars($publicBasePath . 'register.php', ENT_QUOTES, 'UTF-8') ?>">Registrieren</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php if ($hasFullAdminAccess): ?>
|
||
<div class="menu-group">
|
||
<div class="menu-title">Administration</div>
|
||
<?php foreach ($adminLinks as $adminLink): ?>
|
||
<a href="<?= htmlspecialchars($publicBasePath . $adminLink['href'], ENT_QUOTES, 'UTF-8') ?>">
|
||
<?= htmlspecialchars($adminLink['label'], ENT_QUOTES, 'UTF-8') ?>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
<?php if ($showHeaderIntro): ?>
|
||
<div class="page-header-intro">
|
||
<?php if ($headerIntroPre !== ''): ?>
|
||
<div class="page-header-pre"><?= htmlspecialchars($headerIntroPre, ENT_QUOTES, 'UTF-8') ?></div>
|
||
<?php endif; ?>
|
||
<?php if ($headerIntroTitle !== ''): ?>
|
||
<h1 class="page-header-title"><?= htmlspecialchars($headerIntroTitle, ENT_QUOTES, 'UTF-8') ?></h1>
|
||
<?php endif; ?>
|
||
<?php if ($headerIntroSub !== ''): ?>
|
||
<div class="page-header-sub"><?= htmlspecialchars($headerIntroSub, ENT_QUOTES, 'UTF-8') ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</header>
|
||
|
||
<main>
|
||
<div class="container">
|
||
<script>
|
||
(function () {
|
||
const toggle = document.getElementById('menuToggle');
|
||
const panel = document.getElementById('headerMenuPanel');
|
||
|
||
if (!toggle || !panel) {
|
||
return;
|
||
}
|
||
|
||
function closeMenu() {
|
||
toggle.setAttribute('aria-expanded', 'false');
|
||
panel.hidden = true;
|
||
document.body.classList.remove('menu-open');
|
||
}
|
||
|
||
function openMenu() {
|
||
toggle.setAttribute('aria-expanded', 'true');
|
||
panel.hidden = false;
|
||
document.body.classList.add('menu-open');
|
||
}
|
||
|
||
toggle.addEventListener('click', function () {
|
||
if (toggle.getAttribute('aria-expanded') === 'true') {
|
||
closeMenu();
|
||
return;
|
||
}
|
||
|
||
openMenu();
|
||
});
|
||
|
||
document.addEventListener('click', function (event) {
|
||
if (panel.hidden) {
|
||
return;
|
||
}
|
||
|
||
if (!panel.contains(event.target) && !toggle.contains(event.target)) {
|
||
closeMenu();
|
||
}
|
||
});
|
||
|
||
document.addEventListener('keydown', function (event) {
|
||
if (event.key === 'Escape') {
|
||
closeMenu();
|
||
}
|
||
});
|
||
})();
|
||
</script>
|