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 / Services / LandingPage / LandingPageHandler.php

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

445 lines 16.4 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 $data = [
228 'calendar' => $calendar,
229 'calendar_event' => $calendarEvent,
230 'author' => $authorProfile,
231 'title' => $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'],
232 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
233 'url' => home_url($wp->request),
234 'css_files' => [
235 $assetUrl . $publicCss
236 ],
237 'js_files' => [
238 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js',
239 ],
240 'js_vars' => [
241 'fluentCalendarPublicVars' => (new FrontEndHandler())->getGlobalVars(),
242 'fcal_public_vars_' . $calendar->id . '_' . $calendarEvent->id => $eventVars,
243 ]
244 ];
245
246 $extraJs = $this->getEventLandingExtraJsFiles($eventVars['form_fields'], $calendarEvent);
247
248 if ($extraJs) {
249 $data['js_files'] = wp_parse_args($data['js_files'], $extraJs);
250 add_action('fluent_booking/author_landing_head', function () use ($assetUrl) {
251 ?>
252 <style>
253 .fcal_phone_wrapper .flag {
254 background: url(<?php echo esc_url($assetUrl.'images/flags_responsive.png'); ?>) no-repeat;
255 background-size: 100%;
256 }
257 </style>
258 <?php
259 });
260 }
261
262 $data = apply_filters('fluent_booking/event_landing_page_vars', $data, $calendar, $calendarEvent, $existingBooking);
263
264 $app = App::getInstance();
265
266 status_header(200);
267 $app->view->render('landing.booking', $data);
268 exit(200);
269 }
270
271 private function showBookingConfimationPage($booking, $actionType = 'confirmation')
272 {
273 $validActions = ['confirmation'];
274
275 if (in_array($booking->status, ['scheduled', 'pending', 'rescheduled'])) {
276 $validActions = array_merge($validActions, ['reschedule', 'cancel']);
277 }
278
279 if (!in_array($actionType, $validActions)) {
280 $actionType = 'confirmation';
281 }
282
283 if ($actionType == 'reschedule') {
284 $this->handleRescheduleView($booking);
285 }
286
287 if ($actionType == 'confirmation' && !empty($_REQUEST['ics']) && $_REQUEST['ics'] == 'download') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
288 $icsText = BookingService::generateBookingICS($booking);
289 // Output the ICS text
290 header('Content-Type: text/calendar; charset=utf-8');
291 header('Content-Disposition: attachment; filename=event.ics');
292 echo $icsText; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
293 die();
294 }
295
296 global $wp;
297
298 $calendarEvent = $booking->calendar_event;
299
300 $responseHtml = BookingService::getBookingConfirmationHtml($booking, $actionType);
301
302 $authorProfile = $calendarEvent->getAuthorProfile(true);
303
304 $publicCss = 'public/saas_public.css';
305 if (Helper::fluentbooking_is_rtl()) {
306 $publicCss = 'public/saas_public-rtl.css';
307 }
308
309 $data = [
310 'title' => __('Confirmation: ', 'fluent-booking') . $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'],
311 'body' => $responseHtml,
312 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
313 'css_files' => [
314 App::getInstance('url.assets') . $publicCss
315 ],
316 'js_files' => [],
317 'js_vars' => [],
318 'author' => $authorProfile,
319 'slot' => $calendarEvent,
320 'url' => home_url($wp->request),
321 'action_type' => $actionType,
322 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default'),
323 'back_button' => [
324 'show' => true,
325 'text' => __('Back to home', 'fluent-booking'),
326 'url' => site_url('/')
327 ]
328 ];
329
330 if ($actionType == 'cancel') {
331 $data['js_files']['fluent-booking-public-manage-meeting-js'] = App::getInstance('url.assets') . 'public/js/public-manage-meeting.js';
332 }
333
334 $data = apply_filters('fluent_booking/booking_confirmation_page_vars', $data, $booking, $calendarEvent);
335
336 $app = App::getInstance();
337 status_header(200);
338 $app->view->render('landing.confirmation_page', $data);
339 exit(200);
340 }
341
342 private function handleAfterBookingPage()
343 {
344 $bookingHash = sanitize_text_field(Arr::get($_REQUEST, 'meeting_hash')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
345 if (!$bookingHash) {
346 return;
347 }
348
349 $booking = Booking::where('hash', $bookingHash)->first();
350 if (!$booking) {
351 return;
352 }
353
354 $type = Arr::get($_REQUEST, 'type', 'confirmation'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
355
356 $this->showBookingConfimationPage($booking, $type);
357 }
358
359 private function handleRescheduleView(Booking $booking)
360 {
361 add_filter('fluent_booking/public_event_vars', function ($eventVars) use ($booking) {
362 $onlyFields = ['name', 'email', 'rescheduling_reason'];
363
364 $formFields = $eventVars['form_fields'];
365
366 $formFields = Collection::make($formFields)->filter(function ($field) use ($onlyFields) {
367 return in_array($field['name'], $onlyFields);
368 })->map(function ($item) {
369 $item['disabled'] = $item['name'] == 'rescheduling_reason' ? false : true;
370 return $item;
371 })->toArray();
372
373 $formFields[] = [
374 'type' => 'hidden',
375 'name' => 'rescheduling_hash',
376 'enabled' => true
377 ];
378
379 $eventVars['rescheduling'] = 'yes';
380 $eventVars['form_fields'] = array_values($formFields);
381 unset($eventVars['payment_items']);
382 unset($eventVars['payment_methods']);
383
384 return $eventVars;
385 }, 10, 1);
386
387 add_filter('fluent_calendar/global_booking_vars', function ($vars) use ($booking) {
388 $vars['current_person'] = [
389 'name' => trim($booking->first_name . ' ' . $booking->last_name),
390 'email' => $booking->email,
391 'rescheduling_hash' => $booking->hash
392 ];
393
394 return $vars;
395 }, 10, 1);
396
397 add_filter('fluent_booking/public_event_vars', function ($vars, $calendarEvent) {
398 $vars['i18']['Schedule_Meeting'] = __('Confirm Reschedule', 'fluent-booking');
399 $vars['i18']['Continue_to_Payments'] = __('Confirm Reschedule', 'fluent-booking');
400 $vars['i18']['Confirm_Payment'] = __('Confirm Reschedule', 'fluent-booking');
401 return $vars;
402 }, 10, 2);
403
404 add_action('fluent_booking/before_calendar_event_landing_page', function ($calendarEvent) use ($booking) {
405 ?>
406 <div class="fcal_rescheduling_wrap">
407 <h3> <?php esc_html_e('You are rescheduling the booking: ', 'fluent-booking');
408 echo wp_kses_post($booking->getFullBookingDateTimeText($booking->person_time_zone, true)); ?>
409 (<?php echo esc_html($booking->person_time_zone); ?>) </h3>
410 </div>
411 <?php
412 }, 10, 1);
413
414 add_action('fluent_booking/author_landing_head', function () {
415 ?>
416 <style>
417 .fluent_booking_app {
418 margin-top: 0 !important;
419 padding-top: 0 !important;
420 }
421 </style>
422 <?php
423 });
424
425 $this->renderBookingView($booking->calendar, $booking->calendar_event, $booking, true);
426 }
427
428 public function getEventLandingExtraJsFiles($formFields, $calendarEvent)
429 {
430 $files = [];
431 $assetUrl = App::getInstance('url.assets');
432
433 if (BookingFieldService::hasPhoneNumberField($formFields)) {
434 $files['fluent-booking-phone-field-js'] = $assetUrl . 'public/js/phone-field.js';
435 }
436
437 if ($calendarEvent->type == 'paid') {
438 $files['fluent-booking-checkout-sdk-stripe-js'] = 'https://js.stripe.com/v3/';
439 $files['fluent-booking-checkout-handler-stripe-js'] = $assetUrl . 'public/js/stripe-checkout.js';
440 }
441
442 return $files;
443 }
444 }
445