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

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