first commit

This commit is contained in:
2026-03-31 09:44:27 +02:00
commit 8dd4253ee3
123 changed files with 369814 additions and 0 deletions
+277
View File
@@ -0,0 +1,277 @@
<?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 !== '';
$pdoHeader = isset($pdo) ? $pdo : null;
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
$pendingApprovals = 0;
if (($loggedIn || $isAdmin) && $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]
);
}
// 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 ($isAdmin && $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' => 'index_py.php', 'label' => 'Sonne & Mond'],
['href' => 'planetensichtbarkeit.php', 'label' => 'Planetensichtbarkeit'],
['href' => 'solarsystem.php', 'label' => 'Sonnensystem'],
['href' => 'jupitersystem.php', 'label' => 'Jupitersystem'],
['href' => 'tagbogen.php', 'label' => 'Tagbogen'],
],
],
[
'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'],
];
?>
<!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="css/style.css">
</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 = 'images/star16x16.png';
imgRays.src = 'images/star16x16_rays.png';
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="index.php">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 ($isAdmin && $pendingApprovals > 0): ?>
<a href="admin_users.php" 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>
<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($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>
<div class="menu-bottom">
<div class="menu-group">
<div class="menu-title">Konto</div>
<?php if ($loggedIn): ?>
<a href="settings.php">Einstellungen</a>
<a href="my_favorites.php">Favoriten</a>
<a href="satellitenhimmel.php">Satellitenhimmel</a>
<a href="logout.php" class="nav-logout">Abmelden</a>
<?php else: ?>
<a href="login.php">Anmelden</a>
<a href="register.php">Registrieren</a>
<?php endif; ?>
</div>
<?php if ($isAdmin): ?>
<div class="menu-group">
<div class="menu-title">Administration</div>
<?php foreach ($adminLinks as $adminLink): ?>
<a href="<?= htmlspecialchars($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>