| @@ -6,13 +6,15 @@ | ||
| 6 | 6 | use FluentBooking\App\Hooks\Handlers\FrontEndHandler; |
| 7 | 7 | use FluentBooking\App\Models\Booking; |
| 8 | 8 | use FluentBooking\App\Models\Calendar; |
| 9 | 9 | use FluentBooking\App\Models\CalendarSlot; |
| 10 | +use FluentBooking\App\Services\CalendarEventService; | |
| 10 | 11 | use FluentBooking\App\Services\BookingFieldService; |
| 11 | 12 | use FluentBooking\App\Services\BookingService; |
| 12 | 13 | use FluentBooking\App\Services\Helper; |
| 13 | 14 | use FluentBooking\Framework\Support\Arr; |
| 14 | 15 | use FluentBooking\Framework\Support\Collection; |
| 16 | +use FluentBooking\App\Vite; | |
| 15 | 17 | |
| 16 | 18 | class LandingPageHandler |
| 17 | 19 | { |
| 18 | 20 | public function boot() |
| @@ -38,15 +40,23 @@ | ||
| 38 | 40 | } |
| 39 | 41 | |
| 40 | 42 | $authorSlug = sanitize_text_field($urlParts[1]); |
| 41 | 43 | |
| 44 | + if (isset($_GET['embedded'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 45 | + $this->handleEmbeddedStyle(); | |
| 46 | + } | |
| 47 | + | |
| 42 | 48 | $this->routeView($authorSlug, Arr::get($urlParts, 2, null)); |
| 43 | 49 | } |
| 44 | 50 | |
| 45 | 51 | public function handleUrlParamsPage() |
| 46 | 52 | { |
| 47 | - $route = sanitize_text_field($_GET['fluent-booking']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 53 | + $route = isset($_GET['fluent-booking']) ? sanitize_text_field(wp_unslash($_GET['fluent-booking'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 48 | 54 | |
| 55 | + if (isset($_GET['embedded'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 56 | + $this->handleEmbeddedStyle(); | |
| 57 | + } | |
| 58 | + | |
| 49 | 59 | if ($route == 'booking') { |
| 50 | 60 | $this->handleAfterBookingPage(); |
| 51 | 61 | return; |
| 52 | 62 | } |
| @@ -89,9 +99,9 @@ | ||
| 89 | 99 | ->first(); |
| 90 | 100 | if (!$slot) { |
| 91 | 101 | return; |
| 92 | 102 | } |
| 93 | - $this->renderBookingView($calendar, $slot); | |
| 103 | + return $this->renderBookingView($calendar, $slot); | |
| 94 | 104 | } |
| 95 | 105 | |
| 96 | 106 | $this->renderHostView($calendar); |
| 97 | 107 | } |
| @@ -109,14 +119,9 @@ | ||
| 109 | 119 | } |
| 110 | 120 | |
| 111 | 121 | $activeEvents = $activeEvents->get(); |
| 112 | 122 | |
| 113 | - foreach ($activeEvents as $activeEvent) { | |
| 114 | - $activeEvent->public_url = $activeEvent->getPublicUrl(); | |
| 115 | - $activeEvent->durations = $activeEvent->getAvailableDurations(); | |
| 116 | - $activeEvent->description = $activeEvent->getDescription(); | |
| 117 | - $activeEvent->short_description = Helper::excerpt($activeEvent->description); | |
| 118 | - } | |
| 123 | + $activeEvents = CalendarEventService::processEvents($calendar, $activeEvents); | |
| 119 | 124 | |
| 120 | 125 | $metaDescription = Helper::excerpt($calendar->description); |
| 121 | 126 | |
| 122 | 127 | $calendar->description = wpautop($calendar->description); |
| @@ -145,11 +150,12 @@ | ||
| 145 | 150 | if ($extraJs) { |
| 146 | 151 | $vars['lazy_js_files'] = $extraJs; |
| 147 | 152 | } |
| 148 | 153 | $jsVars['fcal_public_vars_' . $calendar->id . '_' . $activeEvent->id] = $vars; |
| 154 | + $activeEvent->locations = $activeEvent->defaultLocationHtml(); | |
| 155 | + do_action_ref_array('fluent_booking/landing_page_event', [&$activeEvent]); | |
| 149 | 156 | } |
| 150 | 157 | |
| 151 | - $assetUrl = App::getInstance('url.assets'); | |
| 152 | 158 | $data = [ |
| 153 | 159 | 'calendar' => $calendar, |
| 154 | 160 | 'events' => $activeEvents, |
| 155 | 161 | 'author' => $authorProfile, |
| @@ -155,17 +161,18 @@ | ||
| 155 | 161 | 'author' => $authorProfile, |
| 156 | 162 | 'title' => $calendar->title .' - '.get_bloginfo('name'), |
| 157 | 163 | 'description' => $metaDescription, |
| 158 | 164 | 'url' => home_url($wp->request), |
| 165 | + 'embedded' => isset($_GET['embedded']) ? true : false, // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 159 | 166 | 'css_files' => [ |
| 160 | - App::getInstance('url.assets') . 'public/saas.css' | |
| 167 | + Vite::styleUrl('saas_css') | |
| 161 | 168 | ], |
| 162 | 169 | 'js_files' => [ |
| 163 | - 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js', | |
| 170 | + 'fluent-booking-public-js' => Vite::scriptUrl('public_app'), | |
| 164 | 171 | ], |
| 165 | 172 | 'js_vars' => $jsVars, |
| 166 | 173 | 'header_js_files' => [ |
| 167 | - 'fluent_booking_team_app-js' => $assetUrl. 'public/js/team_app.js' | |
| 174 | + 'fluent_booking_team_app-js' => Vite::scriptUrl('team_app') | |
| 168 | 175 | ] |
| 169 | 176 | ]; |
| 170 | 177 | |
| 171 | 178 | if ($extraJsFiles) { |
| @@ -170,20 +177,11 @@ | ||
| 170 | 177 | |
| 171 | 178 | if ($extraJsFiles) { |
| 172 | 179 | $extraJsFiles = array_unique($extraJsFiles); |
| 173 | 180 | $data['js_files'] = array_merge($data['js_files'], $extraJsFiles); |
| 174 | - add_action('fluent_booking/main_landing', function () use ($assetUrl) { | |
| 175 | - ?> | |
| 176 | - <style> | |
| 177 | - .fcal_phone_wrapper .flag { | |
| 178 | - background: url(<?php echo esc_url($assetUrl.'images/flags_responsive.png'); ?>) no-repeat; | |
| 179 | - background-size: 100%; | |
| 180 | - } | |
| 181 | - </style> | |
| 182 | - <?php | |
| 183 | - }); | |
| 181 | + } | |
| 184 | 182 | |
| 185 | - } | |
| 183 | + $data = apply_filters('fluent_booking/host_view_page_vars', $data, $calendar, $activeEvents, $authorProfile); | |
| 186 | 184 | |
| 187 | 185 | $app = App::getInstance(); |
| 188 | 186 | status_header(200); |
| 189 | 187 | $app->view->render('landing.author_landing', $data); |
| @@ -189,14 +187,16 @@ | ||
| 189 | 187 | $app->view->render('landing.author_landing', $data); |
| 190 | 188 | exit(200); |
| 191 | 189 | } |
| 192 | 190 | |
| 193 | - private function renderBookingView($calendar, $calendarEvent, $existingBooking = null) | |
| 191 | + private function renderBookingView($calendar, $calendarEvent, $existingBooking = null, $isReschedule = false) | |
| 194 | 192 | { |
| 195 | - $settings = LandingPageHelper::getSettings($calendar, 'public'); | |
| 196 | - if ($settings['show_type'] != 'all') { | |
| 197 | - if (!in_array($calendarEvent->id, $settings['enabled_slots'])) { | |
| 198 | - return ''; | |
| 193 | + if (!$isReschedule) { | |
| 194 | + $settings = LandingPageHelper::getSettings($calendar, 'public'); | |
| 195 | + if ($settings['show_type'] != 'all') { | |
| 196 | + if (!in_array($calendarEvent->id, $settings['enabled_slots'])) { | |
| 197 | + return ''; | |
| 198 | + } | |
| 199 | 199 | } |
| 200 | 200 | } |
| 201 | 201 | |
| 202 | 202 | global $wp; |
| @@ -204,9 +204,9 @@ | ||
| 204 | 204 | $calendarEvent->max_lookup_date = $calendarEvent->getMaxLookUpDate(); |
| 205 | 205 | $calendarEvent->min_lookup_date = $calendarEvent->getMinLookUpDate(); |
| 206 | 206 | |
| 207 | 207 | if (!empty($_REQUEST['booking_id'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 208 | - $bookingHash = sanitize_text_field($_REQUEST['booking_id']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 208 | + $bookingHash = sanitize_text_field(wp_unslash($_REQUEST['booking_id'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 209 | 209 | $booking = Booking::where('hash', $bookingHash) |
| 210 | 210 | ->where('event_id', $calendarEvent->id) |
| 211 | 211 | ->first(); |
| 212 | 212 | if ($booking) { |
| @@ -228,26 +228,27 @@ | ||
| 228 | 228 | $assetUrl = App::getInstance('url.assets'); |
| 229 | 229 | |
| 230 | 230 | $eventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent); |
| 231 | 231 | |
| 232 | - $isRtl = Helper::fluentbooking_is_rtl(); | |
| 232 | + $publicCss = 'saas_css'; | |
| 233 | 233 | |
| 234 | - $publicCss = 'public/saas.css'; | |
| 235 | - if ($isRtl) { | |
| 236 | - $publicCss = 'public/saas-rtl.css'; | |
| 237 | - } | |
| 234 | + $title = $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name']; | |
| 235 | + | |
| 236 | + $title = apply_filters('fluent_booking/booking_confirmation_page_title', $title, $calendarEvent, $authorProfile); | |
| 237 | + | |
| 238 | 238 | $data = [ |
| 239 | 239 | 'calendar' => $calendar, |
| 240 | 240 | 'calendar_event' => $calendarEvent, |
| 241 | 241 | 'author' => $authorProfile, |
| 242 | - 'title' => $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'], | |
| 242 | + 'title' => $title, | |
| 243 | 243 | 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...', |
| 244 | 244 | 'url' => home_url($wp->request), |
| 245 | + 'embedded' => isset($_GET['embedded']) ? true : false, // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 245 | 246 | 'css_files' => [ |
| 246 | - $assetUrl . $publicCss | |
| 247 | + Vite::styleUrl($publicCss) | |
| 247 | 248 | ], |
| 248 | 249 | 'js_files' => [ |
| 249 | - 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js', | |
| 250 | + 'fluent-booking-public-js' => Vite::scriptUrl('public_app'), | |
| 250 | 251 | ], |
| 251 | 252 | 'js_vars' => [ |
| 252 | 253 | 'fluentCalendarPublicVars' => (new FrontEndHandler())->getGlobalVars(), |
| 253 | 254 | 'fcal_public_vars_' . $calendar->id . '_' . $calendarEvent->id => $eventVars, |
| @@ -281,9 +282,8 @@ | ||
| 281 | 282 | |
| 282 | 283 | private function showBookingConfimationPage($booking, $actionType = 'confirmation') |
| 283 | 284 | { |
| 284 | 285 | $validActions = ['confirmation']; |
| 285 | - $isRtl = Helper::fluentbooking_is_rtl(); | |
| 286 | 286 | |
| 287 | 287 | if (in_array($booking->status, ['scheduled', 'pending', 'rescheduled'])) { |
| 288 | 288 | $validActions = array_merge($validActions, ['reschedule', 'cancel']); |
| 289 | 289 | } |
| @@ -304,24 +304,24 @@ | ||
| 304 | 304 | echo $icsText; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 305 | 305 | die(); |
| 306 | 306 | } |
| 307 | 307 | |
| 308 | + global $wp; | |
| 309 | + | |
| 308 | 310 | $calendarEvent = $booking->calendar_event; |
| 309 | - global $wp; | |
| 311 | + | |
| 310 | 312 | $responseHtml = BookingService::getBookingConfirmationHtml($booking, $actionType); |
| 311 | 313 | |
| 312 | 314 | $authorProfile = $calendarEvent->getAuthorProfile(true); |
| 313 | 315 | |
| 314 | - $publicCss = 'public/saas_public.css'; | |
| 315 | - if ($isRtl) { | |
| 316 | - $publicCss = 'public/saas_public-rtl.css'; | |
| 317 | - } | |
| 316 | + $publicCss = 'saas_public_css'; | |
| 317 | + | |
| 318 | 318 | $data = [ |
| 319 | 319 | 'title' => __('Confirmation: ', 'fluent-booking') . $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'], |
| 320 | 320 | 'body' => $responseHtml, |
| 321 | 321 | 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...', |
| 322 | 322 | 'css_files' => [ |
| 323 | - App::getInstance('url.assets') . $publicCss | |
| 323 | + Vite::styleUrl($publicCss) | |
| 324 | 324 | ], |
| 325 | 325 | 'js_files' => [], |
| 326 | 326 | 'js_vars' => [], |
| 327 | 327 | 'author' => $authorProfile, |
| @@ -327,15 +327,23 @@ | ||
| 327 | 327 | 'author' => $authorProfile, |
| 328 | 328 | 'slot' => $calendarEvent, |
| 329 | 329 | 'url' => home_url($wp->request), |
| 330 | 330 | 'action_type' => $actionType, |
| 331 | - 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default') | |
| 331 | + 'embedded' => isset($_GET['embedded']) ? true : false, // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 332 | + 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default'), | |
| 333 | + 'back_button' => [ | |
| 334 | + 'show' => true, | |
| 335 | + 'text' => __('Back to home', 'fluent-booking'), | |
| 336 | + 'url' => site_url('/') | |
| 337 | + ] | |
| 332 | 338 | ]; |
| 333 | 339 | |
| 334 | 340 | if ($actionType == 'cancel') { |
| 335 | - $data['js_files']['fluent-booking-public-manage-meeting-js'] = App::getInstance('url.assets') . 'public/js/public-manage-meeting.js'; | |
| 341 | + $data['js_files']['fluent-booking-public-manage-meeting-js'] = Vite::scriptUrl('manage_meeting'); | |
| 336 | 342 | } |
| 337 | 343 | |
| 344 | + $data = apply_filters('fluent_booking/booking_confirmation_page_vars', $data, $booking, $calendarEvent); | |
| 345 | + | |
| 338 | 346 | $app = App::getInstance(); |
| 339 | 347 | status_header(200); |
| 340 | 348 | $app->view->render('landing.confirmation_page', $data); |
| 341 | 349 | exit(200); |
| @@ -342,21 +350,20 @@ | ||
| 342 | 350 | } |
| 343 | 351 | |
| 344 | 352 | private function handleAfterBookingPage() |
| 345 | 353 | { |
| 346 | - $bookingHash = sanitize_text_field(Arr::get($_REQUEST, 'meeting_hash')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 347 | - | |
| 354 | + $bookingHash = sanitize_text_field(wp_unslash(Arr::get($_REQUEST, 'meeting_hash'))); // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 348 | 355 | if (!$bookingHash) { |
| 349 | 356 | return; |
| 350 | 357 | } |
| 351 | 358 | |
| 352 | 359 | $booking = Booking::where('hash', $bookingHash)->first(); |
| 353 | - | |
| 354 | 360 | if (!$booking) { |
| 355 | 361 | return; |
| 356 | 362 | } |
| 357 | 363 | |
| 358 | 364 | $type = Arr::get($_REQUEST, 'type', 'confirmation'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 365 | + | |
| 359 | 366 | $this->showBookingConfimationPage($booking, $type); |
| 360 | 367 | } |
| 361 | 368 | |
| 362 | 369 | private function handleRescheduleView(Booking $booking) |
| @@ -424,11 +431,29 @@ | ||
| 424 | 431 | </style> |
| 425 | 432 | <?php |
| 426 | 433 | }); |
| 427 | 434 | |
| 428 | - $this->renderBookingView($booking->calendar, $booking->calendar_event, $booking); | |
| 435 | + $this->renderBookingView($booking->calendar, $booking->calendar_event, $booking, true); | |
| 429 | 436 | } |
| 430 | 437 | |
| 438 | + private function handleEmbeddedStyle() | |
| 439 | + { | |
| 440 | + add_action('wp_print_styles', function () { | |
| 441 | + global $wp_styles; | |
| 442 | + if($wp_styles) { | |
| 443 | + foreach ($wp_styles->queue as $style) { | |
| 444 | + $src = $wp_styles->registered[$style]->src; | |
| 445 | + if ( | |
| 446 | + (strpos($src, 'fluentbooking') === false) && | |
| 447 | + (strpos($src, 'fluent_booking') === false) | |
| 448 | + ) { | |
| 449 | + wp_dequeue_style($wp_styles->registered[$style]->handle); | |
| 450 | + } | |
| 451 | + } | |
| 452 | + } | |
| 453 | + }, 100); | |
| 454 | + } | |
| 455 | + | |
| 431 | 456 | public function getEventLandingExtraJsFiles($formFields, $calendarEvent) |
| 432 | 457 | { |
| 433 | 458 | $files = []; |
| 434 | 459 | $assetUrl = App::getInstance('url.assets'); |
| @@ -433,9 +458,9 @@ | ||
| 433 | 458 | $files = []; |
| 434 | 459 | $assetUrl = App::getInstance('url.assets'); |
| 435 | 460 | |
| 436 | 461 | if (BookingFieldService::hasPhoneNumberField($formFields)) { |
| 437 | - $files['fluent-booking-phone-field-js'] = $assetUrl . 'public/js/phone-field.js'; | |
| 462 | + $files['fluent-booking-phone-field-js'] = Vite::scriptUrl('phone_field'); | |
| 438 | 463 | } |
| 439 | 464 | |
| 440 | 465 | if ($calendarEvent->type == 'paid') { |
| 441 | 466 | $files['fluent-booking-checkout-sdk-stripe-js'] = 'https://js.stripe.com/v3/'; |