prepare('SELECT feed_token FROM sufw_booking_project_settings WHERE project = :project'); $settings->execute(['project' => $project]); if (!hash_equals((string) $settings->fetchColumn(), $token)) { http_response_code(404); exit; } $events = $database->prepare(<<<'SQL' SELECT e.id, e.title, e.starts_at, e.ends_at, e.format FROM sufw_booking_events e WHERE e.project = :project AND EXISTS (SELECT 1 FROM sufw_booking_registrations r WHERE r.event_id = e.id AND r.status = 'confirmed') ORDER BY e.starts_at SQL); $events->execute(['project' => $project]); $timezone = new DateTimeZone('Europe/Berlin'); $utc = new DateTimeZone('UTC'); $lines = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//SUFW//Terminbuchung//DE', 'CALSCALE:GREGORIAN', 'X-WR-CALNAME:'.bookingIcsEscape(bookingProjectTitle($project))]; foreach ($events as $event) { $start = new DateTimeImmutable($event['starts_at'], $timezone); $end = new DateTimeImmutable($event['ends_at'], $timezone); $lines[] = 'BEGIN:VEVENT'; $lines[] = 'UID:'.bookingIcsEscape($event['id']).'@sufw-booking'; $lines[] = 'DTSTAMP:'.gmdate('Ymd\\THis\\Z'); $lines[] = 'DTSTART:'.$start->setTimezone($utc)->format('Ymd\\THis\\Z'); $lines[] = 'DTEND:'.$end->setTimezone($utc)->format('Ymd\\THis\\Z'); $lines[] = 'SUMMARY:'.bookingIcsEscape($event['title']); $lines[] = 'DESCRIPTION:'.bookingIcsEscape($event['format'] === 'telefonisch' ? 'Telefonischer Termin' : 'Termin vor Ort'); $lines[] = 'END:VEVENT'; } $lines[] = 'END:VCALENDAR'; header('Content-Type: text/calendar; charset=utf-8'); header('Content-Disposition: inline; filename="'.($project === 'produktionsschule' ? 'produktionsschule' : 'perspektivwerk').'-termine.ics"'); header('Cache-Control: no-store'); echo implode("\r\n", $lines)."\r\n";