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.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 1.7.2 All 33 releases
fluent-booking / app / Services / LandingPage / LandingPageHandler.php

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

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