68 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			68 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace App\Http\Controllers;
 | 
						|
 | 
						|
use App\Models\CalendarInstance;
 | 
						|
use Illuminate\Support\Facades\Response;
 | 
						|
use Carbon\Carbon;
 | 
						|
 | 
						|
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
 | 
						|
    {
 | 
						|
        $output = [];
 | 
						|
        $output[] = 'BEGIN:VCALENDAR';
 | 
						|
        $output[] = 'VERSION:2.0';
 | 
						|
        $output[] = 'PRODID:-//Kithkin Calendar//EN';
 | 
						|
        $output[] = 'CALSCALE:GREGORIAN';
 | 
						|
        $output[] = 'METHOD:PUBLISH';
 | 
						|
 | 
						|
        foreach ($events as $event) {
 | 
						|
            $meta = $event->meta;
 | 
						|
 | 
						|
            if (!$meta || !$meta->start_at || !$meta->end_at) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $start = Carbon::parse($meta->start_at)->timezone($tz)->format('Ymd\THis');
 | 
						|
            $end = Carbon::parse($meta->end_at)->timezone($tz)->format('Ymd\THis');
 | 
						|
 | 
						|
            $output[] = 'BEGIN:VEVENT';
 | 
						|
            $output[] = 'UID:' . $event->uid;
 | 
						|
            $output[] = 'SUMMARY:' . $this->escape($meta->title ?? '(Untitled)');
 | 
						|
            $output[] = 'DESCRIPTION:' . $this->escape($meta->description ?? '');
 | 
						|
            $output[] = 'DTSTART;TZID=' . $tz . ':' . $start;
 | 
						|
            $output[] = 'DTEND;TZID=' . $tz . ':' . $end;
 | 
						|
            $output[] = 'DTSTAMP:' . Carbon::parse($event->lastmodified)->format('Ymd\THis\Z');
 | 
						|
            if ($meta->location) {
 | 
						|
                $output[] = 'LOCATION:' . $this->escape($meta->location);
 | 
						|
            }
 | 
						|
            $output[] = 'END:VEVENT';
 | 
						|
        }
 | 
						|
 | 
						|
        $output[] = 'END:VCALENDAR';
 | 
						|
 | 
						|
        return implode("\r\n", $output);
 | 
						|
    }
 | 
						|
 | 
						|
    protected function escape(?string $text): string
 | 
						|
    {
 | 
						|
        return str_replace(['\\', ';', ',', "\n"], ['\\\\', '\;', '\,', '\n'], $text ?? '');
 | 
						|
    }
 | 
						|
}
 |