79 lines
2.6 KiB
PHP
79 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\CalendarInstance;
|
|
use Illuminate\Support\Facades\Response;
|
|
use Carbon\Carbon;
|
|
use Sabre\VObject\Component\VCalendar;
|
|
use Sabre\VObject\Reader;
|
|
|
|
class IcsController extends Controller
|
|
{
|
|
public function download(string $calendarUri)
|
|
{
|
|
$instance = CalendarInstance::where('uri', $calendarUri)->firstOrFail();
|
|
|
|
$calendar = $instance->calendar()->with(['events.meta'])->firstOrFail();
|
|
$timezone = $instance->timezone ?? 'UTC';
|
|
|
|
$ical = $this->generateICalendarFeed($calendar->events, $timezone);
|
|
|
|
return Response::make($ical, 200, [
|
|
'Content-Type' => 'text/calendar; charset=utf-8',
|
|
'Content-Disposition' => 'inline; filename="' . $calendarUri . '.ics"',
|
|
]);
|
|
}
|
|
|
|
protected function generateICalendarFeed($events, string $tz): string
|
|
{
|
|
$vcalendar = new VCalendar();
|
|
$vcalendar->add('VERSION', '2.0');
|
|
$vcalendar->add('PRODID', '-//Kithkin Calendar//EN');
|
|
$vcalendar->add('CALSCALE', 'GREGORIAN');
|
|
$vcalendar->add('METHOD', 'PUBLISH');
|
|
|
|
foreach ($events as $event) {
|
|
$ical = $event->calendardata ?? null;
|
|
|
|
if ($ical) {
|
|
try {
|
|
$parsed = Reader::read($ical);
|
|
foreach ($parsed->select('VEVENT') as $vevent) {
|
|
$vcalendar->add(clone $vevent);
|
|
}
|
|
continue;
|
|
} catch (\Throwable $e) {
|
|
// fall through to meta-based output
|
|
}
|
|
}
|
|
|
|
$meta = $event->meta;
|
|
if (!$meta || !$meta->start_at || !$meta->end_at) {
|
|
continue;
|
|
}
|
|
|
|
$start = Carbon::parse($meta->start_at)->timezone($tz);
|
|
$end = Carbon::parse($meta->end_at)->timezone($tz);
|
|
|
|
$vevent = $vcalendar->add('VEVENT', []);
|
|
$vevent->add('UID', $event->uid);
|
|
$vevent->add('SUMMARY', $meta->title ?? '(Untitled)');
|
|
$vevent->add('DESCRIPTION', $meta->description ?? '');
|
|
$vevent->add('DTSTART', $start, ['TZID' => $tz]);
|
|
$vevent->add('DTEND', $end, ['TZID' => $tz]);
|
|
$vevent->add('DTSTAMP', Carbon::parse($event->lastmodified)->utc());
|
|
if ($meta->location) {
|
|
$vevent->add('LOCATION', $meta->location);
|
|
}
|
|
}
|
|
|
|
return $vcalendar->serialize();
|
|
}
|
|
|
|
protected function escape(?string $text): string
|
|
{
|
|
return str_replace(['\\', ';', ',', "\n"], ['\\\\', '\;', '\,', '\n'], $text ?? '');
|
|
}
|
|
}
|