mirror of
https://gh.wpcy.net/https://github.com/LJPc-solutions/freescout-calendar-module.git
synced 2026-05-26 01:04:16 +08:00
1186 lines
43 KiB
PHP
1186 lines
43 KiB
PHP
<?php
|
|
|
|
namespace Modules\LJPcCalendarModule\Http\Controllers;
|
|
|
|
use App\Attachment;
|
|
use App\Conversation;
|
|
use App\Misc\Helper;
|
|
use App\Thread;
|
|
use App\User;
|
|
use DateInterval;
|
|
use DateTimeImmutable;
|
|
use DateTimeZone;
|
|
use Exception;
|
|
use ICal\Event;
|
|
use ICal\ICal;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Log;
|
|
use Modules\LJPcCalendarModule\Entities\Calendar;
|
|
use Modules\LJPcCalendarModule\Entities\CalendarItem;
|
|
use Modules\LJPcCalendarModule\Http\Helpers\CalDAV;
|
|
use Modules\LJPcCalendarModule\Http\Helpers\DateTimeRange;
|
|
use Modules\LJPcCalendarModule\Jobs\UpdateExternalCalendarJob;
|
|
use Modules\Teams\Providers\TeamsServiceProvider as Teams;
|
|
|
|
class LJPcCalendarModuleAPIController extends Controller {
|
|
/**
|
|
* Get users for the settings
|
|
*
|
|
* @return JsonResponse A JSON response containing an array of user objects
|
|
*/
|
|
public function getUsers(): JsonResponse {
|
|
$response = [
|
|
'results' => [],
|
|
];
|
|
|
|
// Get all teams
|
|
$allTeams = [];
|
|
if ( class_exists( Teams::class ) ) {
|
|
$allTeams = Teams::getTeams( true );
|
|
}
|
|
|
|
// Get all users that are active
|
|
$allUsers = User::where( 'status', User::STATUS_ACTIVE )
|
|
->remember( Helper::cacheTime() )
|
|
->get();
|
|
|
|
// Add team members to the response array
|
|
/** @var Team $team */
|
|
foreach ( $allTeams as $team ) {
|
|
$response['results'][] = [
|
|
'id' => (string) $team->id,
|
|
'text' => 'Team: ' . $team->getFirstName(),
|
|
];
|
|
}
|
|
|
|
// Add users to the response array
|
|
/** @var User $user */
|
|
foreach ( $allUsers as $user ) {
|
|
$response['results'][] = [
|
|
'id' => (string) $user->id,
|
|
'text' => $user->getFullName(),
|
|
];
|
|
}
|
|
|
|
return response()->json( $response );
|
|
}
|
|
|
|
/**
|
|
* Get calendars for the settings
|
|
*
|
|
* @return JsonResponse A JSON response containing an array of calendar objects
|
|
*/
|
|
public function getCalendars(): JsonResponse {
|
|
$calendars = Calendar::all();
|
|
|
|
return response()->json( $calendars );
|
|
}
|
|
|
|
/**
|
|
* Update a calendar
|
|
*
|
|
* @param int $id The ID of the calendar to update
|
|
* @param Request $request The request object
|
|
*
|
|
* @return JsonResponse A JSON response containing the updated calendar object
|
|
*/
|
|
public function updateCalendar( int $id, Request $request ) {
|
|
$calendar = Calendar::find( $id );
|
|
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
$calendar->name = $request->input( 'name' );
|
|
$calendar->color = $request->input( 'color' );
|
|
$calendar->title_template = $request->input( 'title_template' );
|
|
|
|
$customFields = $request->input( 'custom_fields', [] );
|
|
$calendar->custom_fields = $this->validateCustomFields( $customFields );
|
|
|
|
if ( $calendar->type === 'ics' ) {
|
|
$calendar->custom_fields = array_merge( $calendar->custom_fields, [
|
|
'url' => $request->input( 'url' ),
|
|
'refresh' => $request->input( 'refresh' ),
|
|
] );
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$calendar->custom_fields = array_merge( $calendar->custom_fields, [
|
|
'url' => $request->input( 'url' ),
|
|
'username' => $request->input( 'username' ),
|
|
'password' => $request->input( 'password' ),
|
|
'refresh' => $request->input( 'refresh' ),
|
|
] );
|
|
}
|
|
|
|
$permissions = [];
|
|
foreach ( $request->input( 'permissions' ) as $id => $permission ) {
|
|
$permissions[ $id ] = [
|
|
'showInDashboard' => $permission['showInDashboard'],
|
|
'showInCalendar' => $permission['showInCalendar'],
|
|
'createItems' => $permission['createItems'] ?? false,
|
|
'editItems' => $permission['editItems'] ?? false,
|
|
];
|
|
}
|
|
$calendar->permissions = $permissions;
|
|
|
|
$calendar->save();
|
|
|
|
return response()->json( $calendar );
|
|
}
|
|
|
|
/**
|
|
* Validate and sanitize custom fields
|
|
*
|
|
* @param array $customFields The custom fields to validate
|
|
*
|
|
* @return array The validated and sanitized custom fields
|
|
*/
|
|
private function validateCustomFields( array $customFields ): array {
|
|
$validatedFields = [];
|
|
|
|
foreach ( $customFields['fields'] as $field ) {
|
|
$validatedField = [
|
|
'id' => $field['id'],
|
|
'name' => strip_tags( $field['name'] ),
|
|
'type' => in_array( $field['type'], [ 'text', 'number', 'dropdown', 'boolean', 'multiselect', 'date', 'email', 'source' ] ) ? $field['type'] : 'text',
|
|
'required' => (bool) $field['required'],
|
|
];
|
|
|
|
if ( $validatedField['type'] === 'source' ) {
|
|
$validatedField['required'] = false;
|
|
}
|
|
|
|
if ( in_array( $field['type'], [ 'dropdown', 'multiselect' ] ) ) {
|
|
if ( is_array( $field['options'] ) ) {
|
|
$validatedField['options'] = array_map( 'trim', array_map( 'strip_tags', $field['options'] ) );
|
|
} else {
|
|
$validatedField['options'] = array_map( 'trim', array_map( 'strip_tags', explode( ',', $field['options'] ) ) );
|
|
}
|
|
}
|
|
|
|
$validatedFields[] = $validatedField;
|
|
}
|
|
|
|
return [ 'fields' => $validatedFields ];
|
|
}
|
|
|
|
/**
|
|
* Create a new calendar
|
|
*
|
|
* @param Request $request The request object
|
|
*
|
|
* @return JsonResponse A JSON response containing the created calendar object
|
|
*/
|
|
public function addCalendar( Request $request ) {
|
|
$calendar = new Calendar();
|
|
|
|
$calendar->name = $request->input( 'name' );
|
|
$calendar->color = $request->input( 'color' );
|
|
$calendar->type = $request->input( 'type' );
|
|
$calendar->title_template = $request->input( 'title_template' );
|
|
|
|
$customFields = $request->input( 'custom_fields', [] );
|
|
$calendar->custom_fields = $this->validateCustomFields( $customFields );
|
|
|
|
if ( $calendar->type === 'ics' ) {
|
|
$calendar->custom_fields = [
|
|
'url' => $request->input( 'url' ),
|
|
'refresh' => $request->input( 'refresh' ),
|
|
];
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$calendar->custom_fields = [
|
|
'url' => $request->input( 'url' ),
|
|
'username' => $request->input( 'username' ),
|
|
'password' => $request->input( 'password' ),
|
|
'refresh' => $request->input( 'refresh' ),
|
|
];
|
|
}
|
|
|
|
$permissions = [];
|
|
foreach ( $request->input( 'permissions' ) as $id => $permission ) {
|
|
$permissions[ $id ] = [
|
|
'showInDashboard' => $permission['showInDashboard'],
|
|
'showInCalendar' => $permission['showInCalendar'],
|
|
'createItems' => $permission['createItems'] ?? false,
|
|
'editItems' => $permission['editItems'] ?? false,
|
|
];
|
|
}
|
|
$calendar->permissions = $permissions;
|
|
|
|
$calendar->save();
|
|
|
|
return response()->json( $calendar );
|
|
}
|
|
|
|
/**
|
|
* Delete a calendar
|
|
*
|
|
* @param int $id The ID of the calendar to delete
|
|
*
|
|
* @return JsonResponse A JSON response indicating the deletion result
|
|
* @throws Exception
|
|
*/
|
|
public function deleteCalendar( int $id ): JsonResponse {
|
|
$calendar = Calendar::find( $id );
|
|
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
if ( $calendar->type === 'ics' || $calendar->type === 'caldav' ) {
|
|
//remove temporary ics file
|
|
$filename = $calendar->getTemporaryFile();
|
|
if ( file_exists( $filename ) ) {
|
|
unlink( $filename );
|
|
}
|
|
} else {
|
|
//remove all calendar items for this calendar
|
|
CalendarItem::where( 'calendar_id', $calendar->id )->delete();
|
|
}
|
|
|
|
$calendar->delete();
|
|
|
|
return response()->json( [ 'ok' => true, 'message' => 'Calendar deleted successfully' ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get events
|
|
*
|
|
* This method handles both date range queries and specific event ID lookups.
|
|
* For event ID lookups on external calendars, it uses an optimized approach
|
|
* that avoids loading unnecessary date ranges, improving performance for large calendars.
|
|
*
|
|
* @param Request $request The request containing either date range (start/end) or eventId
|
|
*
|
|
* @return JsonResponse Array of events matching the criteria
|
|
* @throws Exception
|
|
*/
|
|
public function getEvents( Request $request ) {
|
|
// Special case: If looking for a specific event by ID
|
|
$eventId = $request->input( 'eventId' );
|
|
if ( $eventId ) {
|
|
try {
|
|
// Check if tables exist first
|
|
if ( ! \Schema::hasTable( 'calendars' ) || ! \Schema::hasTable( 'calendar_items' ) ) {
|
|
return response()->json( [] );
|
|
}
|
|
|
|
$events = [];
|
|
$calendars = Calendar::all();
|
|
|
|
foreach ( $calendars as $calendar ) {
|
|
if ( $calendar->enabled === false ) {
|
|
continue;
|
|
}
|
|
$permissions = $calendar->permissionsForCurrentUser();
|
|
if ( $permissions === null ) {
|
|
continue;
|
|
}
|
|
if ( $permissions['showInCalendar'] !== true ) {
|
|
continue;
|
|
}
|
|
|
|
// For normal calendars, try to find the specific event
|
|
if ( $calendar->type === 'normal' ) {
|
|
$event = CalendarItem::where( 'uid', $eventId )
|
|
->orWhere( 'id', $eventId )
|
|
->where( 'calendar_id', $calendar->id )
|
|
->first();
|
|
|
|
if ( $event ) {
|
|
// Found the event, return it
|
|
$eventData = json_decode( $event->toJson(), true );
|
|
|
|
// Generate mapping for used custom fields
|
|
if ( isset( $eventData['custom_fields'] ) && is_array( $eventData['custom_fields'] ) ) {
|
|
$eventData['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$eventData['custom_fields']
|
|
);
|
|
}
|
|
|
|
// Set default timezone
|
|
$defaultTimezone = config( 'app.timezone' );
|
|
$eventData['start'] = ( new DateTimeImmutable( $eventData['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
$eventData['end'] = ( new DateTimeImmutable( $eventData['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
|
|
if ( empty( $eventData['id'] ) && ! empty( $eventData['uid'] ) ) {
|
|
$eventData['id'] = $eventData['uid'];
|
|
}
|
|
if ( empty( $eventData['uid'] ) && ! empty( $eventData['id'] ) ) {
|
|
$eventData['uid'] = $eventData['id'];
|
|
}
|
|
if ( empty( $eventData['title'] ) ) {
|
|
$eventData['title'] = '';
|
|
}
|
|
|
|
$events[] = $eventData;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// For external calendars, use optimized single event lookup
|
|
if ( $calendar->type === 'ics' || $calendar->type === 'caldav' ) {
|
|
try {
|
|
// Use the new optimized method for finding a single event
|
|
$event = $calendar->findEventById( $eventId );
|
|
|
|
if ( $event ) {
|
|
// Found the event, prepare it for return
|
|
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
|
|
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$event['custom_fields']
|
|
);
|
|
}
|
|
$events[] = $event;
|
|
break;
|
|
}
|
|
} catch ( \Exception $e ) {
|
|
// Log and continue - we don't want a single calendar to break the entire request
|
|
\Log::error( 'Error fetching event from external calendar: ' . $e->getMessage() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Restore timezones
|
|
$defaultTimezone = config( 'app.timezone' );
|
|
foreach ( $events as &$event ) {
|
|
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
unset( $event );
|
|
|
|
return response()->json( $events );
|
|
} catch ( \Exception $e ) {
|
|
// Log the error but return a valid response
|
|
\Log::error( 'Error in getEvents by ID: ' . $e->getMessage() );
|
|
|
|
return response()->json( [] );
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Regular date range query
|
|
$request->validate( [
|
|
'start' => 'required|date',
|
|
'end' => 'required|date',
|
|
] );
|
|
|
|
// Check if tables exist first
|
|
if ( ! \Schema::hasTable( 'calendars' ) || ! \Schema::hasTable( 'calendar_items' ) ) {
|
|
return response()->json( [] );
|
|
}
|
|
|
|
$defaultTimezone = config( 'app.timezone' );
|
|
|
|
$start = ( new DateTimeImmutable( $request->input( 'start' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$end = ( new DateTimeImmutable( $request->input( 'end' ), new DateTimeZone( $defaultTimezone ) ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
|
|
$calendars = Calendar::all();
|
|
$events = [];
|
|
|
|
foreach ( $calendars as $calendar ) {
|
|
if ( $calendar->enabled === false ) {
|
|
continue;
|
|
}
|
|
$permissions = $calendar->permissionsForCurrentUser();
|
|
if ( $permissions === null ) {
|
|
continue;
|
|
}
|
|
if ( $permissions['showInCalendar'] !== true ) {
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
$calendarEvents = $calendar->events( $start, $end );
|
|
foreach ( $calendarEvents as $event ) {
|
|
// Generate mapping for used custom fields
|
|
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
|
|
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$event['custom_fields']
|
|
);
|
|
}
|
|
$events[] = $event;
|
|
}
|
|
} catch ( \Exception $e ) {
|
|
// Log and continue - we don't want a single calendar to break the entire request
|
|
\Log::error( 'Error fetching events from calendar ' . $calendar->id . ': ' . $e->getMessage() );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restore timezones
|
|
*/
|
|
foreach ( $events as &$event ) {
|
|
if ( empty( $event['id'] ) && ! empty( $event['uid'] ) ) {
|
|
$event['id'] = $event['uid'];
|
|
}
|
|
if ( empty( $event['uid'] ) && ! empty( $event['id'] ) ) {
|
|
$event['uid'] = $event['id'];
|
|
}
|
|
if ( empty( $event['title'] ) ) {
|
|
$event['title'] = '';
|
|
}
|
|
|
|
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
unset( $event );
|
|
|
|
return response()->json( $events );
|
|
} catch ( \Exception $e ) {
|
|
// Log the error but return a valid response
|
|
\Log::error( 'Error in getEvents date range: ' . $e->getMessage() );
|
|
|
|
return response()->json( [] );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update an event
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return JsonResponse
|
|
* @throws Exception
|
|
*/
|
|
public function updateEvent( Request $request ) {
|
|
try {
|
|
$validatedData = $request->validate( [
|
|
'uid' => 'required',
|
|
'title' => 'required',
|
|
'start' => 'required|date',
|
|
'end' => 'required|date|after:start',
|
|
'location' => 'nullable',
|
|
'body' => 'nullable',
|
|
'calendarId' => 'required',
|
|
'customFields' => 'nullable',
|
|
] );
|
|
} catch ( Exception $e ) {
|
|
return response()->json( [ 'error' => $e->getMessage() ], 400 );
|
|
}
|
|
|
|
$calendar = Calendar::find( $validatedData['calendarId'] );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
$customFieldsData = $validatedData['customFields'] ?? [];
|
|
$customFields = $calendar->custom_fields['fields'] ?? [];
|
|
$processedCustomFields = [];
|
|
|
|
foreach ( $customFields as $field ) {
|
|
$fieldId = 'custom_field_' . $field['id'];
|
|
if ( isset( $customFieldsData[ $fieldId ] ) ) {
|
|
$processedCustomFields[ $fieldId ] = $customFieldsData[ $fieldId ];
|
|
} else if ( $field['required'] ) {
|
|
return response()->json( [ 'error' => 'Required custom field missing: ' . $field['name'] ], 400 );
|
|
}
|
|
}
|
|
|
|
if ( $calendar->type === 'normal' ) {
|
|
$calendarItem = CalendarItem::where( 'id', $validatedData['uid'] )->first();
|
|
if ( ! $calendarItem ) {
|
|
return response()->json( [ 'error' => 'Event not found' ], 404 );
|
|
}
|
|
|
|
$calendarItem->title = $validatedData['title'];
|
|
$calendarItem->start = ( new DateTimeImmutable( $validatedData['start'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$calendarItem->end = ( new DateTimeImmutable( $validatedData['end'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$calendarItem->location = $validatedData['location'];
|
|
$calendarItem->body = $validatedData['body'];
|
|
if ( ! is_array( $calendarItem->custom_fields ) ) {
|
|
$calendarItem->custom_fields = [];
|
|
}
|
|
$calendarItem->custom_fields = array_merge( $calendarItem->custom_fields, $processedCustomFields );
|
|
|
|
$calendarItem->save();
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$fullUrl = $calendar->custom_fields['url'];
|
|
$baseUrl = substr( $fullUrl, 0, strpos( $fullUrl, '/', 8 ) );
|
|
$remainingUrl = substr( $fullUrl, strpos( $fullUrl, '/', 8 ) );
|
|
$caldavClient = new CalDAV( $baseUrl, $calendar->custom_fields['username'], $calendar->custom_fields['password'] );
|
|
|
|
$start = ( new DateTimeImmutable( $validatedData['start'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$end = ( new DateTimeImmutable( $validatedData['end'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
|
|
if ( isset( $customFieldsData['author_id'] ) ) {
|
|
$processedCustomFields['author_id'] = $customFieldsData['author_id'];
|
|
}
|
|
if ( isset( $customFieldsData['conversation_id'] ) ) {
|
|
$processedCustomFields['conversation_id'] = $customFieldsData['conversation_id'];
|
|
}
|
|
|
|
if ( count( $processedCustomFields ) > 0 ) {
|
|
if ( ! is_array( $validatedData['body'] ) ) {
|
|
$validatedData['body'] = [ 'body' => $validatedData['body'] ];
|
|
}
|
|
|
|
if ( ! isset( $validatedData['body']['custom_fields'] ) || ! is_array( $validatedData['body']['custom_fields'] ) ) {
|
|
$validatedData['body']['custom_fields'] = [];
|
|
}
|
|
|
|
$validatedData['body']['custom_fields'] = array_merge(
|
|
$validatedData['body']['custom_fields'],
|
|
$processedCustomFields
|
|
);
|
|
|
|
// Add mapping for used custom fields
|
|
$validatedData['body']['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$processedCustomFields
|
|
);
|
|
}
|
|
|
|
$response = $caldavClient->updateEvent(
|
|
$remainingUrl,
|
|
$validatedData['uid'],
|
|
$validatedData['title'],
|
|
$validatedData['body'],
|
|
$start,
|
|
$end,
|
|
$validatedData['location']
|
|
);
|
|
|
|
if ( $response['statusCode'] < 200 || $response['statusCode'] > 300 ) {
|
|
Log::error( 'Error updating event', $response );
|
|
|
|
return response()->json( [ 'error' => 'Error updating event' ], 500 );
|
|
}
|
|
|
|
$calendar->getExternalContent( true );
|
|
}
|
|
|
|
return response()->json( [ 'ok' => true, 'message' => 'Event updated successfully' ] );
|
|
}
|
|
|
|
/**
|
|
* Generate a GUID
|
|
* @return string
|
|
*/
|
|
private function GUID() {
|
|
if ( function_exists( 'com_create_guid' ) === true ) {
|
|
return trim( com_create_guid(), '{}' );
|
|
}
|
|
|
|
return sprintf( '%04X%04X-%04X-%04X-%04X-%04X%04X%04X', mt_rand( 0, 65535 ), mt_rand( 0, 65535 ), mt_rand( 0, 65535 ), mt_rand( 16384, 20479 ), mt_rand( 32768, 49151 ), mt_rand( 0, 65535 ), mt_rand( 0, 65535 ), mt_rand( 0, 65535 ) );
|
|
}
|
|
|
|
/**
|
|
* Delete an event
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return JsonResponse
|
|
* @throws Exception
|
|
*/
|
|
public function deleteEvent( Request $request ) {
|
|
$eventId = $request->input( 'id' );
|
|
$calendarId = $request->input( 'calendarId' );
|
|
|
|
$calendar = Calendar::find( $calendarId );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
if ( $calendar->type === 'normal' ) {
|
|
$calendarItem = CalendarItem::where( 'id', $eventId )->first();
|
|
if ( ! $calendarItem ) {
|
|
return response()->json( [ 'error' => 'Event not found' ], 404 );
|
|
}
|
|
$calendarItem->delete();
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$fullUrl = $calendar->custom_fields['url'];
|
|
$baseUrl = substr( $fullUrl, 0, strpos( $fullUrl, '/', 8 ) );
|
|
$remainingUrl = substr( $fullUrl, strpos( $fullUrl, '/', 8 ) );
|
|
$caldavClient = new CalDAV( $baseUrl, $calendar->custom_fields['username'], $calendar->custom_fields['password'] );
|
|
|
|
$response = $caldavClient->deleteEvent( $remainingUrl, $eventId );
|
|
if ( $response['statusCode'] < 200 || $response['statusCode'] > 300 ) {
|
|
Log::error( 'Error deleting event', $response );
|
|
|
|
return response()->json( [ 'error' => 'Error deleting event' ], 500 );
|
|
}
|
|
$calendar->getExternalContent( true );
|
|
}
|
|
|
|
return response()->json( [ 'ok' => true, 'message' => 'Event deleted successfully' ] );
|
|
}
|
|
|
|
/**
|
|
* Helper function to generate custom field mapping
|
|
*
|
|
* @param array $customFields Array of all possible custom fields
|
|
* @param array $usedCustomFields Array of used custom field values
|
|
*
|
|
* @return array Mapping of used custom field IDs to their names
|
|
*/
|
|
private function generateCustomFieldMapping( array $customFields, array $usedCustomFields ): array {
|
|
$mapping = [];
|
|
$fields = $customFields['fields'] ?? [];
|
|
|
|
foreach ( $fields as $field ) {
|
|
$fieldId = 'custom_field_' . $field['id'];
|
|
// Only include fields that are actually used in the item
|
|
if ( isset( $usedCustomFields[ $fieldId ] ) ) {
|
|
$mapping[ $fieldId ] = $field['name'];
|
|
}
|
|
}
|
|
|
|
return $mapping;
|
|
}
|
|
|
|
/**
|
|
* Create an event
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return JsonResponse
|
|
* @throws Exception
|
|
*/
|
|
public function createEvent( Request $request ) {
|
|
try {
|
|
$validatedData = $request->validate( [
|
|
'title' => 'required',
|
|
'start' => 'required|date',
|
|
'end' => 'required|date',
|
|
'location' => 'nullable',
|
|
'body' => 'nullable',
|
|
'calendarId' => 'required',
|
|
'customFields' => 'nullable',
|
|
] );
|
|
} catch ( Exception $e ) {
|
|
return response()->json( [ 'error' => $e->getMessage() ], 400 );
|
|
}
|
|
|
|
$calendar = Calendar::find( $validatedData['calendarId'] );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
$customFieldsData = $validatedData['customFields'] ?? [];
|
|
$customFields = $calendar->custom_fields['fields'] ?? [];
|
|
$processedCustomFields = [
|
|
'author_id' => auth()->id(),
|
|
];
|
|
|
|
foreach ( $customFields as $field ) {
|
|
$fieldId = 'custom_field_' . $field['id'];
|
|
if ( isset( $customFieldsData[ $fieldId ] ) ) {
|
|
$processedCustomFields[ $fieldId ] = $customFieldsData[ $fieldId ];
|
|
} else if ( $field['required'] ) {
|
|
return response()->json( [ 'error' => 'Required custom field missing: ' . $field['name'] ], 400 );
|
|
}
|
|
}
|
|
|
|
$start = ( new DateTimeImmutable( $validatedData['start'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$end = ( new DateTimeImmutable( $validatedData['end'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
if ( $start->getTimestamp() === $end->getTimestamp() ) {
|
|
$end = $start->add( new DateInterval( 'P1D' ) )->sub( new DateInterval( 'PT1S' ) );
|
|
}
|
|
|
|
$isAllDay = DateTimeRange::isAllDay( $start, $end );
|
|
|
|
if ( $calendar->type === 'normal' ) {
|
|
$calendarItem = new CalendarItem();
|
|
$calendarItem->calendar_id = $validatedData['calendarId'];
|
|
$calendarItem->title = $validatedData['title'];
|
|
$calendarItem->start = $start;
|
|
$calendarItem->end = $end;
|
|
$calendarItem->is_all_day = $isAllDay;
|
|
$calendarItem->location = $validatedData['location'] ?? '';
|
|
$calendarItem->body = $validatedData['body'] ?? '';
|
|
$calendarItem->custom_fields = $processedCustomFields;
|
|
|
|
$calendarItem->save();
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$fullUrl = $calendar->custom_fields['url'];
|
|
$baseUrl = substr( $fullUrl, 0, strpos( $fullUrl, '/', 8 ) );
|
|
$remainingUrl = substr( $fullUrl, strpos( $fullUrl, '/', 8 ) );
|
|
$caldavClient = new CalDAV( $baseUrl, $calendar->custom_fields['username'], $calendar->custom_fields['password'] );
|
|
|
|
$uid = $this->GUID();
|
|
|
|
if ( ! is_array( $validatedData['body'] ) ) {
|
|
$validatedData['body'] = [ 'body' => empty( $validatedData['body'] ) ? '-' : $validatedData['body'] ];
|
|
}
|
|
|
|
$validatedData['body']['custom_fields'] = $processedCustomFields;
|
|
|
|
// Add mapping for used custom fields
|
|
$validatedData['body']['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$processedCustomFields
|
|
);
|
|
|
|
$response = $caldavClient->createEvent(
|
|
$remainingUrl,
|
|
$uid,
|
|
$validatedData['title'],
|
|
$validatedData['body'],
|
|
$start,
|
|
$end,
|
|
$isAllDay,
|
|
$validatedData['location']
|
|
);
|
|
|
|
if ( $response['statusCode'] < 200 || $response['statusCode'] > 300 ) {
|
|
Log::error( 'Error creating event', $response );
|
|
|
|
return response()->json( [ 'error' => 'Error creating event' ], 500 );
|
|
}
|
|
|
|
$calendar->getExternalContent( true );
|
|
}
|
|
|
|
return response()->json( [ 'ok' => true, 'message' => 'Event created successfully' ] );
|
|
}
|
|
|
|
private function processTemplate( string $template, Conversation $conversation, Calendar $calendar, array $customFields = [] ): string {
|
|
$result = str_replace( '{{title}}', $conversation->subject, $template );
|
|
|
|
// Get the field name mapping from calendar's custom fields configuration
|
|
$fieldMapping = [];
|
|
if ( ! empty( $calendar->custom_fields['fields'] ) ) {
|
|
foreach ( $calendar->custom_fields['fields'] as $field ) {
|
|
$fieldMapping[ 'custom_field_' . $field['id'] ] = $field['name'];
|
|
}
|
|
}
|
|
|
|
// Process custom fields using the mapping
|
|
foreach ( $customFields as $fieldId => $value ) {
|
|
if ( is_array( $value ) ) {
|
|
$value = implode( ', ', $value );
|
|
}
|
|
|
|
// Only process if we have a mapping for this field ID
|
|
if ( isset( $fieldMapping[ $fieldId ] ) ) {
|
|
$fieldName = $fieldMapping[ $fieldId ];
|
|
$result = str_replace( '{{' . $fieldName . '}}', $value ?? '', $result );
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Create an event from a conversation
|
|
*
|
|
* @param int $conversation
|
|
* @param Request $request
|
|
*
|
|
* @return JsonResponse
|
|
* @throws Exception
|
|
*/
|
|
public function createEventFromConversation( int $conversation, Request $request ) {
|
|
try {
|
|
$validatedData = $request->validate( [
|
|
'title' => 'required|nullable',
|
|
'start' => 'required|date',
|
|
'end' => 'required|date|after:start',
|
|
'location' => 'nullable',
|
|
'body' => 'nullable',
|
|
'calendarId' => 'required',
|
|
'customFields' => 'nullable',
|
|
] );
|
|
} catch ( Exception $e ) {
|
|
return response()->json( [ 'error' => $e->getMessage() ], 400 );
|
|
}
|
|
|
|
$calendar = Calendar::find( $validatedData['calendarId'] );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
$start = ( new DateTimeImmutable( $validatedData['start'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
$end = ( new DateTimeImmutable( $validatedData['end'] ) )->setTimezone( new DateTimeZone( 'UTC' ) );
|
|
if ( $start->getTimestamp() === $end->getTimestamp() ) {
|
|
$end = $start->add( new DateInterval( 'P1D' ) )->sub( new DateInterval( 'PT1S' ) );
|
|
}
|
|
$isAllDay = DateTimeRange::isAllDay( $start, $end );
|
|
|
|
$customFieldsData = $validatedData['customFields'] ?? [];
|
|
$customFields = $calendar->custom_fields['fields'] ?? [];
|
|
$processedCustomFields = [];
|
|
|
|
foreach ( $customFields as $field ) {
|
|
$fieldId = 'custom_field_' . $field['id'];
|
|
if ( isset( $customFieldsData[ $fieldId ] ) ) {
|
|
$processedCustomFields[ $fieldId ] = $customFieldsData[ $fieldId ];
|
|
} else if ( $field['required'] ) {
|
|
return response()->json( [ 'error' => 'Required custom field missing: ' . $field['name'] ], 400 );
|
|
}
|
|
}
|
|
|
|
if ( empty( $calendar->title_template ) ) {
|
|
$calendar->title_template = $request->input( 'title' );
|
|
}
|
|
|
|
$uid = null;
|
|
if ( $calendar->type === 'normal' ) {
|
|
$calendarItem = new CalendarItem();
|
|
$calendarItem->calendar_id = $validatedData['calendarId'];
|
|
$calendarItem->title = $this->processTemplate( $calendar->title_template, Conversation::find( $conversation ), $calendar, $processedCustomFields );
|
|
$calendarItem->start = $start;
|
|
$calendarItem->end = $end;
|
|
$calendarItem->is_all_day = $isAllDay;
|
|
$calendarItem->location = $validatedData['location'] ?? '';
|
|
$calendarItem->body = $validatedData['body'] ?? '';
|
|
|
|
// Merge all custom fields
|
|
$customFields = $calendarItem->custom_fields;
|
|
if ( ! is_array( $customFields ) ) {
|
|
$customFields = [];
|
|
}
|
|
$mergedCustomFields = array_merge( $customFields, [
|
|
'conversation_id' => $conversation,
|
|
'author_id' => auth()->user()->id,
|
|
], $processedCustomFields );
|
|
|
|
$calendarItem->custom_fields = $mergedCustomFields;
|
|
|
|
$calendarItem->save();
|
|
|
|
$uid = $calendarItem->id;
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$fullUrl = $calendar->custom_fields['url'];
|
|
$baseUrl = substr( $fullUrl, 0, strpos( $fullUrl, '/', 8 ) );
|
|
$remainingUrl = substr( $fullUrl, strpos( $fullUrl, '/', 8 ) );
|
|
$caldavClient = new CalDAV( $baseUrl, $calendar->custom_fields['username'], $calendar->custom_fields['password'] );
|
|
|
|
$uid = $this->GUID();
|
|
|
|
if ( ! is_array( $validatedData['body'] ) ) {
|
|
$validatedData['body'] = [ 'body' => empty( $validatedData['body'] ) ? '-' : $validatedData['body'] ];
|
|
}
|
|
|
|
// Merge all custom fields
|
|
$mergedCustomFields = array_merge( [
|
|
'conversation_id' => $conversation,
|
|
'author_id' => auth()->user()->id,
|
|
], $processedCustomFields );
|
|
|
|
$validatedData['body']['custom_fields'] = $mergedCustomFields;
|
|
|
|
// Add mapping for used custom fields
|
|
$validatedData['body']['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$mergedCustomFields
|
|
);
|
|
|
|
$response = $caldavClient->createEvent(
|
|
$remainingUrl,
|
|
$uid,
|
|
$this->processTemplate( $calendar->title_template, Conversation::find( $conversation ), $calendar, $processedCustomFields ),
|
|
$validatedData['body'],
|
|
$start,
|
|
$end,
|
|
$isAllDay,
|
|
$validatedData['location']
|
|
);
|
|
|
|
if ( $response['statusCode'] < 200 || $response['statusCode'] > 300 ) {
|
|
Log::error( 'Error creating event', $response );
|
|
|
|
return response()->json( [ 'error' => 'Error creating event' ], 500 );
|
|
}
|
|
|
|
$calendar->getExternalContent( true );
|
|
}
|
|
|
|
if ( $uid !== null ) {
|
|
$conversation = Conversation::find( $conversation );
|
|
if ( $conversation !== null ) {
|
|
$action_type = CalendarItem::ACTION_TYPE_ADD_TO_CALENDAR;
|
|
$created_by_user_id = auth()->user()->id;
|
|
|
|
// Store the event UID for better permalink support
|
|
$meta = [
|
|
'calendar_item_id' => $uid,
|
|
'calendar_id' => $calendar->id,
|
|
'calendar_type' => $calendar->type,
|
|
'start' => ( new DateTimeImmutable( $validatedData['start'] ) )->setTimezone( new DateTimeZone( 'UTC' ) )->format( DATE_ATOM ),
|
|
];
|
|
|
|
// For external calendars, also store the event UID
|
|
if ( $calendar->type === 'caldav' || $calendar->type === 'ics' ) {
|
|
$meta['event_uid'] = $uid;
|
|
}
|
|
|
|
Thread::create( $conversation, Thread::TYPE_LINEITEM, '', [
|
|
'user_id' => $conversation->user_id,
|
|
'created_by_user_id' => $created_by_user_id,
|
|
'action_type' => $action_type,
|
|
'source_via' => Thread::PERSON_USER,
|
|
'source_type' => Thread::SOURCE_TYPE_WEB,
|
|
'meta' => $meta,
|
|
] );
|
|
}
|
|
}
|
|
|
|
return response()->json( [ 'status' => 'success', 'ok' => true, 'message' => 'Event created successfully' ] );
|
|
}
|
|
|
|
/**
|
|
* Get a calendar
|
|
*
|
|
* @param int $id
|
|
*
|
|
* @return JsonResponse
|
|
*/
|
|
public function getCalendar( $id ) {
|
|
$calendar = Calendar::find( $id );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
return response()->json( $calendar );
|
|
}
|
|
|
|
/**
|
|
* Get a single event by ID - optimized endpoint for permalinks
|
|
*
|
|
* @param string $eventId The event ID to retrieve
|
|
*
|
|
* @return JsonResponse
|
|
*/
|
|
public function getEventById( string $eventId ): JsonResponse {
|
|
try {
|
|
// Check if tables exist first
|
|
if ( ! \Schema::hasTable( 'calendars' ) || ! \Schema::hasTable( 'calendar_items' ) ) {
|
|
return response()->json( [ 'error' => 'Calendar module not properly initialized' ], 500 );
|
|
}
|
|
|
|
$calendars = Calendar::all();
|
|
|
|
foreach ( $calendars as $calendar ) {
|
|
if ( $calendar->enabled === false ) {
|
|
continue;
|
|
}
|
|
|
|
$permissions = $calendar->permissionsForCurrentUser();
|
|
if ( $permissions === null || ! $permissions['showInCalendar'] ) {
|
|
continue;
|
|
}
|
|
|
|
// For normal calendars, try to find the specific event by ID only
|
|
if ( $calendar->type === 'normal' ) {
|
|
$event = CalendarItem::where( 'calendar_id', $calendar->id )
|
|
->where( 'id', $eventId )
|
|
->first();
|
|
|
|
if ( $event ) {
|
|
$eventData = json_decode( $event->toJson(), true );
|
|
|
|
// Ensure calendar ID is present
|
|
$eventData['calendarId'] = $calendar->id;
|
|
|
|
// Generate mapping for used custom fields
|
|
if ( isset( $eventData['custom_fields'] ) && is_array( $eventData['custom_fields'] ) ) {
|
|
$eventData['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$eventData['custom_fields']
|
|
);
|
|
}
|
|
|
|
// Set default timezone
|
|
$defaultTimezone = config( 'app.timezone' );
|
|
$eventData['start'] = ( new DateTimeImmutable( $eventData['start'], new DateTimeZone( 'UTC' ) ) )
|
|
->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
$eventData['end'] = ( new DateTimeImmutable( $eventData['end'], new DateTimeZone( 'UTC' ) ) )
|
|
->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
|
|
// Ensure IDs are set
|
|
if ( empty( $eventData['id'] ) && ! empty( $eventData['uid'] ) ) {
|
|
$eventData['id'] = $eventData['uid'];
|
|
}
|
|
if ( empty( $eventData['uid'] ) && ! empty( $eventData['id'] ) ) {
|
|
$eventData['uid'] = $eventData['id'];
|
|
}
|
|
if ( empty( $eventData['title'] ) ) {
|
|
$eventData['title'] = '';
|
|
}
|
|
|
|
return response()->json( $eventData );
|
|
}
|
|
}
|
|
|
|
// For external calendars, use optimized single event lookup
|
|
if ( $calendar->type === 'ics' || $calendar->type === 'caldav' ) {
|
|
try {
|
|
$event = $calendar->findEventById( $eventId );
|
|
|
|
if ( $event ) {
|
|
// Add calendar ID to the event data
|
|
$event['calendarId'] = $calendar->id;
|
|
$event['calendar_id'] = $calendar->id;
|
|
|
|
// Generate mapping for used custom fields
|
|
if ( isset( $event['custom_fields'] ) && is_array( $event['custom_fields'] ) ) {
|
|
$event['custom_fields_mapping'] = $this->generateCustomFieldMapping(
|
|
$calendar->custom_fields,
|
|
$event['custom_fields']
|
|
);
|
|
}
|
|
|
|
// Ensure timezone conversion is done
|
|
$defaultTimezone = config( 'app.timezone' );
|
|
$event['start'] = ( new DateTimeImmutable( $event['start'], new DateTimeZone( 'UTC' ) ) )
|
|
->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
$event['end'] = ( new DateTimeImmutable( $event['end'], new DateTimeZone( 'UTC' ) ) )
|
|
->setTimezone( new DateTimeZone( $defaultTimezone ) )->format( 'Y-m-d H:i:s' );
|
|
|
|
return response()->json( $event );
|
|
}
|
|
} catch ( \Exception $e ) {
|
|
// Log and continue checking other calendars
|
|
\Log::error( 'Error fetching event from external calendar: ' . $e->getMessage() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Event not found in any calendar
|
|
return response()->json( [ 'error' => 'Event not found' ], 404 );
|
|
|
|
} catch ( \Exception $e ) {
|
|
\Log::error( 'Error in getEventById: ' . $e->getMessage() );
|
|
return response()->json( [ 'error' => 'Failed to retrieve event' ], 500 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create an event from an attachment
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return JsonResponse
|
|
* @throws Exception
|
|
*/
|
|
public function createEventFromAttachment( Request $request ) {
|
|
try {
|
|
$validatedData = $request->validate( [
|
|
'attachmentId' => 'required',
|
|
'conversationId' => 'required',
|
|
'calendarId' => 'required',
|
|
] );
|
|
} catch ( Exception $e ) {
|
|
return response()->json( [ 'error' => $e->getMessage() ], 400 );
|
|
}
|
|
|
|
$calendar = Calendar::find( $validatedData['calendarId'] );
|
|
if ( ! $calendar ) {
|
|
return response()->json( [ 'error' => 'Calendar not found' ], 404 );
|
|
}
|
|
|
|
$conversation = Conversation::find( $validatedData['conversationId'] );
|
|
if ( ! $conversation ) {
|
|
return response()->json( [ 'error' => 'Conversation not found' ], 404 );
|
|
}
|
|
|
|
$attachment = Attachment::find( $validatedData['attachmentId'] );
|
|
if ( ! $attachment ) {
|
|
return response()->json( [ 'error' => 'Attachment not found' ], 404 );
|
|
}
|
|
$file = $attachment->getLocalFilePath();
|
|
|
|
$ical = new ICal( $file );
|
|
/** @var Event[] $events */
|
|
$events = $ical->events();
|
|
|
|
$refetchCalendarIds = [];
|
|
|
|
//add conversation url to body
|
|
$conversationUrl = route( 'conversations.view', [ 'id' => $conversation->id ] );
|
|
|
|
foreach ( $events as $event ) {
|
|
$start = DateTimeImmutable::createFromMutable( $ical->iCalDateToDateTime( $event->dtstart_array[3] )->setTimezone( new DateTimeZone( 'UTC' ) ) );
|
|
if ( ! empty( $event->dtend ) ) {
|
|
$end = DateTimeImmutable::createFromMutable( $ical->iCalDateToDateTime( $event->dtend_array[3] )->setTimezone( new DateTimeZone( 'UTC' ) ) );
|
|
} else {
|
|
if ( ! empty( $event->duration ) ) {
|
|
$end = $start->add( new DateInterval( $event->duration ) );
|
|
} else {
|
|
$end = $start->add( new DateInterval( 'PT1H' ) );
|
|
}
|
|
}
|
|
if ( $start->getTimestamp() === $end->getTimestamp() ) {
|
|
$end = $start->add( new DateInterval( 'P1D' ) )->sub( new DateInterval( 'PT1S' ) );
|
|
}
|
|
$isAllDay = DateTimeRange::isAllDay( $start, $end );
|
|
|
|
if ( $calendar->type === 'normal' ) {
|
|
$calendarItem = new CalendarItem();
|
|
$calendarItem->calendar_id = $validatedData['calendarId'];
|
|
$calendarItem->title = $event->summary;
|
|
$calendarItem->start = $start;
|
|
$calendarItem->end = $end;
|
|
$calendarItem->is_all_day = $isAllDay;
|
|
$calendarItem->location = $event->location ?? '';
|
|
$calendarItem->body = $event->description ?? '';
|
|
$customFields = $calendarItem->custom_fields;
|
|
if ( ! is_array( $customFields ) ) {
|
|
$customFields = [];
|
|
}
|
|
$customFields['conversation_id'] = $conversation->id;
|
|
$customFields['author_id'] = auth()->user()->id;
|
|
$calendarItem->custom_fields = $customFields;
|
|
$calendarItem->save();
|
|
|
|
$uid = $calendarItem->id;
|
|
} else if ( $calendar->type === 'caldav' ) {
|
|
$fullUrl = $calendar->custom_fields['url'];
|
|
$baseUrl = substr( $fullUrl, 0, strpos( $fullUrl, '/', 8 ) );
|
|
$remainingUrl = substr( $fullUrl, strpos( $fullUrl, '/', 8 ) );
|
|
$caldavClient = new CalDAV( $baseUrl, $calendar->custom_fields['username'], $calendar->custom_fields['password'] );
|
|
|
|
$uid = $event->uid ?? $this->GUID();
|
|
|
|
$description = [
|
|
'body' => empty( $event->description ) ? '-' : $event->description,
|
|
'custom_fields' => [
|
|
'conversation_id' => $conversation->id,
|
|
'author_id' => auth()->user()->id,
|
|
],
|
|
];
|
|
|
|
$response = $caldavClient->createEvent( $remainingUrl, $uid, $event->summary, $description, $start, $end, $isAllDay, $event->location );
|
|
if ( $response['statusCode'] < 200 || $response['statusCode'] > 300 ) {
|
|
Log::error( 'Error creating event', $response );
|
|
|
|
return response()->json( [ 'error' => 'Error creating event', $response['body'], 'data' => [ $uid, $event->summary, $description, $start, $end, $event->location ] ], 500 );
|
|
}
|
|
$refetchCalendarIds[] = $calendar->id;
|
|
}
|
|
|
|
if ( $uid !== null ) {
|
|
$action_type = CalendarItem::ACTION_TYPE_ADD_TO_CALENDAR;
|
|
$created_by_user_id = auth()->user()->id;
|
|
Thread::create( $conversation, Thread::TYPE_LINEITEM, '', [
|
|
'user_id' => $conversation->user_id,
|
|
'created_by_user_id' => $created_by_user_id,
|
|
'action_type' => $action_type,
|
|
'source_via' => Thread::PERSON_USER,
|
|
'source_type' => Thread::SOURCE_TYPE_WEB,
|
|
'meta' => [
|
|
'calendar_item_id' => $uid,
|
|
'calendar_id' => $calendar->id,
|
|
'start' => $start->format( DATE_ATOM ),
|
|
],
|
|
] );
|
|
}
|
|
}
|
|
|
|
$refetchCalendarIds = array_values( array_unique( $refetchCalendarIds ) );
|
|
foreach ( $refetchCalendarIds as $refetchCalendarId ) {
|
|
UpdateExternalCalendarJob::dispatch( $refetchCalendarId );
|
|
}
|
|
|
|
return response()->json( [ 'status' => 'success', 'ok' => true, 'message' => 'Event created successfully' ] );
|
|
}
|
|
|
|
}
|