PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.01
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.01
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.01, at app/Services/LandingPage/LandingPageHandler.php

448 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 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 }
119
120 $metaDescription = Helper::excerpt($calendar->description);
121
122 $calendar->description = wpautop($calendar->description);
123
124 $authorProfile = $calendar->getAuthorProfile(true);
125
126 $globalVars = (new FrontEndHandler())->getGlobalVars();
127
128 $currentUrl = home_url($wp->request);
129
130 $globalVars['is_landing_page'] = true;
131 $globalVars['is_pretty_url'] = defined('FLUENT_BOOKING_LANDING_SLUG');
132 $globalVars['base_url'] = rtrim($currentUrl, '/');
133
134 $jsVars = [
135 'fluentCalendarPublicVars' => $globalVars,
136 'fcal_landing_page' => true
137 ];
138
139 $extraJsFiles = [];
140
141 foreach ($activeEvents as $activeEvent) {
142 $event = clone $activeEvent;
143 $vars = (new FrontEndHandler())->getCalendarEventVars($calendar, $event);
144 $extraJs = $this->getEventLandingExtraJsFiles($vars['form_fields'], $event);
145 if ($extraJs) {
146 $vars['lazy_js_files'] = $extraJs;
147 }
148 $jsVars['fcal_public_vars_' . $calendar->id . '_' . $activeEvent->id] = $vars;
149 }
150
151 $assetUrl = App::getInstance('url.assets');
152 $data = [
153 'calendar' => $calendar,
154 'events' => $activeEvents,
155 'author' => $authorProfile,
156 'title' => $calendar->title .' - '.get_bloginfo('name'),
157 'description' => $metaDescription,
158 'url' => home_url($wp->request),
159 'css_files' => [
160 App::getInstance('url.assets') . 'public/saas.css'
161 ],
162 'js_files' => [
163 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js',
164 ],
165 'js_vars' => $jsVars,
166 'header_js_files' => [
167 'fluent_booking_team_app-js' => $assetUrl. 'public/js/team_app.js'
168 ]
169 ];
170
171 if ($extraJsFiles) {
172 $extraJsFiles = array_unique($extraJsFiles);
173 $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 });
184
185 }
186
187 $app = App::getInstance();
188 status_header(200);
189 $app->view->render('landing.author_landing', $data);
190 exit(200);
191 }
192
193 private function renderBookingView($calendar, $calendarEvent, $existingBooking = null)
194 {
195 $settings = LandingPageHelper::getSettings($calendar, 'public');
196 if ($settings['show_type'] != 'all') {
197 if (!in_array($calendarEvent->id, $settings['enabled_slots'])) {
198 return '';
199 }
200 }
201
202 global $wp;
203
204 $calendarEvent->max_lookup_date = $calendarEvent->getMaxLookUpDate();
205 $calendarEvent->min_lookup_date = $calendarEvent->getMinLookUpDate();
206
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
209 $booking = Booking::where('hash', $bookingHash)
210 ->where('event_id', $calendarEvent->id)
211 ->first();
212 if ($booking) {
213 $this->showBookingConfimationPage($booking, $calendarEvent);
214 }
215 }
216
217 $authorProfile = $calendarEvent->getAuthorProfile(true);
218
219 $calendarEvent->pre_selects = false;
220
221 if (gmdate('m') != gmdate('m', strtotime($calendarEvent->min_lookup_date))) { // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
222 $calendarEvent->pre_selects = [
223 'month' => gmdate('m', strtotime($calendarEvent->min_lookup_date)), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
224 'year' => gmdate('Y', strtotime($calendarEvent->min_lookup_date)) // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
225 ];
226 }
227
228 $assetUrl = App::getInstance('url.assets');
229
230 $eventVars = (new FrontEndHandler())->getCalendarEventVars($calendar, $calendarEvent);
231
232 $isRtl = Helper::fluentbooking_is_rtl();
233
234 $publicCss = 'public/saas.css';
235 if ($isRtl) {
236 $publicCss = 'public/saas-rtl.css';
237 }
238 $data = [
239 'calendar' => $calendar,
240 'calendar_event' => $calendarEvent,
241 'author' => $authorProfile,
242 'title' => $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'],
243 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
244 'url' => home_url($wp->request),
245 'css_files' => [
246 $assetUrl . $publicCss
247 ],
248 'js_files' => [
249 'fluent-booking-public-js' => $assetUrl . 'public/js/app.js',
250 ],
251 'js_vars' => [
252 'fluentCalendarPublicVars' => (new FrontEndHandler())->getGlobalVars(),
253 'fcal_public_vars_' . $calendar->id . '_' . $calendarEvent->id => $eventVars,
254 ]
255 ];
256
257 $extraJs = $this->getEventLandingExtraJsFiles($eventVars['form_fields'], $calendarEvent);
258
259 if ($extraJs) {
260 $data['js_files'] = wp_parse_args($data['js_files'], $extraJs);
261 add_action('fluent_booking/author_landing_head', function () use ($assetUrl) {
262 ?>
263 <style>
264 .fcal_phone_wrapper .flag {
265 background: url(<?php echo esc_url($assetUrl.'images/flags_responsive.png'); ?>) no-repeat;
266 background-size: 100%;
267 }
268 </style>
269 <?php
270 });
271 }
272
273 $data = apply_filters('fluent_booking/event_landing_page_vars', $data, $calendar, $calendarEvent, $existingBooking);
274
275 $app = App::getInstance();
276
277 status_header(200);
278 $app->view->render('landing.booking', $data);
279 exit(200);
280 }
281
282 private function showBookingConfimationPage($booking, $actionType = 'confirmation')
283 {
284 $validActions = ['confirmation'];
285 $isRtl = Helper::fluentbooking_is_rtl();
286
287 if (in_array($booking->status, ['scheduled', 'pending', 'rescheduled'])) {
288 $validActions = array_merge($validActions, ['reschedule', 'cancel']);
289 }
290
291 if (!in_array($actionType, $validActions)) {
292 $actionType = 'confirmation';
293 }
294
295 if ($actionType == 'reschedule') {
296 $this->handleRescheduleView($booking);
297 }
298
299 if ($actionType == 'confirmation' && !empty($_REQUEST['ics']) && $_REQUEST['ics'] == 'download') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
300 $icsText = BookingService::generateBookingICS($booking);
301 // Output the ICS text
302 header('Content-Type: text/calendar; charset=utf-8');
303 header('Content-Disposition: attachment; filename=event.ics');
304 echo $icsText; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
305 die();
306 }
307
308 $calendarEvent = $booking->calendar_event;
309 global $wp;
310 $responseHtml = BookingService::getBookingConfirmationHtml($booking, $actionType);
311
312 $authorProfile = $calendarEvent->getAuthorProfile(true);
313
314 $publicCss = 'public/saas_public.css';
315 if ($isRtl) {
316 $publicCss = 'public/saas_public-rtl.css';
317 }
318 $data = [
319 'title' => __('Confirmation: ', 'fluent-booking') . $calendarEvent->title . ' ' . __('with', 'fluent-booking') . ' ' . $authorProfile['name'],
320 'body' => $responseHtml,
321 'description' => substr(strip_shortcodes(wp_strip_all_tags(str_replace(PHP_EOL, ' ', $calendarEvent->description))), 0, 300) . '...',
322 'css_files' => [
323 App::getInstance('url.assets') . $publicCss
324 ],
325 'js_files' => [],
326 'js_vars' => [],
327 'author' => $authorProfile,
328 'slot' => $calendarEvent,
329 'url' => home_url($wp->request),
330 'action_type' => $actionType,
331 'theme' => Arr::get(get_option('_fluent_booking_settings'), 'theme','system-default')
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 $app = App::getInstance();
339 status_header(200);
340 $app->view->render('landing.confirmation_page', $data);
341 exit(200);
342 }
343
344 private function handleAfterBookingPage()
345 {
346 $bookingHash = sanitize_text_field(Arr::get($_REQUEST, 'meeting_hash')); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
347
348 if (!$bookingHash) {
349 return;
350 }
351
352 $booking = Booking::where('hash', $bookingHash)->first();
353
354 if (!$booking) {
355 return;
356 }
357
358 $type = Arr::get($_REQUEST, 'type', 'confirmation'); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
359 $this->showBookingConfimationPage($booking, $type);
360 }
361
362 private function handleRescheduleView(Booking $booking)
363 {
364 add_filter('fluent_booking/public_event_vars', function ($eventVars) use ($booking) {
365 $onlyFields = ['name', 'email', 'rescheduling_reason'];
366
367 $formFields = $eventVars['form_fields'];
368
369 $formFields = Collection::make($formFields)->filter(function ($field) use ($onlyFields) {
370 return in_array($field['name'], $onlyFields);
371 })->map(function ($item) {
372 $item['disabled'] = $item['name'] == 'rescheduling_reason' ? false : true;
373 return $item;
374 })->toArray();
375
376 $formFields[] = [
377 'type' => 'hidden',
378 'name' => 'rescheduling_hash',
379 'enabled' => true
380 ];
381
382 $eventVars['rescheduling'] = 'yes';
383 $eventVars['form_fields'] = array_values($formFields);
384 unset($eventVars['payment_items']);
385 unset($eventVars['payment_methods']);
386
387 return $eventVars;
388 }, 10, 1);
389
390 add_filter('fluent_calendar/global_booking_vars', function ($vars) use ($booking) {
391 $vars['current_person'] = [
392 'name' => trim($booking->first_name . ' ' . $booking->last_name),
393 'email' => $booking->email,
394 'rescheduling_hash' => $booking->hash
395 ];
396
397 return $vars;
398 }, 10, 1);
399
400 add_filter('fluent_booking/public_event_vars', function ($vars, $calendarEvent) {
401 $vars['i18']['Schedule_Meeting'] = __('Confirm Reschedule', 'fluent-booking');
402 $vars['i18']['Continue_to_Payments'] = __('Confirm Reschedule', 'fluent-booking');
403 $vars['i18']['Confirm_Payment'] = __('Confirm Reschedule', 'fluent-booking');
404 return $vars;
405 }, 10, 2);
406
407 add_action('fluent_booking/before_calendar_event_landing_page', function ($calendarEvent) use ($booking) {
408 ?>
409 <div class="fcal_rescheduling_wrap">
410 <h3> <?php esc_html_e('You are rescheduling the booking: ', 'fluent-booking');
411 echo wp_kses_post($booking->getFullBookingDateTimeText($booking->person_time_zone, true)); ?>
412 (<?php echo esc_html($booking->person_time_zone); ?>) </h3>
413 </div>
414 <?php
415 }, 10, 1);
416
417 add_action('fluent_booking/author_landing_head', function () {
418 ?>
419 <style>
420 .fluent_booking_app {
421 margin-top: 0 !important;
422 padding-top: 0 !important;
423 }
424 </style>
425 <?php
426 });
427
428 $this->renderBookingView($booking->calendar, $booking->calendar_event, $booking);
429 }
430
431 public function getEventLandingExtraJsFiles($formFields, $calendarEvent)
432 {
433 $files = [];
434 $assetUrl = App::getInstance('url.assets');
435
436 if (BookingFieldService::hasPhoneNumberField($formFields)) {
437 $files['fluent-booking-phone-field-js'] = $assetUrl . 'public/js/phone-field.js';
438 }
439
440 if ($calendarEvent->type == 'paid') {
441 $files['fluent-booking-checkout-sdk-stripe-js'] = 'https://js.stripe.com/v3/';
442 $files['fluent-booking-checkout-handler-stripe-js'] = $assetUrl . 'public/js/stripe-checkout.js';
443 }
444
445 return $files;
446 }
447 }
448