PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.0
2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 1.7.2 All 33 releases
fluent-booking / app / Http / Controllers / BookingController.php

BookingController.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.5.0, at app/Http/Controllers/BookingController.php

363 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Http\Controllers;
4
5 use FluentBooking\App\App;
6 use FluentBooking\App\Models\Booking;
7 use FluentBooking\App\Models\CalendarSlot;
8 use FluentBooking\App\Services\BookingService;
9 use FluentBooking\App\Services\DateTimeHelper;
10 use FluentBooking\App\Services\TimeSlotService;
11 use FluentBooking\App\Services\BookingFieldService;
12 use FluentBooking\App\Hooks\Handlers\FrontEndHandler;
13 use FluentBooking\App\Hooks\Handlers\TimeSlotServiceHandler;
14 use FluentBooking\Framework\Http\Request\Request;
15 use FluentBooking\App\Services\Helper;
16 use FluentBooking\Framework\Support\Arr;
17
18 class BookingController extends Controller
19 {
20 public function getSlots(Request $request, $slotId)
21 {
22 $slot = CalendarSlot::findOrfail($slotId);
23
24 if ($slot->status != 'active') {
25 return $this->sendError([
26 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
27 ]);
28 }
29
30 $calendar = $slot->calendar;
31 $startDate = $request->get('start_date', gmdate('Y-m-d H:i:s'));
32 $timeZone = $request->get('timezone', 'UTC');
33
34 if (!$timeZone) {
35 $timeZone = 'UTC';
36 }
37
38 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $slot);
39
40 if (is_wp_error($timeSlotService)) {
41 return TimeSlotServiceHandler::sendError($timeSlotService, $slot, $timezone);
42 }
43
44 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone);
45
46 if(is_wp_error($availableSpots)) {
47 return [
48 'available_slots' => [],
49 'timezone' => $timeZone,
50 'invalid_dates' => true,
51 'max_lookup_date' => $slot->getMaxLookUpDate(),
52 ];
53 }
54
55 return [
56 'available_slots' => array_filter($availableSpots),
57 'timezone' => $timeZone,
58 'max_lookup_date' => $slot->getMaxLookUpDate(),
59 ];
60 }
61
62 public function createBooking(Request $request, $eventId)
63 {
64 $calendarEvent = CalendarSlot::findOrfail($eventId);
65
66 if ($calendarEvent->status != 'active') {
67 return $this->sendError([
68 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
69 ]);
70 }
71
72 $postedData = $request->all();
73
74 $rules = [
75 'name' => 'required',
76 'email' => 'required|email',
77 'timezone' => 'required',
78 'event_time' => 'required'
79 ];
80
81 $messages = [
82 'name.required' => __("Please enter attendee's name", 'fluent-booking'),
83 'email.required' => __("Please enter attendee's email address", 'fluent-booking'),
84 'email.email' => __('Please provide a valid email address', 'fluent-booking'),
85 'timezone.required' => __('Please select the timezone', 'fluent-booking'),
86 'event_time.required' => __('Please select a date and time', 'fluent-booking')
87 ];
88
89 $locationType = Arr::get($postedData, 'location_type');
90
91 if ($calendarEvent->isPhoneRequired()) {
92 $rules['location_description'] = 'required';
93 $messages['location_description.required'] = __("Please provide attendee's phone number", 'fluent-booking');
94 } else if ($calendarEvent->isAddressRequired()) {
95 $rules['location_description'] = 'required';
96 $messages['location_description.required'] = __("Please provide attendee's address", 'fluent-booking');
97 }
98
99 if ($additionalGuests = array_filter(Arr::get($postedData, 'guests', []))) {
100 $postedData['guests'] = array_map('sanitize_email', $additionalGuests);
101 }
102
103 $requiredFields = array_filter($calendarEvent->getMeta('booking_fields', []), function ($field) {
104 return Arr::isTrue($field, 'required') && Arr::isTrue($field, 'enabled') && (Arr::get($field, 'name') == 'message' || Arr::get($field, 'name') == 'guests');
105 });
106
107 foreach ($requiredFields as $field) {
108 if (empty($rules[$field['name']])) {
109 $rules[$field['name']] = 'required';
110 $messages[$field['name'] . '.required'] = __('This field is required', 'fluent-booking');
111 }
112 }
113
114 $validationConfig = apply_filters('fluent_booking/schedule_validation_rules_data', [
115 'rules' => $rules,
116 'messages' => $messages
117 ], $postedData, $calendarEvent);
118
119 $app = App::getInstance();
120
121 $validator = $app->validator->make($postedData, $validationConfig['rules'], $validationConfig['messages']);
122 if ($validator->validate()->fails()) {
123 wp_send_json([
124 'message' => __('Please fill up the required data', 'fluent-booking'),
125 'errors' => $validator->errors()
126 ], 422);
127 return;
128 }
129
130 $customFieldsData = BookingFieldService::getCustomFieldsData(Arr::get($postedData, 'custom_fields', []), $calendarEvent);
131 $customFieldsData = apply_filters('fluent_booking/schedule_custom_field_data', $customFieldsData, $calendarEvent);
132
133 if (is_wp_error($customFieldsData)) {
134 wp_send_json([
135 'message' => $customFieldsData->get_error_message(),
136 'errors' => $customFieldsData->get_error_data()
137 ], 422);
138 return;
139 }
140
141 $duration = $calendarEvent->getDuration(Arr::get($postedData, 'duration', null));
142 $timezone = Arr::get($postedData, 'timezone', 'UTC');
143
144 $startDateTime = DateTimeHelper::convertToUtc($postedData['event_time'], $timezone);
145 $endDateTime = gmdate('Y-m-d H:i:s', strtotime($startDateTime) + ($duration * 60));
146
147 $bookingData = [
148 'person_time_zone' => sanitize_text_field($timezone),
149 'start_time' => $startDateTime,
150 'name' => sanitize_text_field($postedData['name']),
151 'email' => sanitize_email($postedData['email']),
152 'message' => sanitize_textarea_field(wp_unslash(Arr::get($postedData, 'message', ''))),
153 'phone' => sanitize_textarea_field(Arr::get($postedData, 'phone_number', '')),
154 'address' => sanitize_textarea_field(Arr::get($postedData, 'address', '')),
155 'ip_address' => Helper::getIp(),
156 'status' => sanitize_text_field($postedData['status']),
157 'source' => 'admin',
158 'event_type' => $calendarEvent->event_type,
159 'slot_minutes' => $duration
160 ];
161
162 $eventLocations = [];
163 $locationSettings = $calendarEvent->location_settings;
164 foreach ($locationSettings as $index => $location) {
165 $eventLocations[$location['type']] = $location;
166 }
167
168 $locationDetails['type'] = $locationType;
169 if ($locationType == 'phone_organizer') {
170 $locationDetails['description'] = $eventLocations[$locationType]['host_phone_number'];
171 } else if ($locationType == 'phone_guest') {
172 $bookingData['phone'] = Arr::get($postedData, 'location_description', '');
173 } else if ($locationType == 'in_person_guest') {
174 $locationDetails['description'] = Arr::get($postedData, 'location_description', '');
175 } else if (in_array($locationType, ['custom', 'in_person_organizer'])) {
176 $locationDetails['description'] = $eventLocations[$locationType]['description'];
177 } else if (in_array($locationType, ['google_meet', 'online_meeting', 'zoom_meeting', 'ms_teams'])) {
178 $locationDetails['description'] = $eventLocations[$locationType]['meeting_link'];
179 }
180
181 $bookingData['location_details'] = $locationDetails;
182
183 if ($sourceUrl = Arr::get($postedData, 'source_url', '')) {
184 $bookingData['source_url'] = sanitize_url($sourceUrl);
185 }
186
187 if ($additionalGuests) {
188 $guestField = BookingFieldService::getBookingFieldByName($calendarEvent, 'guests');
189 $guestLimit = Arr::get($guestField, 'limit', 10);
190 $bookingData['additional_guests'] = array_slice($additionalGuests, 0, $guestLimit);
191 }
192
193 if ($hostUserId = Arr::get($postedData, 'host_user_id', null)) {
194 $bookingData['host_user_id'] = (int)$hostUserId;
195 }
196
197 $hostIds = null;
198 if ($calendarEvent->isTeamEvent() && !$hostUserId) {
199 $hostIds = $calendarEvent->getHostIdsSortedByBookings($startDateTime);
200 $bookingData['host_user_id'] = $hostIds[0];
201 }
202
203 // Check if the time is available or not for this slot
204 if (!Arr::isTrue($postedData, 'ignore_availability')) {
205 $timeSlotService = TimeSlotServiceHandler::initService($calendarEvent->calendar, $calendarEvent);
206
207 if (is_wp_error($timeSlotService)) {
208 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
209 }
210
211 $isSpotAvailable = $timeSlotService->isSpotAvailable($startDateTime, $endDateTime, $duration, $hostUserId);
212
213 if (!$isSpotAvailable) {
214 wp_send_json([
215 'message' => __('This selected time slot is not available. Maybe someone booked the spot just a few seconds ago.', 'fluent-booking')
216 ], 422);
217 }
218
219 if ($calendarEvent->isTeamEvent() && !$hostUserId) {
220 $bookingData['host_user_id'] = $timeSlotService->hostUserId;
221 }
222 }
223
224 do_action('fluent_booking/before_creating_schedule', $bookingData, $postedData, $calendarEvent);
225
226 try {
227 $booking = BookingService::createBooking($bookingData, $calendarEvent, $customFieldsData);
228
229 do_action('fluent_booking/after_creating_schedule', $booking, $postedData, $calendarEvent);
230 } catch (\Exception $e) {
231 wp_send_json([
232 'message' => $e->getMessage()
233 ], $e->getCode());
234 }
235
236 return [
237 'booking' => $booking,
238 'message' => __('Booking has been created', 'fluent-booking'),
239 ];
240 }
241
242 public function getEvent(Request $request)
243 {
244 $eventId = $request->get('event_id');
245
246 $calendarEvent = CalendarSlot::query()->find($eventId);
247
248 if (!$calendarEvent || $calendarEvent->status != 'active') {
249 wp_send_json([
250 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
251 ], 422);
252 }
253
254 $calendar = $calendarEvent->calendar;
255
256 if (!$calendar) {
257 return $this->sendError([
258 'message' => __('Calendar not found', 'fluent-booking')
259 ]);
260 }
261
262 $calendarEventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent);
263
264 $startDate = $request->get('start_date');
265
266 if (!$startDate) {
267 $startDate = gmdate('Y-m-d H:i:s');
268 }
269
270 $timeZone = $request->get('timezone');
271
272 if (!$timeZone) {
273 $timeZone = wp_timezone_string();
274 }
275
276 if (!in_array($timeZone, \DateTimeZone::listIdentifiers())) {
277 $timeZone = $calendar->author_timezone;
278 }
279
280 $duration = $calendarEvent->getDuration($request->get('duration'));
281
282 $hostId = $request->get('host_id', null);
283
284 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $calendarEvent);
285
286 if (is_wp_error($timeSlotService)) {
287 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
288 }
289
290 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone, $duration, $hostId);
291
292 if (is_wp_error($availableSpots)) {
293 wp_send_json([
294 'available_slots' => [],
295 'calendar_event' => $calendarEventVars,
296 'timezone' => $timeZone,
297 'invalid_dates' => true
298 ], 200);
299 }
300
301 $availableSpots = apply_filters('fluent_booking/available_slots_for_view', array_filter($availableSpots), $calendarEvent, $calendar, $timeZone, $duration);
302
303 return [
304 'calendar_event' => $calendarEventVars,
305 'available_slots' => $availableSpots
306 ];
307 }
308
309 public function getBookings(Request $request)
310 {
311 $userData = get_userdata(get_current_user_id());
312
313 $userEmail = $userData ? $userData->user_email : null;
314
315 if (!$userEmail) {
316 return $this->sendError([
317 'message' => __('Please login to view your bookings', 'fluent-booking')
318 ]);
319 }
320
321 $perPage = intval($request->get('per_page', 10));
322
323 $bookingPeriod = sanitize_text_field($request->get('period', 'all'));
324
325 $bookingQuery = Booking::query()->with('calendar_event')
326 ->where('email', $userEmail)
327 ->orderBy('start_time', 'DESC')
328 ->applyComputedStatus($bookingPeriod);
329
330 $calendarIds = $request->get('calendar_ids', []);
331
332 if (!in_array('all', $calendarIds)) {
333 $calendarIds = array_map('intval', $calendarIds);
334 $bookingQuery->whereIn('calendar_id', $calendarIds);
335 }
336
337 do_action_ref_array('fluent_booking/bookings_query', [&$bookingQuery]);
338
339 $totalBookings = $bookingQuery->count();
340
341 $bookings = $bookingQuery->limit($perPage)->get();
342
343 $formattedBookings = [];
344 foreach ($bookings as $booking) {
345 $formattedBookings[] = [
346 'id' => $booking->id,
347 'person_time_zone' => $booking->person_time_zone,
348 'status' => ucfirst($booking->status),
349 'payment_status' => $booking->payment_status,
350 'booking_title' => $booking->getBookingTitle(true),
351 'author_name' => $booking->getHostDetails(false)['name'],
352 'booking_date' => DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'date'),
353 'booking_time' => DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time'),
354 ];
355 }
356
357 return [
358 'bookings' => $formattedBookings,
359 'total' => $totalBookings
360 ];
361 }
362 }
363