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

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