Refactors content structure and adds blueprints

Restructures content files for better organization and consistency.

Moves static content from template files to content files.

Adds blueprints for new pages, including Archive, Datenschutz, DWZ, Error, Impressum, Kontakt, Satzung, Spielbetrieb, Termine, and Verein.

Updates the "termine" snippet to improve date filtering and security, escaping output for safety.
This commit is contained in:
2025-09-06 18:37:29 +02:00
parent 0954f8ddc4
commit 7e541e1244
16 changed files with 331 additions and 190 deletions
+46 -37
View File
@@ -6,13 +6,14 @@ $events = collection('termine');
$events = array_filter($events, function ($event) {
return isset($event['DTSTART']) && !empty($event['DTSTART']);
});
// Nach Datum sortieren
usort($events, function ($a, $b) {
return strcmp($a['DTSTART'], $b['DTSTART']);
});
// Aktuelles Datum
$today = new DateTime()->format('Ymd');
// Aktuelles Datum - PHP 8.3 kompatibel
$today = (new DateTime('now'))->format('Ymd');
// Nur zukünftige Termine anzeigen
$future_events = array_filter($events, function ($event) use ($today) {
@@ -58,9 +59,10 @@ function group_events_by_year_month($events)
$all_events_grouped = group_events_by_year_month($events);
// --- Filter aus URL ---
$filter_jahr = $_GET['jahr'] ?? null;
$filter_monat = $_GET['monat'] ?? null;
// --- Filter aus URL - sichere Eingabe ---
$filter_jahr = filter_input(INPUT_GET, 'jahr', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? null;
$filter_monat = filter_input(INPUT_GET, 'monat', FILTER_SANITIZE_FULL_SPECIAL_CHARS) ?? null;
if ($filter_jahr && $filter_monat) {
$filtered_events = array_filter($events, function ($event) use ($filter_jahr, $filter_monat) {
$date = $event['DTSTART'] ?? '';
@@ -88,34 +90,34 @@ if ($filter_jahr && $filter_monat) {
<?php foreach ($all_events_grouped as $year => $months): ?>
<li class="mb-2">
<div class="flex items-center">
<a href="?jahr=<?php echo $year; ?>"
class="font-bold text-sf_blau-600 focus:outline-none flex items-center group<?php
if ($filter_jahr === $year && !$filter_monat) {
echo ' underline';
}
if ($filter_jahr === $year && !$filter_monat) {
echo ' selected';
}
?>" onclick="event.stopPropagation(); openYear('<?php echo $year; ?>')">
<span><?php echo $year; ?></span>
<?php
$safe_year = htmlspecialchars($year, ENT_QUOTES, 'UTF-8');
$is_year_selected = ($filter_jahr === $year && !$filter_monat);
?>
<a href="?jahr=<?= $safe_year ?>"
class="font-bold text-sf_blau-600 focus:outline-none flex items-center group<?= $is_year_selected ? ' underline selected' : '' ?>"
onclick="event.stopPropagation(); openYear('<?= $safe_year ?>')">
<span><?= $safe_year ?></span>
</a>
<button type="button" class="ml-1 focus:outline-none" onclick="toggleYear('<?php echo $year; ?>')">
<svg class="w-4 h-4 transition-transform" id="arrow-<?php echo $year; ?>" fill="none" stroke="currentColor" stroke-width="2"
<button type="button" class="ml-1 focus:outline-none" onclick="toggleYear('<?= $safe_year ?>')">
<svg class="w-4 h-4 transition-transform" id="arrow-<?= $safe_year ?>" fill="none" stroke="currentColor" stroke-width="2"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
</svg>
</button>
</div>
<ul class="ml-4 mt-1 hidden" id="months-<?php echo $year; ?>">
<ul class="ml-4 mt-1 hidden" id="months-<?= $safe_year ?>">
<?php foreach ($months as $month => $evts): ?>
<li>
<a href="?jahr=<?php echo $year; ?>&monat=<?php echo $month; ?>" class="text-sf_blau-500 hover:underline<?php if (
$filter_jahr === $year &&
$filter_monat === $month
) {
echo ' font-bold underline selected';
} ?>">
<?php echo $de_months[$month]; ?> (<?php echo count($evts); ?>)
<?php
$safe_month = htmlspecialchars($month, ENT_QUOTES, 'UTF-8');
$month_name = $de_months[$month] ?? 'Unbekannt';
$is_month_selected = ($filter_jahr === $year && $filter_monat === $month);
$event_count = count($evts);
?>
<a href="?jahr=<?= $safe_year ?>&monat=<?= $safe_month ?>"
class="text-sf_blau-500 hover:underline<?= $is_month_selected ? ' font-bold underline selected' : '' ?>">
<?= htmlspecialchars($month_name, ENT_QUOTES, 'UTF-8') ?> (<?= $event_count ?>)
</a>
</li>
<?php endforeach; ?>
@@ -170,26 +172,33 @@ if ($filter_jahr && $filter_monat) {
$location = $event['LOCATION'] ?? '';
$desc = $event['DESCRIPTION'] ?? '';
$timezone = $event['DTSTART_TZID'] ?? null;
$date_info = format_ics_date_with_timezone($start, $timezone);
$date = $date_info['display'];
$time = $date_info['has_time'] ? substr($date, 11) : 'ganztägig';
// Sichere Funktionsaufrufe mit Null-Checks
$date_info = function_exists('format_ics_date_with_timezone')
? format_ics_date_with_timezone($start, $timezone)
: ['display' => '', 'has_time' => false, 'iso' => ''];
$date = $date_info['display'] ?? '';
$time = ($date_info['has_time'] ?? false) ? substr($date, 11) : 'ganztägig';
$date = substr($date, 0, 10);
$iso_date = $date_info['iso'];
$iso_date = $date_info['iso'] ?? '';
// Sichere HTML-Ausgabe
$safe_date = htmlspecialchars($date, ENT_QUOTES, 'UTF-8');
$safe_time = htmlspecialchars($time, ENT_QUOTES, 'UTF-8');
$safe_iso_date = htmlspecialchars($iso_date, ENT_QUOTES, 'UTF-8');
$safe_summary = htmlspecialchars($summary, ENT_QUOTES, 'UTF-8');
?>
<tr class="hover:bg-gray-50">
<td class="py-2 px-4 border-b whitespace-nowrap"><?php echo htmlspecialchars(
$date,
); ?></td>
<td class="py-2 px-4 border-b whitespace-nowrap"><?= $safe_date ?></td>
<td class="py-2 px-4 border-b whitespace-nowrap">
<?php if ($date_info['has_time']): ?>
<span class="local-time" data-iso-date="<?php echo htmlspecialchars(
$iso_date,
); ?>"><?php echo htmlspecialchars($time); ?></span>
<?php if ($date_info['has_time'] ?? false): ?>
<span class="local-time" data-iso-date="<?= $safe_iso_date ?>"><?= $safe_time ?></span>
<?php else: ?>
ganztägig
<?php endif; ?>
</td>
<td class="py-2 px-4 border-b"><?php echo htmlspecialchars($summary); ?></td>
<td class="py-2 px-4 border-b"><?= $safe_summary ?></td>
</tr>
<?php endforeach; ?>
</tbody>