status != 'active') { return $this->sendError([ 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking') ]); } $calendar = $slot->calendar; $startDate = $request->get('start_date', gmdate('Y-m-d H:i:s')); $timeZone = $request->get('timezone', 'UTC'); if (!$timeZone) { $timeZone = 'UTC'; } $timeSlotService = TimeSlotServiceHandler::initService($calendar, $slot); if (is_wp_error($timeSlotService)) { return TimeSlotServiceHandler::sendError($timeSlotService, $slot, $timezone); } $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone); if(is_wp_error($availableSpots)) { return [ 'available_slots' => [], 'timezone' => $timeZone, 'invalid_dates' => true, 'max_lookup_date' => $slot->getMaxLookUpDate(), ]; } return [ 'available_slots' => array_filter($availableSpots), 'timezone' => $timeZone, 'max_lookup_date' => $slot->getMaxLookUpDate(), ]; } public function createBooking(Request $request, $eventId) { $calendarEvent = CalendarSlot::findOrfail($eventId); if ($calendarEvent->status != 'active') { return $this->sendError([ 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking') ]); } $postedData = $request->all(); $rules = [ 'name' => 'required', 'email' => 'required|email', 'timezone' => 'required', 'event_time' => 'required' ]; $messages = [ 'name.required' => __("Please enter attendee's name", 'fluent-booking'), 'email.required' => __("Please enter attendee's email address", 'fluent-booking'), 'email.email' => __('Please provide a valid email address', 'fluent-booking'), 'timezone.required' => __('Please select the timezone', 'fluent-booking'), 'event_time.required' => __('Please select a date and time', 'fluent-booking') ]; $locationType = Arr::get($postedData, 'location_type'); if ($calendarEvent->isPhoneRequired()) { $rules['location_description'] = 'required'; $messages['location_description.required'] = __("Please provide attendee's phone number", 'fluent-booking'); } else if ($calendarEvent->isAddressRequired()) { $rules['location_description'] = 'required'; $messages['location_description.required'] = __("Please provide attendee's address", 'fluent-booking'); } if ($additionalGuests = array_filter(Arr::get($postedData, 'guests', []))) { $postedData['guests'] = array_map('sanitize_email', $additionalGuests); } $requiredFields = array_filter($calendarEvent->getMeta('booking_fields', []), function ($field) { return Arr::isTrue($field, 'required') && Arr::isTrue($field, 'enabled') && (Arr::get($field, 'name') == 'message' || Arr::get($field, 'name') == 'guests'); }); foreach ($requiredFields as $field) { if (empty($rules[$field['name']])) { $rules[$field['name']] = 'required'; $messages[$field['name'] . '.required'] = __('This field is required', 'fluent-booking'); } } $validationConfig = apply_filters('fluent_booking/schedule_validation_rules_data', [ 'rules' => $rules, 'messages' => $messages ], $postedData, $calendarEvent); $app = App::getInstance(); $validator = $app->validator->make($postedData, $validationConfig['rules'], $validationConfig['messages']); if ($validator->validate()->fails()) { wp_send_json([ 'message' => __('Please fill up the required data', 'fluent-booking'), 'errors' => $validator->errors() ], 422); return; } $customFieldsData = BookingFieldService::getCustomFieldsData(Arr::get($postedData, 'custom_fields', []), $calendarEvent); $customFieldsData = apply_filters('fluent_booking/schedule_custom_field_data', $customFieldsData, $calendarEvent); if (is_wp_error($customFieldsData)) { wp_send_json([ 'message' => $customFieldsData->get_error_message(), 'errors' => $customFieldsData->get_error_data() ], 422); return; } $duration = $calendarEvent->getDuration(Arr::get($postedData, 'duration', null)); $timezone = Arr::get($postedData, 'timezone', 'UTC'); $startDateTime = DateTimeHelper::convertToUtc($postedData['event_time'], $timezone); $endDateTime = gmdate('Y-m-d H:i:s', strtotime($startDateTime) + ($duration * 60)); $bookingData = [ 'person_time_zone' => sanitize_text_field($timezone), 'start_time' => $startDateTime, 'name' => sanitize_text_field($postedData['name']), 'email' => sanitize_email($postedData['email']), 'message' => sanitize_textarea_field(wp_unslash(Arr::get($postedData, 'message', ''))), 'phone' => sanitize_textarea_field(Arr::get($postedData, 'phone_number', '')), 'address' => sanitize_textarea_field(Arr::get($postedData, 'address', '')), 'ip_address' => Helper::getIp(), 'status' => sanitize_text_field($postedData['status']), 'source' => 'admin', 'event_type' => $calendarEvent->event_type, 'slot_minutes' => $duration ]; $eventLocations = []; $locationSettings = $calendarEvent->location_settings; foreach ($locationSettings as $index => $location) { $eventLocations[$location['type']] = $location; } $locationDetails['type'] = $locationType; if ($locationType == 'phone_organizer') { $locationDetails['description'] = $eventLocations[$locationType]['host_phone_number']; } else if ($locationType == 'phone_guest') { $bookingData['phone'] = Arr::get($postedData, 'location_description', ''); } else if ($locationType == 'in_person_guest') { $locationDetails['description'] = Arr::get($postedData, 'location_description', ''); } else if (in_array($locationType, ['custom', 'in_person_organizer'])) { $locationDetails['description'] = $eventLocations[$locationType]['description']; } else if (in_array($locationType, ['google_meet', 'online_meeting', 'zoom_meeting', 'ms_teams'])) { $locationDetails['description'] = $eventLocations[$locationType]['meeting_link']; } $bookingData['location_details'] = $locationDetails; if ($sourceUrl = Arr::get($postedData, 'source_url', '')) { $bookingData['source_url'] = sanitize_url($sourceUrl); } if ($additionalGuests) { $guestField = BookingFieldService::getBookingFieldByName($calendarEvent, 'guests'); $guestLimit = Arr::get($guestField, 'limit', 10); $bookingData['additional_guests'] = array_slice($additionalGuests, 0, $guestLimit); } if ($hostUserId = Arr::get($postedData, 'host_user_id', null)) { $bookingData['host_user_id'] = (int)$hostUserId; } $hostIds = null; if ($calendarEvent->isTeamEvent() && !$hostUserId) { $hostIds = $calendarEvent->getHostIdsSortedByBookings($startDateTime); $bookingData['host_user_id'] = $hostIds[0]; } // Check if the time is available or not for this slot if (!Arr::isTrue($postedData, 'ignore_availability')) { $timeSlotService = TimeSlotServiceHandler::initService($calendarEvent->calendar, $calendarEvent); if (is_wp_error($timeSlotService)) { return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone); } $isSpotAvailable = $timeSlotService->isSpotAvailable($startDateTime, $endDateTime, $duration, $hostUserId); if (!$isSpotAvailable) { wp_send_json([ 'message' => __('This selected time slot is not available. Maybe someone booked the spot just a few seconds ago.', 'fluent-booking') ], 422); } if ($calendarEvent->isTeamEvent() && !$hostUserId) { $bookingData['host_user_id'] = $timeSlotService->hostUserId; } } do_action('fluent_booking/before_creating_schedule', $bookingData, $postedData, $calendarEvent); try { $booking = BookingService::createBooking($bookingData, $calendarEvent, $customFieldsData); do_action('fluent_booking/after_creating_schedule', $booking, $postedData, $calendarEvent); } catch (\Exception $e) { wp_send_json([ 'message' => $e->getMessage() ], $e->getCode()); } return [ 'booking' => $booking, 'message' => __('Booking has been created', 'fluent-booking'), ]; } public function getEvent(Request $request) { $eventId = $request->get('event_id'); $calendarEvent = CalendarSlot::query()->find($eventId); if (!$calendarEvent || $calendarEvent->status != 'active') { wp_send_json([ 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking') ], 422); } $calendar = $calendarEvent->calendar; if (!$calendar) { return $this->sendError([ 'message' => __('Calendar not found', 'fluent-booking') ]); } $calendarEventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent); $startDate = $request->get('start_date'); if (!$startDate) { $startDate = gmdate('Y-m-d H:i:s'); } $timeZone = $request->get('timezone'); if (!$timeZone) { $timeZone = wp_timezone_string(); } if (!in_array($timeZone, \DateTimeZone::listIdentifiers())) { $timeZone = $calendar->author_timezone; } $duration = $calendarEvent->getDuration($request->get('duration')); $hostId = $request->get('host_id', null); $timeSlotService = TimeSlotServiceHandler::initService($calendar, $calendarEvent); if (is_wp_error($timeSlotService)) { return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone); } $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone, $duration, $hostId); if (is_wp_error($availableSpots)) { wp_send_json([ 'available_slots' => [], 'calendar_event' => $calendarEventVars, 'timezone' => $timeZone, 'invalid_dates' => true ], 200); } $availableSpots = apply_filters('fluent_booking/available_slots_for_view', array_filter($availableSpots), $calendarEvent, $calendar, $timeZone, $duration); return [ 'calendar_event' => $calendarEventVars, 'available_slots' => $availableSpots ]; } public function getBookings(Request $request) { $userData = get_userdata(get_current_user_id()); $userEmail = $userData ? $userData->user_email : null; if (!$userEmail) { return $this->sendError([ 'message' => __('Please login to view your bookings', 'fluent-booking') ]); } $perPage = intval($request->get('per_page', 10)); $bookingPeriod = sanitize_text_field($request->get('period', 'all')); $bookingQuery = Booking::query()->with('calendar_event') ->where('email', $userEmail) ->orderBy('start_time', 'DESC') ->applyComputedStatus($bookingPeriod); $calendarIds = $request->get('calendar_ids', []); if (!in_array('all', $calendarIds)) { $calendarIds = array_map('intval', $calendarIds); $bookingQuery->whereIn('calendar_id', $calendarIds); } do_action_ref_array('fluent_booking/bookings_query', [&$bookingQuery]); $totalBookings = $bookingQuery->count(); $bookings = $bookingQuery->limit($perPage)->get(); $formattedBookings = []; foreach ($bookings as $booking) { $formattedBookings[] = [ 'id' => $booking->id, 'person_time_zone' => $booking->person_time_zone, 'status' => ucfirst($booking->status), 'payment_status' => $booking->payment_status, 'booking_title' => $booking->getBookingTitle(true), 'author_name' => $booking->getHostDetails(false)['name'], 'booking_date' => DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'date'), 'booking_time' => DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time'), ]; } return [ 'bookings' => $formattedBookings, 'total' => $totalBookings ]; } }