Skip to content

Commit c9d064f

Browse files
committed
feat: event permalinks
1 parent af416a0 commit c9d064f

File tree

4 files changed

+471
-62
lines changed

4 files changed

+471
-62
lines changed

Http/Controllers/LJPcCalendarModuleAPIController.php

Lines changed: 173 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -255,74 +255,194 @@ public function deleteCalendar( int $id ): JsonResponse {
255255
* @throws Exception
256256
*/
257257
public function getEvents( Request $request ) {
258-
$request->validate( [
259-
'start' => 'required|date',
260-
'end' => 'required|date',
261-
] );
258+
// Special case: If looking for a specific event by ID
259+
$eventId = $request->input( 'eventId' );
260+
if ( $eventId ) {
261+
try {
262+
// Check if tables exist first
263+
if ( ! \Schema::hasTable( 'calendars' ) || ! \Schema::hasTable( 'calendar_items' ) ) {
264+
return response()->json( [] );
265+
}
262266

263-
$defaultTimezone = config( 'app.timezone' );
267+
$events = [];
268+
$calendars = Calendar::all();
269+
270+
foreach ( $calendars as $calendar ) {
271+
if ( $calendar->enabled === false ) {
272+
continue;
273+
}
274+
$permissions = $calendar->permissionsForCurrentUser();
275+
if ( $permissions === null ) {
276+
continue;
277+
}
278+
if ( $permissions['showInCalendar'] !== true ) {
279+
continue;
280+
}
281+
282+
// For normal calendars, try to find the specific event
283+
if ( $calendar->type === 'normal' ) {
284+
$event = CalendarItem::where( 'uid', $eventId )
285+
->orWhere( 'id', $eventId )
286+
->where( 'calendar_id', $calendar->id )
287+
->first();
288+
289+
if ( $event ) {
290+
// Found the event, return it
291+
$eventData = json_decode( $event->toJson(), true );
292+
293+
// Generate mapping for used custom fields
294+
if ( isset( $eventData['custom_fields'] ) && is_array( $eventData['custom_fields'] ) ) {
295+
$eventData['custom_fields_mapping'] = $this->generateCustomFieldMapping(
296+
$calendar->custom_fields,
297+
$eventData['custom_fields']
298+
);
299+
}
300+
301+
// Set default timezone
302+
$defaultTimezone = config( 'app.timezone' );
303+
$eventData['start'] = ( new DateTimeImmutable( $eventData['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
304+
$eventData['end'] = ( new DateTimeImmutable( $eventData['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
305+
306+
if ( empty( $eventData['id'] ) && ! empty( $eventData['uid'] ) ) {
307+
$eventData['id'] = $eventData['uid'];
308+
}
309+
if ( empty( $eventData['uid'] ) && ! empty( $eventData['id'] ) ) {
310+
$eventData['uid'] = $eventData['id'];
311+
}
312+
if ( empty( $eventData['title'] ) ) {
313+
$eventData['title'] = '';
314+
}
315+
316+
$events[] = $eventData;
317+
break;
318+
}
319+
}
320+
321+
// For external calendars, we need to look through all events
322+
if ( $calendar->type === 'ics' || $calendar->type === 'caldav' ) {
323+
// Use a wider date range when searching for a specific event
324+
// Use an extremely wide date range to find any event, regardless of when it occurred
325+
// 5 years in past, 5 years in future
326+
$wideStart = ( new DateTimeImmutable() )->sub( new DateInterval( 'P5Y' ) )->setTimezone( new DateTimeZone( 'UTC' ) );
327+
$wideEnd = ( new DateTimeImmutable() )->add( new DateInterval( 'P5Y' ) )->setTimezone( new DateTimeZone( 'UTC' ) );
328+
329+
try {
330+
$calendarEvents = $calendar->events( $wideStart, $wideEnd );
331+
foreach ( $calendarEvents as $event ) {
332+
if ( isset( $event['id'] ) && $event['id'] === $eventId ) {
333+
// Found the event, prepare it for return
334+
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
335+
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
336+
$calendar->custom_fields,
337+
$event['custom_fields']
338+
);
339+
}
340+
$events[] = $event;
341+
break;
342+
}
343+
}
344+
} catch ( \Exception $e ) {
345+
// Log and continue - we don't want a single calendar to break the entire request
346+
\Log::error( 'Error fetching events from external calendar: ' . $e->getMessage() );
347+
}
348+
}
349+
}
264350

265-
$start = ( new DateTimeImmutable( $request->input( 'start' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
266-
$end = ( new DateTimeImmutable( $request->input( 'end' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
351+
// Restore timezones
352+
$defaultTimezone = config( 'app.timezone' );
353+
foreach ( $events as &$event ) {
354+
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
355+
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
356+
}
357+
unset( $event );
267358

268-
$calendars = Calendar::all();
269-
$events = [];
359+
return response()->json( $events );
360+
} catch ( \Exception $e ) {
361+
// Log the error but return a valid response
362+
\Log::error( 'Error in getEvents by ID: ' . $e->getMessage() );
270363

271-
foreach ( $calendars as $calendar ) {
272-
if ( $calendar->enabled === false ) {
273-
continue;
274-
}
275-
$permissions = $calendar->permissionsForCurrentUser();
276-
if ( $permissions === null ) {
277-
continue;
364+
return response()->json( [] );
278365
}
279-
if ( $permissions['showInCalendar'] !== true ) {
280-
continue;
366+
}
367+
368+
try {
369+
// Regular date range query
370+
$request->validate( [
371+
'start' => 'required|date',
372+
'end' => 'required|date',
373+
] );
374+
375+
// Check if tables exist first
376+
if ( ! \Schema::hasTable( 'calendars' ) || ! \Schema::hasTable( 'calendar_items' ) ) {
377+
return response()->json( [] );
281378
}
282379

283-
$calendarEvents = $calendar->events( $start, $end );
284-
foreach ( $calendarEvents as $event ) {
285-
// Generate mapping for used custom fields
286-
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
287-
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
288-
$calendar->custom_fields,
289-
$event['custom_fields']
290-
);
380+
$defaultTimezone = config( 'app.timezone' );
381+
382+
$start = ( new DateTimeImmutable( $request->input( 'start' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
383+
$end = ( new DateTimeImmutable( $request->input( 'end' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
384+
385+
$calendars = Calendar::all();
386+
$events = [];
387+
388+
foreach ( $calendars as $calendar ) {
389+
if ( $calendar->enabled === false ) {
390+
continue;
391+
}
392+
$permissions = $calendar->permissionsForCurrentUser();
393+
if ( $permissions === null ) {
394+
continue;
395+
}
396+
if ( $permissions['showInCalendar'] !== true ) {
397+
continue;
291398
}
292-
$events[] = $event;
293-
}
294-
}
295399

296-
/**
297-
* Restore timezones
298-
*/
299-
foreach ( $events as &$event ) {
300-
if ( empty( $event['id'] ) && ! empty( $event['uid'] ) ) {
301-
$event['id'] = $event['uid'];
302-
}
303-
if ( empty( $event['uid'] ) && ! empty( $event['id'] ) ) {
304-
$event['uid'] = $event['id'];
400+
try {
401+
$calendarEvents = $calendar->events( $start, $end );
402+
foreach ( $calendarEvents as $event ) {
403+
// Generate mapping for used custom fields
404+
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
405+
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
406+
$calendar->custom_fields,
407+
$event['custom_fields']
408+
);
409+
}
410+
$events[] = $event;
411+
}
412+
} catch ( \Exception $e ) {
413+
// Log and continue - we don't want a single calendar to break the entire request
414+
\Log::error( 'Error fetching events from calendar ' . $calendar->id . ': ' . $e->getMessage() );
415+
}
305416
}
306-
if ( empty( $event['title'] ) ) {
307-
$event['title'] = '';
417+
418+
/**
419+
* Restore timezones
420+
*/
421+
foreach ( $events as &$event ) {
422+
if ( empty( $event['id'] ) && ! empty( $event['uid'] ) ) {
423+
$event['id'] = $event['uid'];
424+
}
425+
if ( empty( $event['uid'] ) && ! empty( $event['id'] ) ) {
426+
$event['uid'] = $event['id'];
427+
}
428+
if ( empty( $event['title'] ) ) {
429+
$event['title'] = '';
430+
}
431+
432+
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
433+
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
308434
}
435+
unset( $event );
309436

310-
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
311-
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
312-
}
313-
unset( $event );
437+
return response()->json( $events );
438+
} catch ( \Exception $e ) {
439+
// Log the error but return a valid response
440+
\Log::error( 'Error in getEvents date range: ' . $e->getMessage() );
314441

315-
return response()->json( $events );
442+
return response()->json( [] );
443+
}
316444
}
317445

318-
/**
319-
* Update an event
320-
*
321-
* @param Request $request
322-
*
323-
* @return JsonResponse
324-
* @throws Exception
325-
*/
326446
/**
327447
* Update an event
328448
*

Providers/LJPcCalendarModuleServiceProvider.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,21 @@ public function hooks() {
216216
$person = $thread->getActionPerson( $conversation_number );
217217

218218
$defaultTimezone = config( 'app.timezone' );
219+
220+
// Generate the view event link
221+
$eventLink = '';
222+
if (isset($meta['calendar_item_id'])) {
223+
// Generate permalink URL with the format we created (#eventID)
224+
$calendarUrl = route('ljpccalendarmodule.index');
225+
$permalink = $calendarUrl . '#' . $meta['calendar_item_id'];
226+
$eventLink = ' <a href="' . $permalink . '" target="_blank" class="link-blue">' . __('View event') . '</a>';
227+
}
219228

220229
return __( 'Added to calendar ":calendar" by :person. Starts at :start.', [
221230
'calendar' => $calendar->name,
222231
'person' => $person,
223232
'start' => $start->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( __( 'd-m-Y H:i' ) ),
224-
] );
233+
] ) . $eventLink;
225234
}, 20, 4 );
226235

227236
Eventy::addAction( 'thread.attachments_list_append', function ( $thread, $conversation, $mailbox ) {
@@ -292,4 +301,4 @@ public function registerTranslations() {
292301
public function provides() {
293302
return [];
294303
}
295-
}
304+
}

Resources/views/index.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
<button type="submit" id="update-button" class="btn btn-primary">{{__('Update')}}</button>
116116
<button type="button" id="create-copy-button" class="btn btn-success" disabled>{{__('Create Copy')}}</button>
117117
<button type="button" id="delete-button" class="btn btn-danger" disabled>{{__('Delete')}}</button>
118+
<button type="button" id="copy-permalink-button" class="btn btn-default">{{__('Copy Permalink')}}</button>
118119

119120
</form>
120121
</div>

0 commit comments

Comments
 (0)