PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.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 All 34 releases
← All changes | app/Hooks/Handlers/FrontEndHandler.php +36 -201 2.2.5 → 2.5.0 View file →
@@ -8,8 +8,9 @@
8 8 use FluentBooking\App\Models\CalendarSlot;
9 9 use FluentBooking\App\Services\BookingFieldService;
10 10 use FluentBooking\App\Services\BookingService;
11 11 use FluentBooking\App\Services\DateTimeHelper;
12 +use FluentBooking\App\Services\PublicTransStrings;
12 13 use FluentBooking\App\Services\Helper;
13 14 use FluentBooking\App\Services\LandingPage\LandingPageHandler;
14 15 use FluentBooking\App\Services\LandingPage\LandingPageHelper;
15 16 use FluentBooking\App\Hooks\Handlers\TimeSlotServiceHandler;
@@ -14,9 +15,9 @@
14 15 use FluentBooking\App\Services\LandingPage\LandingPageHelper;
15 16 use FluentBooking\App\Hooks\Handlers\TimeSlotServiceHandler;
16 17 use FluentBooking\App\Services\CalendarEventService;
17 18 use FluentBooking\App\Services\LocationService;
18 -use FluentBooking\App\Services\PermissionManager;
19 +use FluentBooking\App\Services\RescheduleService;
19 20 use FluentBooking\App\Services\CurrenciesHelper;
20 21 use FluentBooking\Framework\Support\Arr;
21 22 use FluentBooking\App\Vite;
22 23
@@ -447,87 +448,28 @@
447 448 'message' => __('Invalid rescheduling request', 'fluent-booking')
448 449 ], 422);
449 450 }
450 451
451 - // The booking must be rescheduled against its own event. Reject mixed-object
452 - // requests where the posted event_id differs from the booking's event so the
453 - // availability validation cannot be performed under a different event than the
454 - // one actually being modified.
455 - if ((int) $existingBooking->event_id !== (int) $calendarEvent->id) {
456 - wp_send_json([
457 - 'message' => __('Invalid rescheduling request', 'fluent-booking')
458 - ], 422);
459 - }
452 + $result = RescheduleService::reschedule(
453 + $existingBooking,
454 + $calendarEvent,
455 + $bookingData['start_time'],
456 + $bookingData['person_time_zone'],
457 + [
458 + 'reason' => Arr::get($postedData, 'rescheduling_reason', ''),
459 + 'host_user_id' => Arr::get($bookingData, 'host_user_id'),
460 + 'source' => __('Web UI', 'fluent-booking')
461 + ]
462 + );
460 463
461 - $rescheduleBy = 'guest';
462 - $hostIds = $existingBooking->getHostIds();
463 - if (in_array(get_current_user_id(), $hostIds) || PermissionManager::userCan(['manage_all_data', 'manage_all_bookings'])) {
464 - $rescheduleBy = 'host';
465 - }
466 -
467 - $existingBooking->updateMeta('rescheduled_by_type', $rescheduleBy);
468 -
469 - if ($rescheduleBy == 'guest' && !$existingBooking->canReschedule()) {
464 + if (is_wp_error($result)) {
470 465 wp_send_json([
471 - 'message' => $existingBooking->getRescheduleMessage()
466 + 'message' => $result->get_error_message()
472 467 ], 422);
473 468 }
474 469
475 - if ($bookingData['start_time'] == $existingBooking->start_time) {
476 - wp_send_json([
477 - 'message' => __('Sorry! you can not reschedule to the same time.', 'fluent-booking')
478 - ], 422);
479 - }
470 + $existingBooking = $result;
480 471
481 - $endDateTime = gmdate('Y-m-d H:i:s', strtotime($bookingData['start_time']) + ($existingBooking->slot_minutes * 60)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
482 -
483 - $previousBooking = clone $existingBooking;
484 -
485 - if ($existingBooking->isMultiGuestBooking()) {
486 - // Need to handle group booking type here
487 - // check for existing group
488 - $parent = Booking::where('status', 'scheduled')
489 - ->where('event_id', $existingBooking->event_id)
490 - ->where('start_time', $bookingData['start_time'])
491 - ->orderBy('id', 'ASC')
492 - ->first();
493 -
494 - if ($parent) {
495 - $existingBooking->group_id = $parent->group_id;
496 - } else {
497 - $existingBooking->group_id = Helper::getNextBookingGroup();
498 - }
499 - }
500 -
501 - if ($existingBooking->isRoundRobinBooking()) {
502 - $hostId = $bookingData['host_user_id'];
503 - $existingBooking->host_user_id = $hostId;
504 - $existingBooking->hosts()->sync([$hostId]);
505 - }
506 -
507 - $existingBooking->start_time = $bookingData['start_time'];
508 - $existingBooking->person_time_zone = $bookingData['person_time_zone'];
509 - $existingBooking->end_time = $endDateTime;
510 - $existingBooking->save();
511 -
512 - $existingBooking->updateMeta('previous_meeting_time', $previousBooking->start_time);
513 -
514 - $reschedulingMessage = sanitize_textarea_field(Arr::get($postedData, 'rescheduling_reason'));
515 - if ($reschedulingMessage) {
516 - $existingBooking->updateMeta('reschedule_reason', $reschedulingMessage);
517 - }
518 -
519 - do_action('fluent_booking/log_booking_activity', [
520 - 'booking_id' => $existingBooking->id,
521 - 'type' => 'info',
522 - 'status' => 'closed',
523 - 'title' => __('Meeting Rescheduled', 'fluent-booking'),
524 - /* translators: %1$s is the user who rescheduled the meeting, %2$s is the previous date and time in UTC. */
525 - 'description' => sprintf(__('Meeting has been rescheduled by %1$s from Web UI. Previous date time: %2$s (UTC)', 'fluent-booking'), $rescheduleBy, $previousBooking->start_time)
526 - ]);
527 -
528 - do_action('fluent_booking/after_booking_rescheduled', $existingBooking, $previousBooking, $calendarEvent);
529 -
530 472 add_filter('fluent_booking/schedule_receipt_data', function ($data) {
531 473 $data['title'] = __('Your meeting has been rescheduled', 'fluent-booking');
532 474 return $data;
533 475 });
@@ -613,92 +555,12 @@
613 555 'ajaxurl' => admin_url('admin-ajax.php'),
614 556 'timezones' => DateTimeHelper::getFlatGroupedTimeZones(),
615 557 'current_person' => $currentPerson,
616 558 'start_day' => $startDay,
617 - 'i18' => [
618 - 'Timezone' => __('Timezone', 'fluent-booking'),
619 - 'Day' => __('Day', 'fluent-booking'),
620 - 'Days' => __('Days', 'fluent-booking'),
621 - 'Hour' => __('Hour', 'fluent-booking'),
622 - 'Hours' => __('Hours', 'fluent-booking'),
623 - 'Minute' => __('Minute', 'fluent-booking'),
624 - 'Minutes' => __('Minutes', 'fluent-booking'),
625 - 'week' => __('week', 'fluent-booking'),
626 - 'month' => __('month', 'fluent-booking'),
627 - 'year' => __('year', 'fluent-booking'),
628 - 'weeks' => __('weeks', 'fluent-booking'),
629 - 'months' => __('months', 'fluent-booking'),
630 - 'years' => __('years', 'fluent-booking'),
631 - 'Every' => __('Every', 'fluent-booking'),
632 - 'for' => __('for', 'fluent-booking'),
633 - 'Number of Occurrences' => __('Number of Occurrences', 'fluent-booking'),
634 - 'occurrence' => __('occurrence', 'fluent-booking'),
635 - 'occurrences' => __('occurrences', 'fluent-booking'),
636 - 'You can only book up to' => __('You can only book up to', 'fluent-booking'),
637 - 'at a time' => __('at a time', 'fluent-booking'),
638 - 'Enter Details' => __('Enter Details', 'fluent-booking'),
639 - 'Summary' => __('Summary', 'fluent-booking'),
640 - 'Payment Details' => __('Payment Details', 'fluent-booking'),
641 - 'Item' => __('Item', 'fluent-booking'),
642 - 'Price' => __('Price', 'fluent-booking'),
643 - 'Quantity' => __('Quantity', 'fluent-booking'),
644 - 'Subtotal:' => __('Subtotal:', 'fluent-booking'),
645 - 'Total:' => __('Total:', 'fluent-booking'),
646 - 'Total Payment' => __('Total Payment', 'fluent-booking'),
647 - 'Payment Method' => __('Payment Method', 'fluent-booking'),
648 - 'Pay Now' => __('Pay Now', 'fluent-booking'),
649 - 'processing' => __('Processing', 'fluent-booking'),
650 - 'date_time_config' => DateTimeHelper::getI18nDateTimeConfig(),
651 - 'Country' => __('Country', 'fluent-booking'),
652 - '12h' => _x('12h', 'date time format switch', 'fluent-booking'),
653 - '24h' => _x('24h', 'date time format switch', 'fluent-booking'),
654 - 'spots left' => _x('spots left', 'for how many spots left for available booking', 'fluent-booking'),
655 - 'spots remaining' => _x('spots remaining', 'for how many spots remaining for available booking', 'fluent-booking'),
656 - 'Next' => _x('Next', 'Booking form spot selection', 'fluent-booking'),
657 - 'Select on the Next Step' => __('Select on the Next Step', 'fluent-booking'),
658 - 'location options' => __('location options', 'fluent-booking'),
659 - 'Your address' => __('Your address', 'fluent-booking'),
660 - 'Organizer Phone Number' => __('Organizer Phone Number', 'fluent-booking'),
661 - 'In Person (Attendee Address)' => __('In Person (Attendee Address)', 'fluent-booking'),
662 - 'In Person (Organizer Address)' => __('In Person (Organizer Address)', 'fluent-booking'),
663 - 'Attendee Phone Number' => __('Attendee Phone Number', 'fluent-booking'),
664 - 'Google Meet' => __('Google Meet', 'fluent-booking'),
665 - 'Zoom Meeting' => __('Zoom Meeting', 'fluent-booking'),
666 - 'Online Meeting' => __('Online Meeting', 'fluent-booking'),
667 - 'Phone Call' => __('Phone Call', 'fluent-booking'),
668 - 'Processing...' => __('Processing...', 'fluent-booking'),
669 - 'Loading Payment Processor...' => __('Loading Payment Processor...', 'fluent-booking'),
670 - 'PM' => __('PM', 'fluent-booking'),
671 - 'AM' => __('AM', 'fluent-booking'),
672 - 'Name' => __('Name', 'fluent-booking'),
673 - 'Email' => __('Email', 'fluent-booking'),
674 - 'Date' => __('Date', 'fluent-booking'),
675 - 'Time' => __('Time', 'fluent-booking'),
676 - 'per occurrence' => __('per occurrence', 'fluent-booking'),
677 - 'per guest' => __('per guest', 'fluent-booking'),
678 - 'Add guest' => __('Add guest', 'fluent-booking'),
679 - 'Add guests' => __('Add guests', 'fluent-booking'),
680 - 'Add another' => __('Add another', 'fluent-booking'),
681 - 'Choose File' => __('Choose File', 'fluent-booking'),
682 - 'This field is required.' => __('This field is required.', 'fluent-booking'),
683 - 'No availability in' => __('No availability in', 'fluent-booking'),
684 - 'View next month' => __('View next month', 'fluent-booking'),
685 - 'View previous month' => __('View previous month', 'fluent-booking'),
686 - 'Back to Date Selection' => __('Back to Date Selection', 'fluent-booking'),
687 - 'Go to previous page' => __('Go to previous page', 'fluent-booking'),
688 - 'Remove this time slot' => __('Remove this time slot', 'fluent-booking'),
689 - 'Remove coupon' => __('Remove coupon', 'fluent-booking'),
690 - 'Select this time' => __('Select this time', 'fluent-booking'),
691 - 'Confirm Time' => __('Confirm Time', 'fluent-booking'),
692 - '12 hour time format' => __('12 hour time format', 'fluent-booking'),
693 - '24 hour time format' => __('24 hour time format', 'fluent-booking'),
694 - 'No_payment_method_description' => __('No activated payment method found. If you are an admin please check the event payment settings', 'fluent-booking'),
695 - 'Please fill up the required data' => __('Please fill up the required data', 'fluent-booking'),
696 - 'Please select a valid payment method' => __('Please select a valid payment method', 'fluent-booking'),
697 - 'Please Select' => __('Please Select', 'fluent-booking'),
698 - 'Something is wrong!' => __('Something is wrong!', 'fluent-booking'),
699 - 'Requires Confirmation' => __('Requires Confirmation', 'fluent-booking'),
700 - ],
559 + // Generated from the i18() calls in resources/public; see scripts/i18n.js.
560 + 'i18' => array_merge(PublicTransStrings::getStrings(), [
561 + 'date_time_config' => DateTimeHelper::getI18nDateTimeConfig(),
562 + ]),
701 563 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme', 'system-default'),
702 564 'currency_settings' => CurrenciesHelper::getGlobalCurrencySettings()
703 565 ];
704 566
@@ -911,10 +773,12 @@
911 773 if (is_wp_error($timeSlotService)) {
912 774 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
913 775 }
914 776
915 - $availableSpot = $timeSlotService->isSpotAvailable($bookingData['start_time'], $bookingData['end_time'], $duration);
777 + $isSlotLocked = Helper::lockRoundRobinSlot($calendarEvent, $bookingData['start_time'], $bookingData['end_time']);
916 778
779 + $availableSpot = $isSlotLocked ? $timeSlotService->isSpotAvailable($bookingData['start_time'], $bookingData['end_time'], $duration) : false;
780 +
917 781 if (!$availableSpot) {
918 782 wp_send_json([
919 783 'message' => __('This selected time slot is not available. Maybe someone booked the spot just a few seconds ago.', 'fluent-booking')
920 784 ], 422);
@@ -985,18 +849,28 @@
985 849 $request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
986 850
987 851 $eventId = (int)Arr::get($request, 'event_id');
988 852
989 - $rescheduling = Arr::get($request, 'rescheduling', 'no');
853 + $reschedulingHash = sanitize_text_field(Arr::get($request, 'rescheduling_hash', '')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
990 854
991 855 $calendarEvent = $eventId ? CalendarSlot::find($eventId) : null;
992 856
993 - if (!$calendarEvent || ($calendarEvent->status != 'active' && $rescheduling == 'no')) {
857 + if (!$calendarEvent) {
994 858 wp_send_json([
995 859 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
996 860 ], 422);
997 861 }
998 862
863 + if ($calendarEvent->status != 'active') {
864 + $existingBooking = $reschedulingHash ? Booking::where('hash', $reschedulingHash)->first() : null;
865 +
866 + if (!$existingBooking || (int)$existingBooking->event_id !== (int)$calendarEvent->id) {
867 + wp_send_json([
868 + 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
869 + ], 422);
870 + }
871 + }
872 +
999 873 $calendar = $calendarEvent->calendar;
1000 874 $startDate = sanitize_text_field(Arr::get($request, 'start_date')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1001 875
1002 876 if (!$startDate) {
@@ -1063,9 +937,9 @@
1063 937 'min_bookable_date' => $calendarEvent->getMinBookableDateTime(),
1064 938 'is_display_spots' => $calendarEvent->isDisplaySpots(),
1065 939 'duration' => $calendarEvent->getDefaultDuration(),
1066 940 'title' => $calendarEvent->title,
1067 - 'location_settings' => $this->sanitizePublicLocationSettings($calendarEvent->location_settings),
941 + 'location_settings' => LocationService::sanitizePublicLocationSettings($calendarEvent->location_settings),
1068 942 'location_icon_html' => $calendarEvent->location_icon_html,
1069 943 'description' => $calendarEvent->description,
1070 944 'pre_selects' => null,
1071 945 'settings' => $this->sanitizePublicEventSettings($calendarEvent->settings),
@@ -1099,47 +973,8 @@
1099 973 $eventVars['team_member_profiles'] = $calendarEvent->getAuthorProfiles(true);
1100 974 }
1101 975
1102 976 return apply_filters('fluent_booking/public_event_vars', $eventVars, $calendarEvent);
1103 - }
1104 -
1105 - private function sanitizePublicLocationSettings($locationSettings)
1106 - {
1107 - if (!is_array($locationSettings)) {
1108 - return [];
1109 - }
1110 -
1111 - $safe = [];
1112 - foreach ($locationSettings as $location) {
1113 - if (!is_array($location)) {
1114 - continue;
1115 - }
1116 -
1117 - $type = Arr::get($location, 'type');
1118 - $displayOnBooking = Arr::get($location, 'display_on_booking') === 'yes';
1119 -
1120 - $sanitized = [
1121 - 'type' => $type,
1122 - 'title' => Arr::get($location, 'title'),
1123 - 'display_on_booking' => Arr::get($location, 'display_on_booking', 'no'),
1124 - ];
1125 -
1126 - // Only expose host-private fields when the host explicitly opted
1127 - // in to display them before booking.
1128 - if ($displayOnBooking) {
1129 - if ($type === 'online_meeting') {
1130 - $sanitized['meeting_link'] = Arr::get($location, 'meeting_link');
1131 - } elseif ($type === 'phone_organizer') {
1132 - $sanitized['host_phone_number'] = Arr::get($location, 'host_phone_number');
1133 - } elseif (in_array($type, ['in_person_organizer', 'custom'], true)) {
1134 - $sanitized['description'] = Arr::get($location, 'description');
1135 - }
1136 - }
1137 -
1138 - $safe[] = $sanitized;
1139 - }
1140 -
1141 - return $safe;
1142 977 }
1143 978
1144 979 private function sanitizePublicEventSettings($settings)
1145 980 {