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

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