findOrFail($userId); $lines = [ 'BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//VotreApp//FR', ]; $now = Carbon::now()->utc()->format('Ymd\THis\Z'); foreach ($user->tasks as $task) { // On ignore les tâches sans dates if (!$task->start || !$task->end) { continue; } $uid = $task->id . '-' . $user->id . '@votreapp.local'; $dtstart = Carbon::parse($task->start)->utc()->format('Ymd\THis\Z'); $dtend = Carbon::parse($task->end)->utc()->format('Ymd\THis\Z'); $summary = $this->escapeText($task->name); $description = $this->escapeText( trim( ($task->description ?? '') . ($task->event ? "\nÉvénement: {$task->event->name}" : '') ) ); $location = $this->escapeText($task->location ?? ''); $lines[] = 'BEGIN:VEVENT'; $lines[] = 'UID:' . $uid; $lines[] = 'DTSTAMP:' . $now; $lines[] = 'DTSTART:' . $dtstart; $lines[] = 'DTEND:' . $dtend; $lines[] = 'SUMMARY:' . $summary; $lines[] = 'DESCRIPTION:' . $description; $lines[] = 'LOCATION:' . $location; $lines[] = 'END:VEVENT'; } $lines[] = 'END:VCALENDAR'; $content = implode("\r\n", $lines) . "\r\n"; return response($content, 200, [ 'Content-Type' => 'text/calendar; charset=utf-8', 'Content-Disposition' => 'attachment; filename="agenda_user_' . $user->id . '.ics"', ]); } protected function escapeText($text) { $text = (string) $text; return str_replace( ["\\", "\r\n", "\n", "\r", ";", ","], ["\\\\", "\\n", "\\n", "\\n", "\\;", "\\,"], $text ); } }