PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.7.1
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.7.1
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 / Services / LandingPage / LandingPageHandler.php

LandingPageHandler.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.7.1, at app/Services/LandingPage/LandingPageHandler.php

449 lines 16.6 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\Services\LandingPage;
4
5 use FluentBooking\App\App;
6 use FluentBooking\App\Hooks\Handlers\FrontEndHandler;
7 use FluentBooking\App\Models\Booking;
8 use FluentBooking\App\Models\Calendar;
9 use FluentBooking\App\Models\CalendarSlot;
10 use FluentBooking\App\Services\CalendarEventService;
11 use FluentBooking\App\Services\BookingFieldService;
12 use FluentBooking\App\Services\BookingService;
13 use FluentBooking\App\Services\Helper;
14 use FluentBooking\Framework\Support\Arr;
15 use FluentBooking\Framework\Support\Collection;
16
17 class LandingPageHandler
18 {
19 public function boot()
20 {
21 if (defined('FLUENT_BOOKING_LANDING_SLUG')) {
22 add_action('template_redirect', [$this, 'handleSlugDefinedPage'], 1);
23 }
24
25 if (isset($_GET['fluent-booking'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
26 add_action('init', [$this, 'handleUrlParamsPage'], 100);
27 }
28 }
29
30 public function handleSlugDefinedPage()
31 {
32 global $wp;
33 // remove starting / and end / from $uri
34 $uri = trim($wp->request, '/');
35 $urlParts = explode('/', $uri);
36
37 if ($urlParts[0] != FLUENT_BOOKING_LANDING_SLUG || count($urlParts) < 2) {
38 return;
39 }
40
41 $authorSlug = sanitize_text_field($urlParts[1]);
42
43 $this->routeView($authorSlug, Arr::get($urlParts, 2, null));
44 }
45
46 public function handleUrlParamsPage()
47 {
48 $route = sanitize_text_field($_GET['fluent-booking']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
49
50 if ($route == 'booking') {
51 $this->handleAfterBookingPage();
52 return;
53 }
54
55 $request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
56
57 if (empty($request['host'])) {
58 do_action('fluent_booking/landing_page_route_' . $route, $request);
59 return;
60 }
61
62 $authorSlug = sanitize_text_field($request['host']);
63
64 $slotSlug = null;
65
66 if (!empty($request['event'])) {
67 $slotSlug = sanitize_text_field($request['event']);
68 }
69
70 $this->routeView($authorSlug, $slotSlug);
71 }
72
73 public function routeView($authorSlug, $slotSlug = null)
74 {
75 $calendar = Calendar::where('slug', $authorSlug)->first();
76
77 if (!$calendar) {
78 return;
79 }
80
81 $sharingSettings = LandingPageHelper::getSettings($calendar);
82
83 if (Arr::get($sharingSettings, 'enabled') != 'yes') {
84 return '';
85 }
86
87 if ($slotSlug) {
88 $slot = CalendarSlot::where('calendar_id', $calendar->id)
89 ->where('slug', $slotSlug)
90 ->first();
91 if (!$slot) {
92 return;
93 }
94 $this->renderBookingView($calendar, $slot);
95 }
96
97 $this->renderHostView($calendar);
98 }
99
100 private function renderHostView($calendar)
101 {
102 global $wp;
103 $settings = LandingPageHelper::getSettings($calendar, 'public');
104
105 $activeEvents = CalendarSlot::where('calendar_id', $calendar->id)
106 ->where('status', 'active');
107
108 if ($settings['show_type'] != 'all') {
109 $activeEvents = $activeEvents->whereIn('id', $settings['enabled_slots']);
110 }
111
112 $activeEvents = $activeEvents->get();
113
114 $activeEvents = CalendarEventService::processEvents($calendar, $activeEvents);
115
116 $metaDescription = Helper::excerpt($calendar->description);
117
118 $calendar->description = wpautop($calendar->description);
119
120 $authorProfile = $calendar->getAuthorProfile(true);
121
122 $globalVars = (new FrontEndHandler())->getGlobalVars();
123
124 $currentUrl = home_url($wp->request);
125
126 $globalVars['is_landing_page'] = true;
127 $globalVars['is_pretty_url'] = defined('FLUENT_BOOKING_LANDING_SLUG');
128 $globalVars['base_url'] = rtrim($currentUrl, '/');
129
130 $jsVars = [
131 'fluentCalendarPublicVars' => $globalVars,
132 'fcal_landing_page' => true
133 ];
134
135 $extraJsFiles = [];
136
137 foreach ($activeEvents as $activeEvent) {
138 $event = clone $activeEvent;
139 $vars = (new FrontEndHandler())->getCalendarEventVars($calendar, $event);
140 $extraJs = $this->getEventLandingExtraJsFiles($vars['form_fields'], $event);
141 if ($extraJs) {
142 $vars['lazy_js_files'] = $extraJs;
143 }
144 $jsVars['fcal_public_vars_' . $calendar->id . '_' . $activeEvent->id] = $vars;
145 $activeEvent->locations = $activeEvent->defaultLocationHtml();
146 do_action_ref_array('fluent_booking/landing_page_event', [&$activeEvent]);
147 }
148
149 $assetUrl = App::getInstance('url.assets');
150 $data = [
151 'calendar' => $calendar,
152 'events' => $activeEvents,
153 'author' => $authorProfile,
154 'title' => $calendar->title .' - '.get_bloginfo('name'),
155 'description' => $metaDescription,
156 'url' => home_url($wp->request),
157 'css_files' => [
158 App::getInstance('url.assets') . 'public/saas.css'
159 ],
160 'js_files' => [
161 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js',
162 ],
163 'js_vars' => $jsVars,
164 'header_js_files' => [
165 'fluent_booking_team_app-js' => $assetUrl. 'public/js/team_app.js'
166 ]
167 ];
168
169 if ($extraJsFiles) {
170 $extraJsFiles = array_unique($extraJsFiles);
171 $data['js_files'] = array_merge($data['js_files'], $extraJsFiles);
172 }
173
174 $data = apply_filters('fluent_booking/host_view_page_vars', $data, $calendar, $activeEvents, $authorProfile);
175
176 $app = App::getInstance();
177 status_header(200);
178 $app->view->render('landing.author_landing', $data);
179 exit(200);
180 }
181
182 private function renderBookingView($calendar, $calendarEvent, $existingBooking = null, $isReschedule = false)
183 {
184 if (!$isReschedule) {
185 $settings = LandingPageHelper::getSettings($calendar, 'public');
186 if ($settings['show_type'] != 'all') {
187 if (!in_array($calendarEvent->id, $settings['enabled_slots'])) {
188 return '';
189 }
190 }
191 }
192
193 global $wp;
194
195 $calendarEvent->max_lookup_date = $calendarEvent->getMaxLookUpDate();
196 $calendarEvent->min_lookup_date = $calendarEvent->getMinLookUpDate();
197
198 if (!empty($_REQUEST['booking_id'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
199 $bookingHash = sanitize_text_field($_REQUEST['booking_id']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
200 $booking = Booking::where('hash', $bookingHash)
201 ->where('event_id', $calendarEvent->id)
202 ->first();
203 if ($booking) {
204 $this->showBookingConfimationPage($booking, $calendarEvent);
205 }
206 }
207
208 $authorProfile = $calendarEvent->getAuthorProfile(true);
209
210 $calendarEvent->pre_selects = false;
211
212 if (gmdate('m') != gmdate('m', strtotime($calendarEvent->min_lookup_date))) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
213 $calendarEvent->pre_selects = [
214 'month' => gmdate('m', strtotime($calendarEvent->min_lookup_date)), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
215 'year' => gmdate('Y', strtotime($calendarEvent->min_lookup_date)) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
216 ];
217 }
218
219 $assetUrl = App::getInstance('url.assets');
220
221 $eventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent);
222
223 $isRtl = Helper::fluentbooking_is_rtl();
224
225 $publicCss = $isRtl ? 'public/saas-rtl.css' : 'public/saas.css';
226
227 $title = $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'];
228
229 $title = apply_filters('fluent_booking/booking_confirmation_page_title', $title, $calendarEvent, $authorProfile);
230
231 $data = [
232 'calendar' => $calendar,
233 'calendar_event' => $calendarEvent,
234 'author' => $authorProfile,
235 'title' => $title,
236 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
237 'url' => home_url($wp->request),
238 'css_files' => [
239 $assetUrl . $publicCss
240 ],
241 'js_files' => [
242 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js',
243 ],
244 'js_vars' => [
245 'fluentCalendarPublicVars' => (new FrontEndHandler())->getGlobalVars(),
246 'fcal_public_vars_' . $calendar->id . '_' . $calendarEvent->id => $eventVars,
247 ]
248 ];
249
250 $extraJs = $this->getEventLandingExtraJsFiles($eventVars['form_fields'], $calendarEvent);
251
252 if ($extraJs) {
253 $data['js_files'] = wp_parse_args($data['js_files'], $extraJs);
254 add_action('fluent_booking/author_landing_head', function () use ($assetUrl) {
255 ?>
256 <style>
257 .fcal_phone_wrapper .flag {
258 background: url(<?php echo esc_url($assetUrl.'images/flags_responsive.png'); ?>) no-repeat;
259 background-size: 100%;
260 }
261 </style>
262 <?php
263 });
264 }
265
266 $data = apply_filters('fluent_booking/event_landing_page_vars', $data, $calendar, $calendarEvent, $existingBooking);
267
268 $app = App::getInstance();
269
270 status_header(200);
271 $app->view->render('landing.booking', $data);
272 exit(200);
273 }
274
275 private function showBookingConfimationPage($booking, $actionType = 'confirmation')
276 {
277 $validActions = ['confirmation'];
278
279 if (in_array($booking->status, ['scheduled', 'pending', 'rescheduled'])) {
280 $validActions = array_merge($validActions, ['reschedule', 'cancel']);
281 }
282
283 if (!in_array($actionType, $validActions)) {
284 $actionType = 'confirmation';
285 }
286
287 if ($actionType == 'reschedule') {
288 $this->handleRescheduleView($booking);
289 }
290
291 if ($actionType == 'confirmation' && !empty($_REQUEST['ics']) && $_REQUEST['ics'] == 'download') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
292 $icsText = BookingService::generateBookingICS($booking);
293 // Output the ICS text
294 header('Content-Type: text/calendar; charset=utf-8');
295 header('Content-Disposition: attachment; filename=event.ics');
296 echo $icsText; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
297 die();
298 }
299
300 global $wp;
301
302 $calendarEvent = $booking->calendar_event;
303
304 $responseHtml = BookingService::getBookingConfirmationHtml($booking, $actionType);
305
306 $authorProfile = $calendarEvent->getAuthorProfile(true);
307
308 $publicCss = 'public/saas_public.css';
309 if (Helper::fluentbooking_is_rtl()) {
310 $publicCss = 'public/saas_public-rtl.css';
311 }
312
313 $data = [
314 'title' => __('Confirmation: ', 'fluent-booking') . $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'],
315 'body' => $responseHtml,
316 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
317 'css_files' => [
318 App::getInstance('url.assets') . $publicCss
319 ],
320 'js_files' => [],
321 'js_vars' => [],
322 'author' => $authorProfile,
323 'slot' => $calendarEvent,
324 'url' => home_url($wp->request),
325 'action_type' => $actionType,
326 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default'),
327 'back_button' => [
328 'show' => true,
329 'text' => __('Back to home', 'fluent-booking'),
330 'url' => site_url('/')
331 ]
332 ];
333
334 if ($actionType == 'cancel') {
335 $data['js_files']['fluent-booking-public-manage-meeting-js'] = App::getInstance('url.assets') . 'public/js/public-manage-meeting.js';
336 }
337
338 $data = apply_filters('fluent_booking/booking_confirmation_page_vars', $data, $booking, $calendarEvent);
339
340 $app = App::getInstance();
341 status_header(200);
342 $app->view->render('landing.confirmation_page', $data);
343 exit(200);
344 }
345
346 private function handleAfterBookingPage()
347 {
348 $bookingHash = sanitize_text_field(Arr::get($_REQUEST, 'meeting_hash')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
349 if (!$bookingHash) {
350 return;
351 }
352
353 $booking = Booking::where('hash', $bookingHash)->first();
354 if (!$booking) {
355 return;
356 }
357
358 $type = Arr::get($_REQUEST, 'type', 'confirmation'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
359
360 $this->showBookingConfimationPage($booking, $type);
361 }
362
363 private function handleRescheduleView(Booking $booking)
364 {
365 add_filter('fluent_booking/public_event_vars', function ($eventVars) use ($booking) {
366 $onlyFields = ['name', 'email', 'rescheduling_reason'];
367
368 $formFields = $eventVars['form_fields'];
369
370 $formFields = Collection::make($formFields)->filter(function ($field) use ($onlyFields) {
371 return in_array($field['name'], $onlyFields);
372 })->map(function ($item) {
373 $item['disabled'] = $item['name'] == 'rescheduling_reason' ? false : true;
374 return $item;
375 })->toArray();
376
377 $formFields[] = [
378 'type' => 'hidden',
379 'name' => 'rescheduling_hash',
380 'enabled' => true
381 ];
382
383 $eventVars['rescheduling'] = 'yes';
384 $eventVars['form_fields'] = array_values($formFields);
385 unset($eventVars['payment_items']);
386 unset($eventVars['payment_methods']);
387
388 return $eventVars;
389 }, 10, 1);
390
391 add_filter('fluent_calendar/global_booking_vars', function ($vars) use ($booking) {
392 $vars['current_person'] = [
393 'name' => trim($booking->first_name . ' ' . $booking->last_name),
394 'email' => $booking->email,
395 'rescheduling_hash' => $booking->hash
396 ];
397
398 return $vars;
399 }, 10, 1);
400
401 add_filter('fluent_booking/public_event_vars', function ($vars, $calendarEvent) {
402 $vars['i18']['Schedule_Meeting'] = __('Confirm Reschedule', 'fluent-booking');
403 $vars['i18']['Continue_to_Payments'] = __('Confirm Reschedule', 'fluent-booking');
404 $vars['i18']['Confirm_Payment'] = __('Confirm Reschedule', 'fluent-booking');
405 return $vars;
406 }, 10, 2);
407
408 add_action('fluent_booking/before_calendar_event_landing_page', function ($calendarEvent) use ($booking) {
409 ?>
410 <div class="fcal_rescheduling_wrap">
411 <h3> <?php esc_html_e('You are rescheduling the booking: ', 'fluent-booking');
412 echo wp_kses_post($booking->getFullBookingDateTimeText($booking->person_time_zone, true)); ?>
413 (<?php echo esc_html($booking->person_time_zone); ?>) </h3>
414 </div>
415 <?php
416 }, 10, 1);
417
418 add_action('fluent_booking/author_landing_head', function () {
419 ?>
420 <style>
421 .fluent_booking_app {
422 margin-top: 0 !important;
423 padding-top: 0 !important;
424 }
425 </style>
426 <?php
427 });
428
429 $this->renderBookingView($booking->calendar, $booking->calendar_event, $booking, true);
430 }
431
432 public function getEventLandingExtraJsFiles($formFields, $calendarEvent)
433 {
434 $files = [];
435 $assetUrl = App::getInstance('url.assets');
436
437 if (BookingFieldService::hasPhoneNumberField($formFields)) {
438 $files['fluent-booking-phone-field-js'] = $assetUrl . 'public/js/phone-field.js';
439 }
440
441 if ($calendarEvent->type == 'paid') {
442 $files['fluent-booking-checkout-sdk-stripe-js'] = 'https://js.stripe.com/v3/';
443 $files['fluent-booking-checkout-handler-stripe-js'] = $assetUrl . 'public/js/stripe-checkout.js';
444 }
445
446 return $files;
447 }
448 }
449