- calendar.php : ajout DTEND, DESCRIPTION, URL ; dépliage des lignes iCal et désescapage (\n, \,, \;) - index.php : tuiles cliquables avec data-event JSON, modale native (overlay + dialog div), JS vanilla pour ouverture/fermeture (clic extérieur, touche Escape) - style.css : styles modale et hover sur tuiles événements Closes #2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.3 KiB
PHP
67 lines
2.3 KiB
PHP
<?php
|
|
|
|
function calendar_next_events(int $n = 5): array {
|
|
$url = 'https://alpinux.org/public-calendars/n5BWPYsxw7FCYozM';
|
|
$cache = sys_get_temp_dir() . '/alpinux_calendar.ics';
|
|
$ttl = 3600;
|
|
|
|
if (!file_exists($cache) || (time() - filemtime($cache)) >= $ttl) {
|
|
$data = @file_get_contents($url);
|
|
if ($data !== false) file_put_contents($cache, $data);
|
|
}
|
|
|
|
if (!file_exists($cache)) return [];
|
|
|
|
return _ical_upcoming(file_get_contents($cache), $n);
|
|
}
|
|
|
|
function _ical_upcoming(string $ical, int $n): array {
|
|
$events = [];
|
|
$now = time();
|
|
|
|
// Unfold continuation lines before parsing
|
|
$ical = preg_replace('/\r?\n[ \t]/', '', $ical);
|
|
|
|
preg_match_all('/BEGIN:VEVENT(.+?)END:VEVENT/s', $ical, $blocks);
|
|
|
|
foreach ($blocks[1] as $b) {
|
|
$e = [];
|
|
|
|
if (preg_match('/^SUMMARY[^:]*:(.+)$/m', $b, $m))
|
|
$e['title'] = _ical_unescape(trim($m[1]));
|
|
|
|
if (preg_match('/^LOCATION[^:]*:(.+)$/m', $b, $m))
|
|
$e['location'] = _ical_unescape(trim($m[1]));
|
|
|
|
if (preg_match('/^DESCRIPTION[^:]*:(.+)$/m', $b, $m))
|
|
$e['description'] = _ical_unescape(trim($m[1]));
|
|
|
|
if (preg_match('/^URL[^:]*:(.+)$/m', $b, $m))
|
|
$e['url'] = trim($m[1]);
|
|
|
|
// Handles: DTSTART:YYYYMMDD, DTSTART:YYYYMMDDTHHmmss[Z], DTSTART;TZID=...:YYYYMMDDTHHmmss
|
|
foreach (['start' => 'DTSTART', 'end' => 'DTEND'] as $key => $prop) {
|
|
if (preg_match('/^' . $prop . '(?:;[^:]+)?:([\dTZ]+)/m', $b, $m)) {
|
|
$raw = $m[1];
|
|
if (strlen($raw) === 8) {
|
|
$e[$key] = mktime(0, 0, 0, (int)substr($raw, 4, 2), (int)substr($raw, 6, 2), (int)substr($raw, 0, 4));
|
|
} else {
|
|
$dt = DateTime::createFromFormat('Ymd\THis\Z', $raw, new DateTimeZone('UTC'))
|
|
?: DateTime::createFromFormat('Ymd\THis', $raw, new DateTimeZone('Europe/Paris'));
|
|
if ($dt) $e[$key] = $dt->getTimestamp();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($e['start'], $e['title']) && $e['start'] >= $now)
|
|
$events[] = $e;
|
|
}
|
|
|
|
usort($events, fn($a, $b) => $a['start'] - $b['start']);
|
|
|
|
return array_slice($events, 0, $n);
|
|
}
|
|
|
|
function _ical_unescape(string $s): string {
|
|
return str_replace(['\\n', '\\,', '\\;', '\\\\'], ["\n", ',', ';', '\\'], $s);
|
|
}
|