PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.25
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.25
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.25, at app/Http/Controllers/BookingController.php

361 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 $availableSpot = $timeSlotService->isSpotAvailable($startDateTime, $endDateTime, $duration, $hostUserId);
212
213 if (!$availableSpot) {
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, $eventId)
243 {
244 $calendarEvent = CalendarSlot::find($eventId);
245
246 if (!$calendarEvent || $calendarEvent->status != 'active') {
247 wp_send_json([
248 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
249 ], 422);
250 }
251
252 $calendar = $calendarEvent->calendar;
253
254 if (!$calendar) {
255 return $this->sendError([
256 'message' => __('Calendar not found', 'fluent-booking')
257 ]);
258 }
259
260 $calendarEventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent);
261
262 $startDate = $request->get('start_date');
263
264 if (!$startDate) {
265 $startDate = gmdate('Y-m-d H:i:s');
266 }
267
268 $timeZone = $request->get('timezone');
269
270 if (!$timeZone) {
271 $timeZone = wp_timezone_string();
272 }
273
274 if (!in_array($timeZone, \DateTimeZone::listIdentifiers())) {
275 $timeZone = $calendar->author_timezone;
276 }
277
278 $duration = $calendarEvent->getDuration($request->get('duration'));
279
280 $hostId = $request->get('host_id', null);
281
282 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $calendarEvent);
283
284 if (is_wp_error($timeSlotService)) {
285 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
286 }
287
288 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone, $duration, $hostId);
289
290 if (is_wp_error($availableSpots)) {
291 wp_send_json([
292 'available_slots' => [],
293 'calendar_event' => $calendarEventVars,
294 'timezone' => $timeZone,
295 'invalid_dates' => true
296 ], 200);
297 }
298
299 $availableSpots = apply_filters('fluent_booking/available_slots_for_view', array_filter($availableSpots), $calendarEvent, $calendar, $timeZone, $duration);
300
301 return [
302 'calendar_event' => $calendarEventVars,
303 'available_slots' => $availableSpots
304 ];
305 }
306
307 public function getBookings(Request $request)
308 {
309 $userData = get_userdata(get_current_user_id());
310
311 $userEmail = $userData ? $userData->user_email : null;
312
313 if (!$userEmail) {
314 return $this->sendError([
315 'message' => __('Please login to view your bookings', 'fluent-booking')
316 ]);
317 }
318
319 $perPage = intval($request->get('per_page', 10));
320
321 $bookingPeriod = sanitize_text_field($request->get('period', 'all'));
322
323 $bookingQuery = Booking::query()->with('calendar_event')
324 ->where('email', $userEmail)
325 ->orderBy('start_time', 'DESC')
326 ->applyComputedStatus($bookingPeriod);
327
328 $calendarIds = $request->get('calendar_ids', []);
329
330 if (!in_array('all', $calendarIds)) {
331 $calendarIds = array_map('intval', $calendarIds);
332 $bookingQuery->whereIn('calendar_id', $calendarIds);
333 }
334
335 do_action_ref_array('fluent_booking/bookings_query', [&$bookingQuery]);
336
337 $totalBookings = $bookingQuery->count();
338
339 $bookings = $bookingQuery->limit($perPage)->get();
340
341 $formattedBookings = [];
342 foreach ($bookings as $booking) {
343 $formattedBookings[] = [
344 'id' => $booking->id,
345 'person_time_zone' => $booking->person_time_zone,
346 'status' => ucfirst($booking->status),
347 'payment_status' => $booking->payment_status,
348 'booking_title' => $booking->getBookingTitle(true),
349 'author_name' => $booking->getHostDetails(false)['name'],
350 'booking_date' => DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'date'),
351 'booking_time' => DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time'),
352 ];
353 }
354
355 return [
356 'bookings' => $formattedBookings,
357 'total' => $totalBookings
358 ];
359 }
360 }
361