authorize('update', $calendar); return view('events.form', ['calendar' => $calendar, 'event' => new Event]); } /** * insert VEVENT into Sabre’s calendarobjects + meta row */ public function store(Request $req, Calendar $calendar) { $this->authorize('update', $calendar); $data = $req->validate([ 'title' => 'required|string|max:200', 'start_at' => 'required|date', 'end_at' => 'required|date|after:start_at', 'description' => 'nullable|string', 'location' => 'nullable|string', 'all_day' => 'sometimes|boolean', 'category' => 'nullable|string|max:50', ]); // prepare payload $uid = Str::uuid() . '@' . parse_url(config('app.url'), PHP_URL_HOST); $start = new Carbon($data['start_at'], $calendar->timezone); $end = new Carbon($data['end_at'], $calendar->timezone); // prepare strings $description = $data['description'] ?? ''; $location = $data['location'] ?? ''; $description = str_replace("\n", '\\n', $description); $location = str_replace("\n", '\\n', $location); /* build minimal iCalendar payload */ $ical = <<utc()->format('Ymd\\THis\\Z')} DTSTART:{$start->format('Ymd\\THis')} DTEND:{$end->format('Ymd\\THis')} SUMMARY:{$data['title']} DESCRIPTION:$description LOCATION:$location END:VEVENT END:VCALENDAR ICS; $event = Event::create([ 'calendarid' => $calendar->id, 'uri' => Str::uuid() . '.ics', 'lastmodified' => time(), 'etag' => md5($ical), 'size' => strlen($ical), 'componenttype' => 'VEVENT', 'uid' => $uid, 'calendardata' => $ical, ]); $event->meta()->create([ 'title' => $data['title'], 'description' => $data['description'] ?? null, 'location' => $data['location'] ?? null, 'all_day' => $data['all_day'] ?? false, 'category' => $data['category'] ?? null, 'start_at' => $start, 'end_at' => $end, ]); return redirect()->route('calendars.show', $calendar); } public function edit(Calendar $calendar, Event $event) { $this->authorize('update', $calendar); return view('events.form', compact('calendar', 'event')); } /** * update vevent + meta */ public function update(Request $req, Calendar $calendar, Event $event) { $this->authorize('update', $calendar); $data = $req->validate([ 'title' => 'required|string|max:200', 'start_at' => 'required|date', 'end_at' => 'required|date|after:start_at', 'description' => 'nullable|string', 'location' => 'nullable|string', 'category' => 'nullable|string|max:50', ]); // rebuild the icalendar payload $tz = $calendar->timezone; $start = new \Carbon\Carbon($data['start_at'], $tz); $end = new \Carbon\Carbon($data['end_at'], $tz); // prepare strings $description = $data['description'] ?? ''; $location = $data['location'] ?? ''; $description = str_replace("\n", '\\n', $description); $location = str_replace("\n", '\\n', $location); // note: keep the UID stable (CalDAV relies on it!) $uid = $event->uid; $ical = <<utc()->format('Ymd\\THis\\Z')} DTSTART;TZID={$tz}:{$start->format('Ymd\\THis')} DTEND;TZID={$tz}:{$end->format('Ymd\\THis')} SUMMARY:{$data['title']} DESCRIPTION:$description LOCATION:$location END:VEVENT END:VCALENDAR ICS; // persist changes $event->update([ 'calendardata' => $ical, 'etag' => md5($ical), 'lastmodified' => time(), ]); $event->meta()->updateOrCreate([], [ 'title' => $data['title'], 'description' => $data['description'] ?? null, 'location' => $data['location'] ?? null, 'all_day' => $data['all_day'] ?? false, 'category' => $data['category'] ?? null, 'start_at' => $start, 'end_at' => $end, ]); return redirect()->route('calendars.show', $calendar); } }