PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.6.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.6.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
fluent-booking / app / Hooks / Handlers / FrontEndHandler.php

FrontEndHandler.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.6.0, at app/Hooks/Handlers/FrontEndHandler.php

1,113 lines 50.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\Hooks\Handlers;
4
5 use FluentBooking\App\App;
6 use FluentBooking\App\Models\Booking;
7 use FluentBooking\App\Models\Calendar;
8 use FluentBooking\App\Models\CalendarSlot;
9 use FluentBooking\App\Services\BookingFieldService;
10 use FluentBooking\App\Services\BookingService;
11 use FluentBooking\App\Services\DateTimeHelper;
12 use FluentBooking\App\Services\Helper;
13 use FluentBooking\App\Services\LandingPage\LandingPageHandler;
14 use FluentBooking\App\Hooks\Handlers\TimeSlotServiceHandler;
15 use FluentBooking\App\Services\CalendarEventService;
16 use FluentBooking\App\Services\LocationService;
17 use FluentBooking\App\Services\PermissionManager;
18 use FluentBooking\Framework\Support\Arr;
19
20 class FrontEndHandler
21 {
22 public function register()
23 {
24 add_shortcode('fluent_booking', [$this, 'handleBookingShortcode']);
25
26 add_shortcode('fluent_booking_team', [$this, 'handleTeamShortcode']);
27
28 add_shortcode('fluent_booking_calendar', [$this, 'handleCalendarShortcode']);
29
30 add_shortcode('fluent_booking_lists', [$this, 'handleBookingListsShortcode']);
31
32 add_shortcode('fluent_booking_receipt', [$this, 'handleReceiptShortcode']);
33
34 add_action('wp_ajax_fluent_cal_schedule_meeting', [$this, 'ajaxScheduleMeeting']);
35 add_action('wp_ajax_nopriv_fluent_cal_schedule_meeting', [$this, 'ajaxScheduleMeeting']);
36
37 add_action('wp_ajax_fcal_cancel_meeting', [$this, 'ajaxHandleCancelMeeting']);
38 add_action('wp_ajax_nopriv_fcal_cancel_meeting', [$this, 'ajaxHandleCancelMeeting']);
39
40 add_action('wp_ajax_fluent_cal_get_available_dates', [$this, 'ajaxGetAvailableDates']);
41 add_action('wp_ajax_nopriv_fluent_cal_get_available_dates', [$this, 'ajaxGetAvailableDates']);
42
43 add_action('fluent_booking/starting_scheduling_ajax', [$this, 'handleRescheduling']);
44 }
45
46 public function handleBookingShortcode($atts, $content)
47 {
48 $atts = shortcode_atts([
49 'id' => 0,
50 'theme' => 'light',
51 'disable_author' => 'no',
52 'hash' => ''
53 ], $atts);
54
55 if (!$atts['id'] && !$atts['hash']) {
56 return '';
57 }
58
59 $calendarEvent = CalendarSlot::find($atts['id']);
60 if (!$calendarEvent) {
61 $calendarEvent = CalendarSlot::where('hash', $atts['hash'])->first();
62 if (!$calendarEvent) {
63 return __('Calendar event not found', 'fluent-booking');
64 }
65 }
66
67 $calendar = $calendarEvent->calendar;
68 if (!$calendar) {
69 return __('Calendar not found', 'fluent-booking');
70 }
71
72 $assetUrl = App::getInstance('url.assets');
73
74 $localizeData = $this->getCalendarEventVars($calendar, $calendarEvent);
75 $localizeData['disable_author'] = $atts['disable_author'] == 'yes';
76 $localizeData['theme'] = $atts['theme'];
77
78 if (BookingFieldService::hasPhoneNumberField($localizeData['form_fields'])) {
79 wp_enqueue_script('fluent-booking-phone-field', $assetUrl . 'public/js/phone-field.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
80 }
81
82 wp_enqueue_script('fluent-booking-public', $assetUrl . 'public/js/app.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
83
84 $this->loadGlobalVars();
85 wp_localize_script(
86 'fluent-booking-public',
87 'fcal_public_vars_' . $calendar->id . '_' . $calendarEvent->id,
88 $localizeData,
89 );
90
91 return App::make('view')->make('public.calendar', [
92 'calenderEvent' => $calendarEvent,
93 'theme' => $atts['theme']
94 ]);
95 }
96
97 public function handleTeamShortcode($atts, $content)
98 {
99 $atts = shortcode_atts([
100 'event_ids' => '',
101 'title' => '',
102 'description' => '',
103 'logo_url' => ''
104 ], $atts);
105
106 if (!$atts['event_ids']) {
107 return '';
108 }
109
110 $eventIds = array_filter(array_map('intval', explode(',', $atts['event_ids'])));
111
112 if (empty($eventIds)) {
113 return '';
114 }
115
116 $events = CalendarSlot::query()->whereIn('id', $eventIds)
117 ->where('status', 'active')
118 ->get();
119
120 $calendarIds = [];
121 $calendarEvents = [];
122
123 foreach ($events as $event) {
124 $calendarIds[] = $event->calendar_id;
125 if (!isset($calendarEvents[$event->calendar_id])) {
126 $calendarEvents[$event->calendar_id] = [];
127 }
128 $event = CalendarEventService::processEvent($event);
129 $calendarEvents[$event->calendar_id][] = $event;
130 }
131
132 $calendars = Calendar::query()->whereIn('id', $calendarIds)->get();
133
134 foreach ($calendars as $calendar) {
135 $calendar->activeEvents = $calendarEvents[$calendar->id] ?? [];
136 $eventOrder = $calendar->getMeta('event_order');
137 if (!empty($eventOrder)) {
138 $eventsArray = $calendar->activeEvents;
139 usort($eventsArray, function($a, $b) use ($eventOrder) {
140 $posA = array_search($a->id, $eventOrder);
141 $posB = array_search($b->id, $eventOrder);
142 return $posA - $posB;
143 });
144 $calendar->activeEvents = $eventsArray;
145 }
146 }
147
148 return $this->renderTeamHosts($calendars, [
149 'title' => $atts['title'],
150 'description' => $atts['description'],
151 'logo' => $atts['logo_url'],
152 'wrapper_class' => ''
153 ]);
154 }
155
156 public function renderTeamHosts($calendars, $headerConfig = [])
157 {
158 $wrapperId = 'fcal_team_' . Helper::getNextIndex();
159 wp_enqueue_script('fluent-booking-team', App::getInstance('url.assets') . 'public/js/team_app.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
160
161 $vars = [];
162 foreach ($calendars as $calendar) {
163 $hostHtml = (string)(string)\FluentBooking\App\App::getInstance('view')->make('landing.author_html', [
164 'author' => $calendar->getAuthorProfile(),
165 'calendar' => $calendar,
166 'events' => $calendar->activeEvents
167 ]);
168
169 $hostHtml .= '<div onclick="fcalBackToTeam(this)" class="fcal_back_btn_team"><svg height="20px" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><polygon points="352,128.4 319.7,96 160,256 160,256 160,256 319.7,416 352,383.6 224.7,256 "></polygon></svg> <span>' . __('Back to team', 'fluent-booking') . '</span></div>';
170
171 $eventCount = count($calendar->activeEvents);
172
173 $vars['fcal_host_' . $calendar->id] = [
174 'host_html' => $hostHtml,
175 'event_count' => $eventCount,
176 'target_event_id' => ($eventCount == 1) ? $calendar->activeEvents[0]->id : 0
177 ];
178
179 foreach ($calendar->activeEvents as $event) {
180 $itemVars = $this->getCalendarEventVars($event->calendar, $event);
181 $extraJs = (new LandingPageHandler())->getEventLandingExtraJsFiles($itemVars['form_fields'], $event);
182 if ($extraJs) {
183 $itemVars['lazy_js_files'] = $extraJs;
184 }
185 wp_localize_script('fluent-booking-team', 'fcal_public_vars_' . $event->calendar_id . '_' . $event->id, $itemVars);
186 }
187 }
188
189 wp_localize_script('fluent-booking-team', $wrapperId, $vars);
190
191 $assetUrl = App::getInstance('url.assets');
192 wp_enqueue_script('fluent-booking-public', $assetUrl . 'public/js/app.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
193 $this->loadGlobalVars();
194
195 return App::make('view')->make('public.team_page', [
196 'hosts' => $calendars,
197 'wrapper_id' => $wrapperId,
198 'logo' => Arr::get($headerConfig, 'logo', ''),
199 'title' => Arr::get($headerConfig, 'title', ''),
200 'description' => Arr::get($headerConfig, 'description', ''),
201 'wrapper_class' => Arr::get($headerConfig, 'wrapper_class', '')
202 ]);
203 }
204
205 public function handleCalendarShortcode($atts, $content)
206 {
207 $atts = shortcode_atts([
208 'calendar_id' => '',
209 'event_ids' => '',
210 'title' => '',
211 'description' => '',
212 'logo' => '',
213 'hide_info' => false
214 ], $atts);
215
216 $calendarId = intval($atts['calendar_id']);
217 $title = sanitize_text_field($atts['title']);
218 $description = sanitize_text_field($atts['description']);
219 $logo = sanitize_text_field($atts['logo']);
220 $hideInfo = $atts['hide_info'] ? true : false;
221 $eventIds = array_filter(array_map('intval', explode(',', $atts['event_ids'])));
222
223 if (!$calendarId) {
224 return '';
225 }
226
227 $calendar = Calendar::find($calendarId);
228 if (!$calendar) {
229 return '';
230 }
231
232 $calendarEventQuery = CalendarSlot::where('calendar_id', $calendar->id)
233 ->where('status', 'active');
234
235 if ($eventIds && $eventIds != 'all') {
236 $calendarEventQuery->whereIn('id', $eventIds);
237 }
238
239 $calendarEvents = $calendarEventQuery->get();
240
241 if ($calendarEvents->isEmpty()) {
242 return '';
243 }
244
245 $calendarEvents = CalendarEventService::processEvents($calendar, $calendarEvents);
246
247 $calendar->activeEvents = $calendarEvents;
248
249 return $this->renderCalendarBlock($calendar, [
250 'title' => $title,
251 'description' => $description,
252 'logo' => $logo,
253 'hide_info' => $hideInfo,
254 'wrapper_class' => '',
255 ]);
256 }
257
258 public function renderCalendarBlock($calendar, $headerConfig = [])
259 {
260 $wrapperId = 'fcal_team_' . Helper::getNextIndex();
261 wp_enqueue_script('fluent-booking-calendar', App::getInstance('url.assets') . 'public/js/calendar_app.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
262
263 $calendarHtml = (string)(string)\FluentBooking\App\App::getInstance('view')->make('landing.author_html', [
264 'author' => $calendar->getAuthorProfile(),
265 'calendar' => $calendar,
266 'events' => $calendar->activeEvents,
267 'hideInfo' => Arr::isTrue($headerConfig, 'hide_info'),
268 'block' => true
269 ]);
270
271 $eventCount = count($calendar->activeEvents);
272
273 $vars['fcal_host_calendar' ] = [
274 'calendar_html' => $calendarHtml,
275 'event_count' => $eventCount,
276 'target_event_id' => ($eventCount == 1) ? $calendar->activeEvents[0]->id : 0
277 ];
278
279 foreach ($calendar->activeEvents as $event) {
280 $itemVars = $this->getCalendarEventVars($event->calendar, $event);
281 $extraJs = (new LandingPageHandler())->getEventLandingExtraJsFiles($itemVars['form_fields'], $event);
282 if ($extraJs) {
283 $itemVars['lazy_js_files'] = $extraJs;
284 }
285 wp_localize_script('fluent-booking-calendar', 'fcal_public_vars_' . $event->calendar_id . '_' . $event->id, $itemVars);
286 }
287
288 wp_localize_script('fluent-booking-calendar', $wrapperId, $vars);
289
290 $assetUrl = App::getInstance('url.assets');
291 wp_enqueue_script('fluent-booking-public', $assetUrl . 'public/js/app.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
292 $this->loadGlobalVars();
293
294 return App::make('view')->make('public.calendar_page', [
295 'calendar' => $calendar,
296 'wrapper_id' => $wrapperId,
297 'logo' => Arr::get($headerConfig, 'logo', ''),
298 'title' => Arr::get($headerConfig, 'title', ''),
299 'description' => Arr::get($headerConfig, 'description', ''),
300 'wrapper_class' => Arr::get($headerConfig, 'wrapper_class', ''),
301 'hide_info' => Arr::isTrue($headerConfig, 'hide_info')
302 ]);
303 }
304
305 public function handleBookingListsShortcode($atts, $content)
306 {
307 $atts = shortcode_atts([
308 'title' => __('My Bookings', 'fluent-booking'),
309 'filter' => 'show',
310 'pagination' => 'show',
311 'period' => 'all',
312 'calendar_ids' => 'all',
313 'no_bookings' => __('No bookings found', 'fluent-booking'),
314 'per_page' => 10
315 ], $atts);
316
317 $atts['title'] = sanitize_text_field($atts['title']);
318 $atts['filter'] = sanitize_text_field($atts['filter']);
319 $atts['pagination'] = sanitize_text_field($atts['pagination']);
320 $atts['no_bookings'] = sanitize_text_field($atts['no_bookings']);
321
322 $userData = get_userdata(get_current_user_id());
323
324 $userEmail = $userData ? $userData->user_email : null;
325
326 if (!$userEmail) {
327 return __('Please login to view your bookings', 'fluent-booking');
328 }
329
330 $data = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
331
332 $perPage = intval(Arr::get($data, 'booking_per_page', $atts['per_page']));
333 $currentPage = intval(Arr::get($data, 'booking_page', 1));
334 $bookingPeriod = sanitize_text_field(Arr::get($data, 'booking_period', $atts['period']));
335
336 $bookingQuery = Booking::query()->with('calendar_event')
337 ->where('email', $userEmail)
338 ->applyComputedStatus($bookingPeriod)
339 ->applyBookingOrderByStatus($bookingPeriod);
340
341 if ($atts['calendar_ids'] != 'all') {
342 $atts['calendar_ids'] = array_map('intval', explode(',', $atts['calendar_ids']));
343 $bookingQuery->whereIn('calendar_id', $atts['calendar_ids']);
344 }
345
346 $bookings = $bookingQuery->paginate($perPage, ['*'], 'booking_page', $currentPage)
347 ->appends(['booking_page' => $currentPage])
348 ->withQueryString();
349
350 foreach ($bookings as &$booking) {
351 $booking->author_name = $booking->getHostDetails(false)['name'];
352 $booking->happening_status = $booking->getOngoingStatus();
353 $booking->booking_status_text = $booking->getBookingStatus();
354 $booking->payment_status_text = $booking->getPaymentStatus();
355
356 $booking->booking_date = DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'date');
357 $booking->booking_time = DateTimeHelper::formatToLocale($booking->getAttendeeStartTime(), 'time') . ' - ' . DateTimeHelper::formatToLocale($booking->getAttendeeEndTime(), 'time');
358 }
359
360 $currentPage = $bookings->currentPage();
361 $lastPage = $bookings->lastPage();
362 $startPage = max(1, $currentPage - 2);
363 $endPage = min($lastPage, $currentPage + 2);
364
365 // Adjust if near the beginning or the end
366 if ($currentPage < 3) {
367 $endPage = min($lastPage, 5);
368 }
369 if ($currentPage > $lastPage - 2) {
370 $startPage = max(1, $lastPage - 4);
371 }
372
373 $periodOptions = Helper::getBookingPeriodOptions();
374
375 $pageOptions = apply_filters('fluent_booking/booking_per_page_options', [5, 10, 15, 20, 50, 100]);
376
377 wp_enqueue_script('fluent-booking-list', App::getInstance('url.assets') . 'public/js/bookings.js', [], FLUENT_BOOKING_ASSETS_VERSION, true);
378
379 return App::make('view')->make('public.bookings', [
380 'bookings' => $bookings,
381 'attributes' => $atts,
382 'per_page' => $perPage,
383 'start_page' => $startPage,
384 'end_page' => $endPage,
385 'booking_period' => $bookingPeriod,
386 'page_options' => $pageOptions,
387 'period_options' => $periodOptions
388 ]);
389 }
390
391 public function handleReceiptShortcode($atts, $content)
392 {
393 if (!isset($_REQUEST['hash'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
394 return __('Booking hash is missing!', 'fluent-booking');
395 }
396
397 $hash = sanitize_text_field($_REQUEST['hash']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
398
399 return apply_filters('fluent_booking/payment_receipt_html', '', $hash);
400 }
401
402 public function handleRescheduling($data)
403 {
404 if (empty($data['rescheduling_hash'])) {
405 return;
406 }
407
408 add_filter('fluent_booking/schedule_custom_field_data', function ($array) {
409 return [];
410 });
411
412 add_filter('fluent_booking/schedule_validation_rules_data', function ($data, $postedData, $calendarEvent)
413 {
414 $rules = $messages = [];
415 $rescheduleField = BookingFieldService::getBookingFieldByName($calendarEvent, 'rescheduling_reason');
416
417 if (Arr::isTrue($rescheduleField, 'required')) {
418 $rules['rescheduling_reason'] = 'required';
419 $messages['rescheduling_reason.required'] = __('Please provide a rescheduling reason', 'fluent-booking');
420 }
421
422 return [
423 'rules' => $rules,
424 'messages' => $messages
425 ];
426 }, 10, 3);
427
428 add_action('fluent_booking/before_creating_schedule', function ($bookingData, $postedData, $calendarEvent) {
429 $existingHash = Arr::get($postedData, 'rescheduling_hash');
430 $existingBooking = Booking::where('hash', $existingHash)->first();
431
432 if (!$existingBooking) {
433 wp_send_json([
434 'message' => __('Invalid rescheduling request', 'fluent-booking')
435 ], 422);
436 }
437
438 $rescheduleBy = 'guest';
439 $hostIds = $existingBooking->getHostIds();
440 if (in_array(get_current_user_id(), $hostIds) || PermissionManager::userCan('manage_all_bookings')) {
441 $rescheduleBy = 'host';
442 }
443
444 $existingBooking->updateMeta('rescheduled_by_type', $rescheduleBy);
445
446 if ($rescheduleBy == 'guest' && !$existingBooking->canReschedule()) {
447 wp_send_json([
448 'message' => $existingBooking->getRescheduleMessage()
449 ], 422);
450 }
451
452 if ($bookingData['start_time'] == $existingBooking->start_time) {
453 wp_send_json([
454 'message' => __('Sorry! you can not reschedule to the same time.', 'fluent-booking')
455 ], 422);
456 }
457
458 $endDateTime = gmdate('Y-m-d H:i:s', strtotime($bookingData['start_time']) + ($existingBooking->slot_minutes * 60)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
459
460 $previousBooking = clone $existingBooking;
461
462 if ($existingBooking->isMultiGuestBooking()) {
463 // Need to handle group booking type here
464 // check for existing group
465 $parent = Booking::where('status', 'scheduled')
466 ->where('event_id', $existingBooking->event_id)
467 ->where('start_time', $bookingData['start_time'])
468 ->orderBy('id', 'ASC')
469 ->first();
470
471 if ($parent) {
472 $existingBooking->group_id = $parent->group_id;
473 } else {
474 $existingBooking->group_id = Helper::getNextBookingGroup();
475 }
476 }
477
478 $existingBooking->start_time = $bookingData['start_time'];
479 $existingBooking->person_time_zone = $bookingData['person_time_zone'];
480 $existingBooking->end_time = $endDateTime;
481 $existingBooking->save();
482
483 $existingBooking->updateMeta('previous_meeting_time', $previousBooking->start_time);
484
485 $reschedulingMessage = sanitize_textarea_field(Arr::get($postedData, 'rescheduling_reason'));
486 if ($reschedulingMessage) {
487 $existingBooking->updateMeta('reschedule_reason', $reschedulingMessage);
488 }
489
490 do_action('fluent_booking/log_booking_activity', [
491 'booking_id' => $existingBooking->id,
492 'type' => 'info',
493 'status' => 'closed',
494 'title' => __('Meeting Rescheduled', 'fluent-booking'),
495 /* translators: %1$s is the user who rescheduled the meeting, %2$s is the previous date and time in UTC. */
496 'description' => sprintf(__('Meeting has been rescheduled by %1$s from Web UI. Previous date time: %2$s (UTC)', 'fluent-booking'), $rescheduleBy, $previousBooking->start_time)
497 ]);
498
499 do_action('fluent_booking/after_booking_rescheduled', $existingBooking, $previousBooking, $calendarEvent);
500
501 add_filter('fluent_booking/schedule_receipt_data', function ($data) {
502 $data['title'] = __('Your meeting has been rescheduled', 'fluent-booking');
503 return $data;
504 });
505
506 $redirectUrl = $existingBooking->getRedirectUrlWithQuery();
507
508 $html = BookingService::getBookingConfirmationHtml($existingBooking);
509
510 wp_send_json(apply_filters('fluent_booking/booking_rescheduled_response', [
511 'message' => __('Booking has been rescheduled', 'fluent-booking'),
512 'redirect_url' => $redirectUrl,
513 'response_html' => $html,
514 'booking_hash' => $existingBooking->hash
515 ], $existingBooking), 200);
516
517 }, 10, 3);
518 }
519
520 private function loadGlobalVars()
521 {
522 static $loaded;
523
524 if ($loaded) {
525 return;
526 }
527
528 $loaded = true;
529
530 wp_localize_script('fluent-booking-public', 'fluentCalendarPublicVars', $this->getGlobalVars());
531 }
532
533 public function getGlobalVars()
534 {
535 $currentPerson = [
536 'name' => '',
537 'email' => ''
538 ];
539
540 if (is_user_logged_in()) {
541 $currentUser = wp_get_current_user();
542 $name = trim($currentUser->first_name . ' ' . $currentUser->last_name);
543
544 if (!$name) {
545 $name = $currentUser->display_name;
546 }
547
548 $currentPerson = [
549 'name' => $name,
550 'email' => $currentUser->user_email,
551 'user_id' => $currentUser->ID
552 ];
553 } else {
554 $request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
555
556 // Check for url params
557 if ($name = sanitize_text_field(Arr::get($request, 'invitee_name'))) {
558 $currentPerson['name'] = $name;
559 }
560
561 if ($email = sanitize_email(Arr::get($request, 'invitee_email'))) {
562 if (is_email($email)) {
563 $currentPerson['email'] = $email;
564 }
565 }
566 }
567
568 if (empty($currentPerson['email'])) {
569 // Let's try to get from FluentCRM is exists
570 if (defined('FLUENTCRM')) {
571 $contactApi = FluentCrmApi('contacts');
572 $contact = $contactApi->getCurrentContact();
573 if ($contact) {
574 $currentPerson['email'] = $contact->email;
575 $currentPerson['name'] = $contact->full_name;
576 }
577 }
578 }
579
580 $globalSettings = Helper::getGlobalSettings();
581 $startDay = Arr::get($globalSettings, 'administration.start_day', 'mon');
582
583 $data = [
584 'ajaxurl' => admin_url('admin-ajax.php'),
585 'timezones' => DateTimeHelper::getFlatGroupedTimeZones(),
586 'current_person' => $currentPerson,
587 'start_day' => $startDay,
588 'i18' => [
589 'Timezone' => __('Timezone', 'fluent-booking'),
590 'Day' => __('Day', 'fluent-booking'),
591 'Days' => __('Days', 'fluent-booking'),
592 'Hour' => __('Hour', 'fluent-booking'),
593 'Hours' => __('Hours', 'fluent-booking'),
594 'Minute' => __('Minute', 'fluent-booking'),
595 'Minutes' => __('Minutes', 'fluent-booking'),
596 'Enter Details' => __('Enter Details', 'fluent-booking'),
597 'Summary' => __('Summary', 'fluent-booking'),
598 'Payment Details' => __('Payment Details', 'fluent-booking'),
599 'Total Payment' => __('Total Payment', 'fluent-booking'),
600 'Payment Method' => __('Payment Method', 'fluent-booking'),
601 'Pay Now' => __('Pay Now', 'fluent-booking'),
602 'processing' => __('Processing', 'fluent-booking'),
603 'date_time_config' => [
604 'weekdays' => array(
605 'sunday' => _x('Sunday', 'calendar day full', 'fluent-booking'),
606 'monday' => _x('Monday', 'calendar day full', 'fluent-booking'),
607 'tuesday' => _x('Tuesday', 'calendar day full', 'fluent-booking'),
608 'wednesday' => _x('Wednesday', 'calendar day full', 'fluent-booking'),
609 'thursday' => _x('Thursday', 'calendar day full', 'fluent-booking'),
610 'friday' => _x('Friday', 'calendar day full', 'fluent-booking'),
611 'saturday' => _x('Saturday', 'calendar day full', 'fluent-booking'),
612 ),
613 'months' => array(
614 'January' => _x('January', 'calendar month name full', 'fluent-booking'),
615 'February' => _x('February', 'calendar month name full', 'fluent-booking'),
616 'March' => _x('March', 'calendar month name full', 'fluent-booking'),
617 'April' => _x('April', 'calendar month name full', 'fluent-booking'),
618 'May' => _x('May', 'calendar month name full', 'fluent-booking'),
619 'June' => _x('June', 'calendar month name full', 'fluent-booking'),
620 'July' => _x('July', 'calendar month name full', 'fluent-booking'),
621 'August' => _x('August', 'calendar month name full', 'fluent-booking'),
622 'September' => _x('September', 'calendar month name full', 'fluent-booking'),
623 'October' => _x('October', 'calendar month name full', 'fluent-booking'),
624 'November' => _x('November', 'calendar month name full', 'fluent-booking'),
625 'December' => _x('December', 'calendar month name full', 'fluent-booking')
626 ),
627 'weekdaysShort' => array(
628 'sun' => _x('Sun', 'calendar day short', 'fluent-booking'),
629 'mon' => _x('Mon', 'calendar day short', 'fluent-booking'),
630 'tue' => _x('Tue', 'calendar day short', 'fluent-booking'),
631 'wed' => _x('Wed', 'calendar day short', 'fluent-booking'),
632 'thu' => _x('Thu', 'calendar day short', 'fluent-booking'),
633 'fri' => _x('Fri', 'calendar day short', 'fluent-booking'),
634 'sat' => _x('Sat', 'calendar day short', 'fluent-booking')
635 ),
636 'monthsShort' => array(
637 'jan' => _x('Jan', 'calendar month name short', 'fluent-booking'),
638 'feb' => _x('Feb', 'calendar month name short', 'fluent-booking'),
639 'mar' => _x('Mar', 'calendar month name short', 'fluent-booking'),
640 'apr' => _x('Apr', 'calendar month name short', 'fluent-booking'),
641 'may' => _x('May', 'calendar month name short', 'fluent-booking'),
642 'jun' => _x('Jun', 'calendar month name short', 'fluent-booking'),
643 'jul' => _x('Jul', 'calendar month name short', 'fluent-booking'),
644 'aug' => _x('Aug', 'calendar month name short', 'fluent-booking'),
645 'sep' => _x('Sep', 'calendar month name short', 'fluent-booking'),
646 'oct' => _x('Oct', 'calendar month name short', 'fluent-booking'),
647 'nov' => _x('Nov', 'calendar month name short', 'fluent-booking'),
648 'dec' => _x('Dec', 'calendar month name short', 'fluent-booking')
649 ),
650 'numericSystem' => _x('0_1_2_3_4_5_6_7_8_9', 'calendar numeric system - Sequence must need to maintained', 'fluent-booking'),
651 ],
652 'Country' => __('Country', 'fluent-booking'),
653 '12h' => _x('12h', 'date time format switch', 'fluent-booking'),
654 '24h' => _x('24h', 'date time format switch', 'fluent-booking'),
655 'spots left' => _x('spots left', 'for how many spots left for available booking', 'fluent-booking'),
656 'spots remaining' => _x('spots remaining', 'for how many spots remaining for available booking', 'fluent-booking'),
657 'Next' => _x('Next', 'Booking form spot selection', 'fluent-booking'),
658 'Select on the Next Step' => __('Select on the Next Step', 'fluent-booking'),
659 'location options' => __('location options', 'fluent-booking'),
660 'Your address' => __('Your address', 'fluent-booking'),
661 'Organizer Phone Number' => __('Organizer Phone Number', 'fluent-booking'),
662 'In Person (Attendee Address)' => __('In Person (Attendee Address)', 'fluent-booking'),
663 'In Person (Organizer Address)' => __('In Person (Organizer Address)', 'fluent-booking'),
664 'Attendee Phone Number' => __('Attendee Phone Number', 'fluent-booking'),
665 'Google Meet' => __('Google Meet', 'fluent-booking'),
666 'Zoom Meeting' => __('Zoom Meeting', 'fluent-booking'),
667 'Online Meeting' => __('Online Meeting', 'fluent-booking'),
668 'Phone Call' => __('Phone Call', 'fluent-booking'),
669 'Processing...' => __('Processing...', 'fluent-booking'),
670 'Loading Payment Processor...' => __('Loading Payment Processor...', 'fluent-booking'),
671 'PM' => __('PM', 'fluent-booking'),
672 'AM' => __('AM', 'fluent-booking'),
673 'Email' => __('Email', 'fluent-booking'),
674 'Date' => __('Date', 'fluent-booking'),
675 'Time' => __('Time', 'fluent-booking'),
676 'per guest' => __('per guest', 'fluent-booking'),
677 'Add guest' => __('Add guest', 'fluent-booking'),
678 'Add guests' => __('Add guests', 'fluent-booking'),
679 'Add another' => __('Add another', 'fluent-booking'),
680 'Choose File' => __('Choose File', 'fluent-booking'),
681 'This field is required.' => __('This field is required.', 'fluent-booking'),
682 'No availability in' => __('No availability in', 'fluent-booking'),
683 'View next month' => __('View next month', 'fluent-booking'),
684 'View previous month' => __('View previous month', 'fluent-booking'),
685 'No_payment_method_description' => __('No activated payment method found. If you are an admin please check the event payment settings', 'fluent-booking'),
686 'Please fill up the required data' => __('Please fill up the required data', 'fluent-booking'),
687 'Please select a valid payment method' => __('Please select a valid payment method', 'fluent-booking'),
688 'Please Select' => __('Please Select', 'fluent-booking'),
689 'Something is wrong!' => __('Something is wrong!', 'fluent-booking'),
690 'Requires Confirmation' => __('Requires Confirmation', 'fluent-booking'),
691 ],
692 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default')
693 ];
694
695 if (isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
696 $data['user_country'] = sanitize_text_field($_SERVER['HTTP_CF_IPCOUNTRY']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
697 } else {
698 $data['user_country'] = Arr::get($globalSettings, 'administration.default_country', '');
699 }
700
701 return apply_filters('fluent_calendar/global_booking_vars', $data);
702 }
703
704 public function ajaxScheduleMeeting()
705 {
706 $app = App::getInstance();
707
708 $postedData = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
709
710 $eventId = (int)$postedData['event_id'];
711
712 $isRescheduling = Arr::get($postedData, 'rescheduling_hash', '');
713
714 $calendarEvent = CalendarSlot::find($eventId);
715
716 if (!$calendarEvent || ($calendarEvent->status != 'active' && !$isRescheduling)) {
717 wp_send_json([
718 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
719 ], 422);
720 }
721
722 do_action('fluent_booking/starting_scheduling_ajax', $postedData);
723
724 $rules = [
725 'name' => 'required',
726 'email' => 'required|email',
727 'timezone' => 'required',
728 'start_date' => 'required'
729 ];
730
731 $messages = [
732 'name.required' => __('Please enter your name', 'fluent-booking'),
733 'email.required' => __('Please enter your email address', 'fluent-booking'),
734 'email.email' => __('Please enter provide a valid email address', 'fluent-booking'),
735 'timezone.required' => __('Please select timezone first', 'fluent-booking'),
736 'start_date.required' => __('Please select a date and time', 'fluent-booking')
737 ];
738
739 if ($calendarEvent->isPhoneRequired()) {
740 $rules['phone_number'] = 'required';
741 $messages['phone_number.required'] = __('Please provide your phone number', 'fluent-booking');
742 } else if ($calendarEvent->isAddressRequired()) {
743 $rules['address'] = 'required';
744 $messages['address.required'] = __('Please provide your Address', 'fluent-booking');
745 } else if ($calendarEvent->isLocationFieldRequired()) {
746 $rules['location_config.driver'] = 'required';
747 $messages['location_config.driver'] = __('Please select location', 'fluent-booking');
748
749 $selectedLocation = LocationService::getLocationDetails($calendarEvent, Arr::get($postedData, 'location_config', []), $postedData);
750 $selectedLocationDriver = Arr::get($selectedLocation, 'type');
751 // is user input required
752 if (in_array($selectedLocationDriver, ['in_person_guest', 'phone_guest'])) {
753 $rules['location_config.user_location_input'] = 'required';
754 if ($selectedLocationDriver == 'in_person_guest') {
755 $messages['location_config.user_location_input.required'] = __('Please provide your address', 'fluent-booking');
756 } else {
757 $messages['location_config.user_location_input.required'] = __('Please provide your phone number', 'fluent-booking');
758 }
759 }
760 }
761
762 $duration = (int)$calendarEvent->getDuration(Arr::get($postedData, 'duration', null));
763
764 if ($calendarEvent->isPaymentEnabled($duration)) {
765 $rules['payment_method'] = 'required';
766 $messages['payment_method.required'] = __('Please select a valid payment method', 'fluent-booking');
767 }
768
769 if ($additionalGuests = Arr::get($postedData, 'guests', [])) {
770 if ($calendarEvent->isMultiGuestEvent()) {
771 $additionalGuests = $this->sanitize_mapped_data($additionalGuests);
772 $additionalGuests = array_values(array_filter($additionalGuests, function ($guest) {
773 return Arr::get($guest, 'name') && Arr::get($guest, 'email');
774 }));
775 } else {
776 $additionalGuests = array_filter(array_map('sanitize_email', $additionalGuests));
777 }
778 }
779
780 $postedData['guests'] = $additionalGuests;
781
782 $requiredFields = array_filter($calendarEvent->getMeta('booking_fields', []), function ($field) {
783 return Arr::isTrue($field, 'required') && Arr::isTrue($field, 'enabled') && (Arr::get($field, 'name') == 'message' || Arr::get($field, 'name') == 'guests');
784 });
785
786 foreach ($requiredFields as $field) {
787 if (empty($rules[$field['name']])) {
788 $rules[$field['name']] = 'required';
789 $messages[$field['name'] . '.required'] = __('This field is required', 'fluent-booking');
790 }
791 }
792
793 $validationConfig = apply_filters('fluent_booking/schedule_validation_rules_data', [
794 'rules' => $rules,
795 'messages' => $messages
796 ], $postedData, $calendarEvent);
797
798 $validator = $app->validator->make($postedData, $validationConfig['rules'], $validationConfig['messages']);
799 if ($validator->validate()->fails()) {
800 wp_send_json([
801 'message' => __('Please fill up the required data', 'fluent-booking'),
802 'errors' => $validator->errors()
803 ], 422);
804 return;
805 }
806
807 $customFieldsData = BookingFieldService::getCustomFieldsData($postedData, $calendarEvent);
808 $customFieldsData = apply_filters('fluent_booking/schedule_custom_field_data', $customFieldsData, $customFieldsData, $calendarEvent);
809
810 if (is_wp_error($customFieldsData)) {
811 wp_send_json([
812 'message' => $customFieldsData->get_error_message(),
813 'errors' => $customFieldsData->get_error_data()
814 ], 422);
815 return;
816 }
817
818 $validateDateFields = BookingFieldService::validateDateFields($customFieldsData, $calendarEvent);
819
820 if (is_wp_error($validateDateFields)) {
821 wp_send_json([
822 'message' => $validateDateFields->get_error_message(),
823 ], 422);
824 return;
825 }
826
827 $startDate = Arr::get($postedData, 'start_date');
828 $timezone = sanitize_text_field(Arr::get($postedData, 'timezone', 'UTC'));
829
830 if (is_array($startDate)) {
831 $startDateTime = array_slice(
832 array_map(function($date) use ($timezone) {
833 return DateTimeHelper::convertToUtc(sanitize_text_field($date), $timezone);
834 }, $startDate), 0, $calendarEvent->multiBookingLimit()
835 );
836 $endDateTime = array_map(function($date) use ($duration) {
837 return gmdate('Y-m-d H:i:s', strtotime($date) + ($duration * 60)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
838 }, $startDateTime);
839 }
840
841 if (is_string($startDate)) {
842 $startDate = sanitize_text_field($startDate);
843 $startDateTime = DateTimeHelper::convertToUtc($startDate, $timezone);
844 $endDateTime = gmdate('Y-m-d H:i:s', strtotime($startDateTime) + ($duration * 60)); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
845 }
846
847 $bookingData = [
848 'person_time_zone' => sanitize_text_field($timezone),
849 'start_time' => $startDateTime,
850 'end_time' => $endDateTime,
851 'name' => sanitize_text_field($postedData['name']),
852 'email' => sanitize_email($postedData['email']),
853 'message' => sanitize_textarea_field(wp_unslash(Arr::get($postedData, 'message', ''))),
854 'phone' => sanitize_textarea_field(Arr::get($postedData, 'phone_number', '')),
855 'address' => sanitize_textarea_field(Arr::get($postedData, 'address', '')),
856 'ip_address' => Helper::getIp(),
857 'status' => 'scheduled',
858 'source' => 'web',
859 'event_type' => $calendarEvent->event_type,
860 'slot_minutes' => $duration
861 ];
862
863 if ($calendarEvent->isConfirmationRequired($startDateTime)) {
864 $bookingData['status'] = 'pending';
865 }
866
867 $locationConfig = Arr::get($postedData, 'location_config', []);
868 $selectedLocation = LocationService::getLocationDetails($calendarEvent, $locationConfig, $postedData);
869 if ($selectedLocation['type'] == 'phone_guest') {
870 $bookingData['phone'] = $selectedLocation['description'];
871 }
872
873 $bookingData['location_details'] = $selectedLocation;
874
875 if ($sourceUrl = Arr::get($postedData, 'source_url', '')) {
876 $bookingData['source_url'] = sanitize_url($sourceUrl);
877 }
878
879 if (!empty($postedData['payment_method'])) {
880 $customFieldsData['payment_method'] = $postedData['payment_method'];
881 }
882
883 $timeSlotService = TimeSlotServiceHandler::initService($calendarEvent->calendar, $calendarEvent);
884
885 if (is_wp_error($timeSlotService)) {
886 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timezone);
887 }
888
889 $availableSpot = $timeSlotService->isSpotAvailable($startDateTime, $endDateTime, $duration);
890
891 if (!$availableSpot) {
892 wp_send_json([
893 'message' => __('This selected time slot is not available. Maybe someone booked the spot just a few seconds ago.', 'fluent-booking')
894 ], 422);
895 }
896
897 if ($additionalGuests) {
898 $guestField = BookingFieldService::getBookingFieldByName($calendarEvent, 'guests');
899 $guestLimit = Arr::get($guestField, 'limit', 10);
900 if ($calendarEvent->isMultiGuestEvent()) {
901 $remaining = Arr::get($availableSpot, 'remaining', $calendarEvent->getMaxBookingPerSlot());
902 $guestLimit = min($remaining, $guestLimit) - 1;
903 }
904 $bookingData['additional_guests'] = array_slice($additionalGuests, 0, $guestLimit);
905 }
906
907 if ($calendarEvent->isRoundRobin()) {
908 $bookingData['host_user_id'] = $timeSlotService->hostUserId;
909 }
910
911 do_action('fluent_booking/before_creating_schedule', $bookingData, $postedData, $calendarEvent);
912
913 try {
914 $booking = BookingService::createBooking($bookingData, $calendarEvent, $customFieldsData);
915
916 if (is_wp_error($booking)) {
917 throw new \Exception(wp_kses_post($booking->get_error_message()), 422);
918 }
919
920 } catch (\Exception $e) {
921 wp_send_json([
922 'message' => $e->getMessage()
923 ], 422);
924 return;
925 }
926
927 $redirectUrl = $booking->getRedirectUrlWithQuery();
928
929 $html = BookingService::getBookingConfirmationHtml($booking);
930
931 wp_send_json(apply_filters('fluent_booking/booking_confirmation_response', [
932 'message' => __('Booking has been confirmed', 'fluent-booking'),
933 'redirect_url' => $redirectUrl,
934 'response_html' => $html,
935 'booking_hash' => $booking->hash
936 ], $booking), 200);
937 }
938
939 public function ajaxGetAvailableDates()
940 {
941 $startBenchmark = microtime(true);
942
943 $request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
944
945 $eventId = (int)$request['event_id'];
946
947 $rescheduling = Arr::get($request, 'rescheduling', 'no');
948
949 $calendarEvent = CalendarSlot::findOrfail($eventId);
950
951 if (!$calendarEvent || ($calendarEvent->status != 'active' && $rescheduling == 'no')) {
952 wp_send_json([
953 'message' => __('Sorry, the host is not accepting any new bookings at the moment.', 'fluent-booking')
954 ], 422);
955 }
956
957 $calendar = $calendarEvent->calendar;
958 $startDate = sanitize_text_field(Arr::get($request, 'start_date')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
959
960 if (!$startDate) {
961 $startDate = gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
962 }
963
964 $timeZone = sanitize_text_field(Arr::get($request, 'timezone')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
965
966 if (!$timeZone) {
967 $timeZone = wp_timezone_string();
968 }
969
970 if (!in_array($timeZone, \DateTimeZone::listIdentifiers())) {
971 $timeZone = $calendar->author_timezone;
972 }
973
974 $duration = (int)$calendarEvent->getDuration(Arr::get($request, 'duration', null));
975
976 $timeSlotService = TimeSlotServiceHandler::initService($calendar, $calendarEvent);
977
978 if (is_wp_error($timeSlotService)) {
979 return TimeSlotServiceHandler::sendError($timeSlotService, $calendarEvent, $timeZone);
980 }
981
982 $availableSpots = $timeSlotService->getAvailableSpots($startDate, $timeZone, $duration);
983
984 if (is_wp_error($availableSpots)) {
985 return TimeSlotServiceHandler::sendError($availableSpots, $calendarEvent, $timeZone);
986 }
987
988 $availableSpots = array_filter($availableSpots);
989 $availableSpots = apply_filters('fluent_booking/available_slots_for_view', $availableSpots, $calendarEvent, $calendar, $timeZone, $duration);
990
991 wp_send_json([
992 'available_slots' => $availableSpots,
993 'timezone' => $timeZone,
994 'max_lookup_date' => $calendarEvent->getMaxLookUpDate(),
995 'execution_time' => microtime(true) - $startBenchmark
996 ], 200);
997 }
998
999 public function getCalendarEventVars(Calendar $calendar, CalendarSlot $calendarEvent)
1000 {
1001 $calendarEvent->description = wpautop($calendarEvent->description);
1002 $calendarEvent->location_icon_html = $calendarEvent->defaultLocationHtml();
1003 $formFields = BookingFieldService::getBookingFields($calendarEvent);
1004
1005 $eventData = [
1006 'id' => $calendarEvent->id,
1007 'max_lookup_date' => $calendarEvent->getMaxLookUpDate(),
1008 'min_lookup_date' => $calendarEvent->getMinLookUpDate(),
1009 'min_bookable_date' => $calendarEvent->getMinBookableDateTime(),
1010 'is_display_spots' => $calendarEvent->isDisplaySpots(),
1011 'duration' => $calendarEvent->getDefaultDuration(),
1012 'title' => $calendarEvent->title,
1013 'location_settings' => $calendarEvent->location_settings,
1014 'location_icon_html' => $calendarEvent->location_icon_html,
1015 'description' => $calendarEvent->description,
1016 'pre_selects' => null,
1017 'settings' => $calendarEvent->settings,
1018 'type' => $calendarEvent->type,
1019 'event_type' => $calendarEvent->event_type,
1020 'time_format' => Arr::get(get_option('_fluent_booking_settings'), 'time_format', '12'),
1021 ];
1022
1023 $author = $calendar->getAuthorProfile(true);
1024 $author['name'] = $calendar->title;
1025
1026 $eventVars = [
1027 'slot' => $eventData,
1028 'author_profile' => $author,
1029 'form_fields' => $formFields,
1030 'i18n' => [
1031 'Schedule_Meeting' => __('Schedule Meeting', 'fluent-booking'),
1032 'Continue_to_Payments' => __('Continue to Payments', 'fluent-booking'),
1033 'Confirm_Payment' => __('Confirm Payment', 'fluent-booking'),
1034 ],
1035 'date_formatter' => DateTimeHelper::getDateFormatter(true),
1036 'isRtl' => Helper::fluentbooking_is_rtl(),
1037 'duration_lookup' => Helper::getDurationLookup(),
1038 'multi_duration_lookup' => Helper::getDurationLookup(true)
1039 ];
1040
1041 $eventVars['form_fields'] = array_values($eventVars['form_fields']);
1042
1043 if (!$calendar->isHostCalendar()) {
1044 $eventVars['team_member_profiles'] = $calendarEvent->getAuthorProfiles(true);
1045 }
1046
1047 return apply_filters('fluent_booking/public_event_vars', $eventVars, $calendarEvent);
1048 }
1049
1050 public function ajaxHandleCancelMeeting()
1051 {
1052 $data = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1053
1054 $meetingHash = Arr::get($data, 'meeting_hash');
1055
1056 $meeting = Booking::where('hash', $meetingHash)->first();
1057
1058 if (!$meeting) {
1059 wp_send_json([
1060 'message' => __('Sorry! meeting could not be found', 'fluent-booking')
1061 ], 422);
1062 }
1063
1064 if (!$meeting->canCancel()) {
1065 wp_send_json([
1066 'message' => $meeting->getCancellationMessage()
1067 ], 422);
1068 }
1069
1070 $message = sanitize_textarea_field(Arr::get($data, 'cancellation_reason', ''));
1071
1072 $cancelField = BookingFieldService::getBookingFieldByName($meeting->calendar_event, 'cancellation_reason');
1073
1074 if (!$message && Arr::isTrue($cancelField, 'required')) {
1075 wp_send_json([
1076 'message' => __('Please provide a reason for cancellation', 'fluent-booking')
1077 ], 422);
1078 }
1079
1080 $result = $meeting->cancelMeeting($message, 'guest', get_current_user_id());
1081
1082 if (is_wp_error($result)) {
1083 if (!wp_doing_ajax()) {
1084 wp_redirect($meeting->getConfirmationUrl());
1085 exit();
1086 }
1087
1088 wp_send_json([
1089 'message' => $result->get_error_message()
1090 ], 422);
1091 }
1092
1093 if (wp_doing_ajax()) {
1094 wp_send_json([
1095 'message' => __('Meeting has been cancelled', 'fluent-booking')
1096 ], 200);
1097 }
1098
1099 wp_redirect($meeting->getConfirmationUrl());
1100 exit;
1101 }
1102
1103 private static function sanitize_mapped_data($settings)
1104 {
1105 $sanitizerMap = [
1106 'name' => 'sanitize_text_field',
1107 'email' => 'sanitize_email',
1108 ];
1109
1110 return Helper::fcal_backend_sanitizer($settings, $sanitizerMap);
1111 }
1112 }
1113