Files
skyview.astronomiemuseum.de/public/header.php
T
2026-04-09 08:56:18 +02:00

406 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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 für 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 &amp; Planeten',
'links' => [
['href' => 'moonphase.php', 'label' => 'Mondphase'],
['href' => 'planetensichtbarkeit.php', 'label' => 'Planetensichtbarkeit'],
['href' => 'astronomical_conversions.php', 'label' => 'Astronomical Conversions'],
['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' => 'satellitenhimmel.php', 'label' => 'Satellitenhimmel'],
['href' => 'meteorshowers.php', 'label' => 'Meteorströme'],
['href' => 'geocron/index.php', 'label' => 'Geocron'],
['href' => 'geocron3d.php', 'label' => 'Geocron 3D'],
['href' => 'geocron3d_C.php', 'label' => 'Geocron 3D Cesium'],
],
],
[
'title' => 'Finsternisse &amp; 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'],
];
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 &amp; 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="Menü öffnen">
<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>