PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / trunk
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution vtrunk
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
← All changes | app/Http/Controllers/BookingController.php +87 -31 1.5.23trunk View file →
@@ -6,9 +6,8 @@
6 6 use FluentBooking\App\Models\Booking;
7 7 use FluentBooking\App\Models\CalendarSlot;
8 8 use FluentBooking\App\Services\BookingService;
9 9 use FluentBooking\App\Services\DateTimeHelper;
10 -use FluentBooking\App\Services\TimeSlotService;
11 10 use FluentBooking\App\Services\BookingFieldService;
12 11 use FluentBooking\App\Hooks\Handlers\FrontEndHandler;
13 12 use FluentBooking\App\Hooks\Handlers\TimeSlotServiceHandler;
14 13 use FluentBooking\Framework\Http\Request\Request;
@@ -37,9 +36,9 @@
37 36
38 37 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $slot);
39 38
40 39 if (is_wp_error($timeSlotService)) {
41 - return TimeSlotServiceHandler::sendError($timeSlotService, $slot, $timezone);
40 + return TimeSlotServiceHandler::sendError($timeSlotService, $slot, $timeZone);
42 41 }
43 42
44 43 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone);
45 44
@@ -88,19 +87,31 @@
88 87
89 88 $locationType = Arr::get($postedData, 'location_type');
90 89
91 90 if ($calendarEvent->isPhoneRequired()) {
92 - $rules['location_description'] = 'required';
91 + $rules['location_description'] = ['required', $this->validPhoneNumberRule()];
93 92 $messages['location_description.required'] = __('Please provide attendee\'s phone number', 'fluent-booking');
94 93 } else if ($calendarEvent->isAddressRequired()) {
95 94 $rules['location_description'] = 'required';
96 95 $messages['location_description.required'] = __('Please provide attendee\'s address', 'fluent-booking');
96 + } else if ($locationType === 'phone_guest') {
97 + $rules['location_description'] = [$this->validPhoneNumberRule()];
97 98 }
98 99
99 - if ($additionalGuests = array_filter(Arr::get($postedData, 'guests', []))) {
100 - $postedData['guests'] = array_map('sanitize_email', $additionalGuests);
100 + $additionalGuests = Arr::get($postedData, 'guests', []);
101 + if (!empty($additionalGuests)) {
102 + if ($calendarEvent->isMultiGuestEvent()) {
103 + $additionalGuests = $this->sanitize_mapped_data($additionalGuests);
104 + $additionalGuests = array_values(array_filter($additionalGuests, function ($guest) {
105 + return Arr::get($guest, 'name') && Arr::get($guest, 'email');
106 + }));
107 + } else {
108 + $additionalGuests = array_filter(array_map('sanitize_email', $additionalGuests));
109 + }
101 110 }
102 111
112 + $postedData['guests'] = $additionalGuests;
113 +
103 114 $requiredFields = array_filter($calendarEvent->getMeta('booking_fields', []), function ($field) {
104 115 return Arr::isTrue($field, 'required') && Arr::isTrue($field, 'enabled') && (Arr::get($field, 'name') == 'message' || Arr::get($field, 'name') == 'guests');
105 116 });
106 117
@@ -138,27 +149,27 @@
138 149 return;
139 150 }
140 151
141 152 $duration = $calendarEvent->getDuration(Arr::get($postedData, 'duration', null));
142 - $timezone = Arr::get($postedData, 'timezone', 'UTC');
153 + $timezone = sanitize_text_field($postedData['timezone']);
143 154
144 - $startDateTime = DateTimeHelper::convertToUtc($postedData['event_time'], $timezone);
155 + $startDateTime = DateTimeHelper::convertToUtc(sanitize_text_field($postedData['event_time']), $timezone);
145 156 $endDateTime = gmdate('Y-m-d H:i:s', strtotime($startDateTime) + ($duration * 60));
146 157
147 - $bookingData = [
148 - 'person_time_zone' => sanitize_text_field($timezone),
158 + $bookingData = apply_filters('fluent_booking/initialize_booking_data', [
159 + 'person_time_zone' => $timezone,
149 160 'start_time' => $startDateTime,
150 161 'name' => sanitize_text_field($postedData['name']),
151 162 'email' => sanitize_email($postedData['email']),
152 163 'message' => sanitize_textarea_field(wp_unslash(Arr::get($postedData, 'message', ''))),
153 - 'phone' => sanitize_textarea_field(Arr::get($postedData, 'phone_number', '')),
164 + 'phone' => sanitize_text_field(Arr::get($postedData, 'phone_number', '')),
154 165 'address' => sanitize_textarea_field(Arr::get($postedData, 'address', '')),
155 166 'ip_address' => Helper::getIp(),
156 - 'status' => sanitize_text_field($postedData['status']),
157 - 'source' => 'admin',
167 + 'status' => sanitize_text_field(Arr::get($postedData, 'status', 'scheduled')),
168 + 'source' => Arr::get($postedData, 'source') == 'admin' ? 'admin' : 'web',
158 169 'event_type' => $calendarEvent->event_type,
159 170 'slot_minutes' => $duration
160 - ];
171 + ], $postedData, $calendarEvent);
161 172
162 173 $eventLocations = [];
163 174 $locationSettings = $calendarEvent->location_settings;
164 175 foreach ($locationSettings as $index => $location) {
@@ -168,15 +179,16 @@
168 179 $locationDetails['type'] = $locationType;
169 180 if ($locationType == 'phone_organizer') {
170 181 $locationDetails['description'] = $eventLocations[$locationType]['host_phone_number'];
171 182 } else if ($locationType == 'phone_guest') {
172 - $bookingData['phone'] = Arr::get($postedData, 'location_description', '');
183 + $bookingData['phone'] = sanitize_text_field(Arr::get($postedData, 'location_description', ''));
173 184 } else if ($locationType == 'in_person_guest') {
174 - $locationDetails['description'] = Arr::get($postedData, 'location_description', '');
185 + $locationDetails['description'] = sanitize_textarea_field(Arr::get($postedData, 'location_description', ''));
175 186 } else if (in_array($locationType, ['custom', 'in_person_organizer'])) {
176 187 $locationDetails['description'] = $eventLocations[$locationType]['description'];
177 188 } else if (in_array($locationType, ['google_meet', 'online_meeting', 'zoom_meeting', 'ms_teams'])) {
178 - $locationDetails['description'] = $eventLocations[$locationType]['meeting_link'];
189 + $locationDetails['description'] = Arr::get($eventLocations[$locationType], 'meeting_link', '');
190 + $locationDetails['online_platform_link'] = $locationDetails['description'];
179 191 }
180 192
181 193 $bookingData['location_details'] = $locationDetails;
182 194
@@ -183,25 +195,27 @@
183 195 if ($sourceUrl = Arr::get($postedData, 'source_url', '')) {
184 196 $bookingData['source_url'] = sanitize_url($sourceUrl);
185 197 }
186 198
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 - }
199 + if ($hostUserId = Arr::get($postedData, 'host_user_id', null)) {
200 + $hostUserId = (int) $hostUserId;
192 201
193 - if ($hostUserId = Arr::get($postedData, 'host_user_id', null)) {
194 - $bookingData['host_user_id'] = (int)$hostUserId;
202 + if (!in_array($hostUserId, array_map('intval', $calendarEvent->getHostIds()), true)) {
203 + return $this->sendError([
204 + 'message' => __('The selected host is not a host of this event', 'fluent-booking')
205 + ], 422);
206 + }
207 +
208 + $bookingData['host_user_id'] = $hostUserId;
195 209 }
196 210
197 211 $hostIds = null;
198 - if ($calendarEvent->isTeamEvent() && !$hostUserId) {
212 + if ($calendarEvent->isRoundRobin() && !$hostUserId) {
199 213 $hostIds = $calendarEvent->getHostIdsSortedByBookings($startDateTime);
200 214 $bookingData['host_user_id'] = $hostIds[0];
201 215 }
202 216
203 - // Check if the time is available or not for this slot
217 + $availableSpot = false;
204 218 if (!Arr::isTrue($postedData, 'ignore_availability')) {
205 219 $timeSlotService = TimeSlotServiceHandler::initService($calendarEvent->calendar, $calendarEvent);
206 220
207 221 if (is_wp_error($timeSlotService)) {
@@ -215,13 +229,23 @@
215 229 'message' => __('This selected time slot is not available. Maybe someone booked the spot just a few seconds ago.', 'fluent-booking')
216 230 ], 422);
217 231 }
218 232
219 - if ($calendarEvent->isTeamEvent() && !$hostUserId) {
233 + if ($calendarEvent->isRoundRobin() && !$hostUserId) {
220 234 $bookingData['host_user_id'] = $timeSlotService->hostUserId;
221 235 }
222 236 }
223 237
238 + if ($additionalGuests) {
239 + $guestField = BookingFieldService::getBookingFieldByName($calendarEvent, 'guests');
240 + $guestLimit = Arr::get($guestField, 'limit', 10);
241 + if ($calendarEvent->isMultiGuestEvent() && $availableSpot) {
242 + $remaining = Arr::get($availableSpot, 'remaining', $calendarEvent->getMaxBookingPerSlot());
243 + $guestLimit = min($remaining, $guestLimit) - 1;
244 + }
245 + $bookingData['additional_guests'] = array_slice($additionalGuests, 0, $guestLimit);
246 + }
247 +
224 248 do_action('fluent_booking/before_creating_schedule', $bookingData, $postedData, $calendarEvent);
225 249
226 250 try {
227 251 $booking = BookingService::createBooking($bookingData, $calendarEvent, $customFieldsData);
@@ -238,8 +262,20 @@
238 262 'message' => __('Booking has been created', 'fluent-booking'),
239 263 ];
240 264 }
241 265
266 + /**
267 + * @return \Closure
268 + */
269 + private function validPhoneNumberRule()
270 + {
271 + return function ($attribute, $value) {
272 + if (!empty($value) && !Helper::isValidPhoneNumber($value)) {
273 + return __('Please provide a valid phone number', 'fluent-booking');
274 + }
275 + };
276 + }
277 +
242 278 public function getEvent(Request $request, $eventId)
243 279 {
244 280 $calendarEvent = CalendarSlot::find($eventId);
245 281
@@ -277,13 +313,23 @@
277 313
278 314 $duration = $calendarEvent->getDuration($request->get('duration'));
279 315
280 316 $hostId = $request->get('host_id', null);
281 -
317 +
318 + if ($hostId) {
319 + $hostId = (int) $hostId;
320 +
321 + if (!in_array($hostId, array_map('intval', $calendarEvent->getHostIds()), true)) {
322 + wp_send_json([
323 + 'message' => __('The selected host is not a host of this event', 'fluent-booking')
324 + ], 422);
325 + }
326 + }
327 +
282 328 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $calendarEvent);
283 329
284 330 if (is_wp_error($timeSlotService)) {
285 - return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
331 + return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timeZone);
286 332 }
287 333
288 334 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone, $duration, $hostId);
289 335
@@ -321,10 +367,10 @@
321 367 $bookingPeriod = sanitize_text_field($request->get('period', 'all'));
322 368
323 369 $bookingQuery = Booking::query()->with('calendar_event')
324 370 ->where('email', $userEmail)
325 - ->orderBy('start_time', 'DESC')
326 - ->applyComputedStatus($bookingPeriod);
371 + ->applyComputedStatus($bookingPeriod)
372 + ->applyBookingOrderByStatus($bookingPeriod);
327 373
328 374 $calendarIds = $request->get('calendar_ids', []);
329 375
330 376 if (!in_array('all', $calendarIds)) {
@@ -347,9 +393,9 @@
347 393 'payment_status' => $booking->payment_status,
348 394 'booking_title' => $booking->getBookingTitle(true),
349 395 'author_name' => $booking->getHostDetails(false)['name'],
350 396 'booking_date' => DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'date'),
351 - 'booking_time' => DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time'),
397 + 'booking_time' => DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time'),
352 398 ];
353 399 }
354 400
355 401 return [
@@ -355,6 +401,16 @@
355 401 return [
356 402 'bookings' => $formattedBookings,
357 403 'total' => $totalBookings
358 404 ];
405 + }
406 +
407 + private static function sanitize_mapped_data($settings)
408 + {
409 + $sanitizerMap = [
410 + 'name' => 'sanitize_text_field',
411 + 'email' => 'sanitize_email',
412 + ];
413 +
414 + return Helper::fcal_backend_sanitizer($settings, $sanitizerMap);
359 415 }
360 416 }