query('owner'); $token = (string) $request->query('token'); abort_unless($owner !== '' && $token !== '', 403); abort_unless( hash_equals( hash_hmac('sha256', "{$organization->id}:{$owner}", (string) config('app.key')), $token, ), 403, ); abort_unless($organization->owner_ref === $owner, 404); $visits = Visit::owned($owner) ->where('organization_id', $organization->id) ->whereIn('status', [ Visit::STATUS_SCHEDULED, Visit::STATUS_EXPECTED, Visit::STATUS_WAITING, ]) ->whereNotNull('scheduled_at') ->where('scheduled_at', '>=', now()->subDay()) ->with(['visitor', 'host']) ->orderBy('scheduled_at') ->limit(500) ->get(); $lines = [ 'BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Ladill Frontdesk//EN', 'CALSCALE:GREGORIAN', ]; foreach ($visits as $visit) { $start = $visit->scheduled_at->utc()->format('Ymd\THis\Z'); $end = $visit->scheduled_at->copy()->addHour()->utc()->format('Ymd\THis\Z'); $summary = $this->escape('Visit: '.$visit->visitor->full_name); $description = $this->escape(trim(($visit->purpose ?? '').' Host: '.($visit->host?->name ?? '—'))); $lines[] = 'BEGIN:VEVENT'; $lines[] = 'UID:frontdesk-visit-'.$visit->id.'@ladill.com'; $lines[] = 'DTSTAMP:'.now()->utc()->format('Ymd\THis\Z'); $lines[] = 'DTSTART:'.$start; $lines[] = 'DTEND:'.$end; $lines[] = 'SUMMARY:'.$summary; $lines[] = 'DESCRIPTION:'.$description; $lines[] = 'END:VEVENT'; } $lines[] = 'END:VCALENDAR'; return response(implode("\r\n", $lines), 200, [ 'Content-Type' => 'text/calendar; charset=utf-8', 'Content-Disposition' => 'attachment; filename="frontdesk-visits.ics"', ]); } protected function escape(string $value): string { return str_replace(["\n", ',', ';'], ['\\n', '\\,', '\\;'], $value); } }