PluginProbe
Booking Calendar / 11.2.1
Booking Calendar v11.2.1
11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 10.11.3 All 202 releases
booking / includes / _front_end / class-fe-shortcodes.php

class-fe-shortcodes.php in Booking Calendar 11.2.1, at includes/_front_end/class-fe-shortcodes.php

411 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 * Front-End Shortcodes (Phase #1)
4 *
5 * File: /wp-content/plugins/booking/includes/_front_end/class-fe-shortcodes.php
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12
13 class WPBC_FE_Shortcodes {
14
15 /**
16 * Boot.
17 */
18 public static function init() {
19
20 // Register after legacy (legacy uses init priority 9999).
21 add_action( 'init', array( __CLASS__, 'register_shortcodes' ), 10000 );
22 }
23
24 /**
25 * Register the shortcodes we move in Phase #1.
26 */
27 public static function register_shortcodes() {
28
29 /**
30 * [booking]
31 * - WPBC_FE_Shortcodes::booking_shortcode()
32 * - WPBC_FE_Render::render_booking_form()
33 * - WPBC_FE_Form_Body_Renderer::render()
34 * - WPBC_FE_Form_Source::get_form_body_html()
35 * - WPBC_FE_Form_Source_Resolver::resolve() | (engine bfb_db)
36 * - wpbc_bfb_get_booking_form_pair()
37 * - wpbc_render_booking_form_shortcodes()
38 * - WPBC_BFB_FormShortcodeEngine->render().
39 */
40 add_shortcode( 'booking', array( __CLASS__, 'booking_shortcode' ) );
41 add_shortcode( 'booking_popup', array( __CLASS__, 'booking_popup_shortcode' ) );
42 add_shortcode( 'bookingcalendar', array( __CLASS__, 'booking_calendar_only_shortcode' ) );
43 add_shortcode( 'bookingform', array( __CLASS__, 'bookingform_shortcode' ) );
44 }
45
46 // ---------------------------------------------------------------------
47 // Helpers
48 // ---------------------------------------------------------------------
49
50 private static function force_attr_array( $attr ) {
51 return is_array( $attr ) ? $attr : array();
52 }
53
54 private static function maybe_escape_shortcode_params( $attr ) {
55
56 // This function sanitize each value in attr array by using WP function: sanitize_text_field().
57 $attr = wpbc_escape_shortcode_params( $attr );
58
59 return is_array( $attr ) ? $attr : array();
60 }
61
62 /**
63 * Check shortcode parameter for a boolean-like true value.
64 *
65 * @param array $attr Shortcode attributes.
66 * @param string $key Attribute key.
67 *
68 * @return bool
69 */
70 private static function is_truthy_attr( $attr, $key ) {
71
72 if ( ! isset( $attr[ $key ] ) ) {
73 return false;
74 }
75
76 $value = strtolower( trim( (string) $attr[ $key ] ) );
77
78 return in_array( $value, array( '1', 'true', 'yes', 'on' ), true );
79 }
80
81 /**
82 * Check if the current render is happening inside Elementor editor/preview.
83 *
84 * Elementor can render widgets through admin AJAX, where is_admin() is true
85 * even though the output is displayed in the front-end canvas iframe.
86 *
87 * @return bool
88 */
89 private static function is_elementor_render_context() {
90
91 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing
92 $action = isset( $_REQUEST['action'] ) ? sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : '';
93 if ( in_array( $action, array( 'elementor', 'elementor_ajax' ), true ) ) {
94 return true;
95 }
96
97 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
98 if ( isset( $_GET['elementor-preview'] ) ) {
99 return true;
100 }
101
102 if ( class_exists( '\Elementor\Plugin' ) && isset( \Elementor\Plugin::$instance ) ) {
103 if ( isset( \Elementor\Plugin::$instance->editor ) && method_exists( \Elementor\Plugin::$instance->editor, 'is_edit_mode' ) && \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
104 return true;
105 }
106 if ( isset( \Elementor\Plugin::$instance->preview ) && method_exists( \Elementor\Plugin::$instance->preview, 'is_preview_mode' ) && \Elementor\Plugin::$instance->preview->is_preview_mode() ) {
107 return true;
108 }
109 }
110
111 return false;
112 }
113
114 /**
115 * Check if popup wrapping is safe for the current render context.
116 *
117 * @return bool
118 */
119 private static function is_popup_render_allowed() {
120
121 return ( ! is_admin() ) || self::is_elementor_render_context();
122 }
123
124 /**
125 * Sanitize a space-separated HTML class list.
126 *
127 * @param string $class_list Class list.
128 * @param string $default_value Fallback classes.
129 *
130 * @return string
131 */
132 private static function sanitize_class_list( $class_list, $default_value = '' ) {
133
134 $class_list = is_scalar( $class_list ) ? trim( (string) $class_list ) : '';
135
136 if ( '' === $class_list ) {
137 $class_list = (string) $default_value;
138 }
139
140 $classes = preg_split( '/\s+/', $class_list );
141 $classes = array_filter( array_map( 'sanitize_html_class', $classes ) );
142
143 return implode( ' ', array_unique( $classes ) );
144 }
145
146 /**
147 * Get normalized popup parameters from shortcode attributes.
148 *
149 * @param array $attr Shortcode attributes.
150 *
151 * @return array
152 */
153 private static function get_popup_params_from_attr( $attr ) {
154
155 $popup_title = isset( $attr['popup_title'] ) ? sanitize_text_field( wp_unslash( $attr['popup_title'] ) ) : '';
156 if ( '' === $popup_title ) {
157 $popup_title = __( 'Booking Form', 'booking' );
158 }
159
160 $button_title = isset( $attr['popup_button_title'] ) ? sanitize_text_field( wp_unslash( $attr['popup_button_title'] ) ) : '';
161 if ( '' === $button_title ) {
162 $button_title = __( 'Book now', 'booking' );
163 }
164
165 $button_class = isset( $attr['popup_button_class'] ) ? $attr['popup_button_class'] : 'wp-element-button';
166 $modal_class = isset( $attr['popup_modal_class'] ) ? $attr['popup_modal_class'] : '';
167
168 $popup_size = isset( $attr['popup_size'] ) ? sanitize_key( wp_unslash( $attr['popup_size'] ) ) : 'lg';
169 if ( ! in_array( $popup_size, array( 'lg', 'sm', 'default' ), true ) ) {
170 $popup_size = 'lg';
171 }
172
173 return array(
174 'title' => $popup_title,
175 'button_title' => $button_title,
176 'button_class' => self::sanitize_class_list( $button_class, 'wp-element-button' ),
177 'modal_class' => self::sanitize_class_list( $modal_class ),
178 'size' => $popup_size,
179 );
180 }
181
182 /**
183 * Enqueue front-end assets needed for popup booking forms.
184 *
185 * @return string Fallback inline assets if they must be printed at the shortcode location.
186 */
187 private static function enqueue_popup_assets() {
188
189 if ( function_exists( 'wpbc_load_js__required_for_front_end_modals' ) ) {
190 wpbc_load_js__required_for_front_end_modals();
191 } else {
192 wp_enqueue_script( 'wpbc-modal', wpbc_plugin_url( '/vendors/_custom/dropdown_modal/_out/dropdown_modal.js' ), array( 'jquery' ), WP_BK_VERSION_NUM, array( 'in_footer' => WPBC_JS_IN_FOOTER ) );
193 wp_enqueue_style( 'wpbc-admin-modal-popups', wpbc_plugin_url( '/css/modal.css' ), array(), WP_BK_VERSION_NUM );
194 }
195
196 $js_body = "jQuery( document ).on( 'shown.wpbc.modal', '.wpbc_booking_form_popup_modal', function(){ jQuery( window ).trigger( 'resize' ); jQuery( this ).trigger( 'wpbc_booking_popup_opened' ); } );";
197 $added = WPBC_FE_Assets::add_jq_ready_js_to_wp_script( 'wpbc-modal', $js_body, 'wpbc-booking-popup-modal-shown' );
198 if ( ! $added ) {
199 return WPBC_FE_Assets::add_inline_js( wpbc_jq_ready_start() . "\n" . $js_body . "\n" . wpbc_jq_ready_end(), 'wpbc-booking-popup-modal-shown' );
200 }
201
202 return '';
203 }
204
205 /**
206 * Wrap already-rendered booking form HTML into a front-end modal.
207 *
208 * @param string $booking_form_html Rendered booking form.
209 * @param array $popup_params Normalized popup params.
210 * @param int $resource_id Primary booking resource ID.
211 *
212 * @return string
213 */
214 private static function render_booking_popup( $booking_form_html, $popup_params, $resource_id ) {
215
216 static $popup_counter = 0;
217 $popup_counter++;
218
219 $fallback_assets = self::enqueue_popup_assets();
220
221 $resource_id = absint( $resource_id );
222 $modal_id = 'wpbc_booking_form_popup_modal_' . $resource_id . '_' . $popup_counter;
223
224 $modal_size_class = '';
225 if ( 'default' !== $popup_params['size'] ) {
226 $modal_size_class = ' modal-' . $popup_params['size'];
227 }
228
229 $modal_extra_class = ( '' !== $popup_params['modal_class'] ) ? ' ' . $popup_params['modal_class'] : '';
230
231 $html = $fallback_assets;
232 $html .= '<div class="wpbc_booking_form_popup">';
233 $html .= '<a href="#' . esc_attr( $modal_id ) . '" class="' . esc_attr( $popup_params['button_class'] ) . '" data-toggle="wpbc_my_modal" data-target="#' . esc_attr( $modal_id ) . '" aria-haspopup="dialog" aria-controls="' . esc_attr( $modal_id ) . '">' . esc_html( $popup_params['button_title'] ) . '</a>';
234 $html .= '<div class="wpdevelop">';
235 $html .= '<div id="' . esc_attr( $modal_id ) . '" class="modal wpbc_popup_modal wpbc_booking_form_popup_modal' . esc_attr( $modal_extra_class ) . '" tabindex="-1" role="dialog" aria-hidden="true" data-keyboard="false" data-backdrop="true" data-resource-id="' . esc_attr( $resource_id ) . '" style="display:none;">';
236 $html .= '<div class="modal-dialog' . esc_attr( $modal_size_class ) . '" role="document">';
237 $html .= '<div class="modal-content">';
238 $html .= '<div class="modal-header">';
239 $html .= '<button type="button" class="close" data-dismiss="modal" aria-label="' . esc_attr__( 'Close', 'booking' ) . '"><span aria-hidden="true">&times;</span></button>';
240 $html .= '<h4 class="modal-title">' . esc_html( $popup_params['title'] ) . '</h4>';
241 $html .= '</div>';
242 $html .= '<div class="modal-body">';
243 $html .= $booking_form_html;
244 $html .= '</div>';
245 $html .= '</div>';
246 $html .= '</div>';
247 $html .= '</div>';
248 $html .= '</div>';
249 $html .= '</div>';
250
251 return $html;
252 }
253
254 // ---------------------------------------------------------------------
255 // Shortcodes
256 // ---------------------------------------------------------------------
257
258 /**
259 * Shortcode [booking ...]
260 *
261 * @param array $attr
262 *
263 * @return string
264 */
265 public static function booking_shortcode( $attr ) {
266
267 if ( wpbc_is_on_edit_page() ) {
268 return wpbc_get_preview_for_shortcode( 'booking', $attr );
269 }
270
271 if ( WPBC_GET_Request::has_non_empty_get( 'booking_hash' ) ) {
272 /* translators: 1: URL to FAQ page. */
273 return __( 'You need to use special shortcode [bookingedit] for booking editing.', 'booking' ) . ' ' . sprintf( __( 'Please check FAQ instruction how to configure it here %s', 'booking' ), '<a href="https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/">https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/</a>' );
274 }
275
276 $attr = self::force_attr_array( $attr );
277 $attr = self::maybe_escape_shortcode_params( $attr );
278
279 // Parse, sanitize and normilize shortcode paramaters.
280 $shortcode_params = array(
281 'is_echo' => 0,
282 'resource_id' => WPBC_FE_Shortcode_Params::get_from_attr__resource_id_with_aggregate( $attr, WPBC_FE_Attr_Postprocessor::get_default_booking_resource_id() ),
283 'cal_count' => WPBC_FE_Shortcode_Params::get_from_attr__months_count( $attr, 1 ),
284 'start_month_calendar' => WPBC_FE_Shortcode_Params::get_from_attr__start_month( $attr, false ),// [ year, month ] | false.
285 'calendar_dates_start' => WPBC_FE_Shortcode_Params::get_from_attr__calendar_dates_start( $attr, '' ),
286 'calendar_dates_end' => WPBC_FE_Shortcode_Params::get_from_attr__calendar_dates_end( $attr, '' ),
287 'selected_dates_without_calendar' => '',
288 'custom_booking_form' => WPBC_FE_Shortcode_Params::get_from_attr__custom_form( $attr, 'standard' ),
289 'shortcode_param__options' => WPBC_FE_Shortcode_Params::get_from_attr__options( $attr, '' ),
290 'form_status' => WPBC_FE_Shortcode_Params::get_from_attr__form_status( $attr, 'published' ),
291 );
292
293 $shortcode_result = WPBC_FE_Render::render_booking_form( $shortcode_params );
294
295 if ( self::is_popup_render_allowed() && self::is_truthy_attr( $attr, 'popup' ) ) {
296 $resource_id_for_popup = WPBC_FE_Shortcode_Params::get_from_attr__primary_resource_id( $attr, WPBC_FE_Attr_Postprocessor::get_default_booking_resource_id() );
297 $shortcode_result = self::render_booking_popup( $shortcode_result, self::get_popup_params_from_attr( $attr ), $resource_id_for_popup );
298 }
299
300 return $shortcode_result;
301 }
302
303 /**
304 * Shortcode [booking_popup ...]
305 *
306 * Convenience alias for [booking ... popup=1].
307 *
308 * @param array $attr
309 *
310 * @return string
311 */
312 public static function booking_popup_shortcode( $attr ) {
313
314 $attr = self::force_attr_array( $attr );
315
316 $attr['popup'] = '1';
317
318 return self::booking_shortcode( $attr );
319 }
320
321
322 /**
323 * Shortcode [bookingcalendar ...]
324 *
325 * @param array $attr
326 *
327 * @return string
328 */
329 public static function booking_calendar_only_shortcode( $attr ) {
330
331 if ( wpbc_is_on_edit_page() ) {
332 return wpbc_get_preview_for_shortcode( 'bookingcalendar', $attr );
333 }
334
335 if ( WPBC_GET_Request::has_non_empty_get( 'booking_hash' ) ) {
336 /* translators: 1: URL to FAQ page. */
337 return __( 'You need to use special shortcode [bookingedit] for booking editing.', 'booking' ) . ' ' . sprintf( __( 'Please check FAQ instruction how to configure it here %s', 'booking' ), '<a href="https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/">https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/</a>' );
338 }
339
340 $attr = self::force_attr_array( $attr );
341 $attr = self::maybe_escape_shortcode_params( $attr );
342
343 $resource_id = WPBC_FE_Shortcode_Params::get_from_attr__primary_resource_id( $attr, WPBC_FE_Attr_Postprocessor::get_default_booking_resource_id() );
344
345 // Parse, sanitize and normilize shortcode paramaters.
346 $shortcode_params = array(
347 'is_echo' => 0,
348 'resource_id' => WPBC_FE_Shortcode_Params::get_from_attr__resource_id_with_aggregate( $attr, WPBC_FE_Attr_Postprocessor::get_default_booking_resource_id() ),
349 'cal_count' => WPBC_FE_Shortcode_Params::get_from_attr__months_count( $attr, 1 ),
350 'start_month_calendar' => WPBC_FE_Shortcode_Params::get_from_attr__start_month( $attr, false ), // [ year, month ] | false.
351 'calendar_dates_start' => WPBC_FE_Shortcode_Params::get_from_attr__calendar_dates_start( $attr, '' ),
352 'calendar_dates_end' => WPBC_FE_Shortcode_Params::get_from_attr__calendar_dates_end( $attr, '' ),
353 'shortcode_param__options' => WPBC_FE_Shortcode_Params::get_from_attr__options( $attr, '' ),
354 'selected_dates_without_calendar' => '',
355 'custom_booking_form' => '', // WPBC_FE_Shortcode_Params::get_from_attr__custom_form( $attr, 'standard' ), //.
356 );
357
358 $shortcode_calendar_html = WPBC_FE_Render::render_calendar_only( $shortcode_params );
359
360 $shortcode_html = "<div class='wpbc_only_calendar wpbc_container'>" .
361 "<div id='calendar_booking_unselectable" . esc_attr( intval( preg_replace( '/[^0-9].*$/', '', (string) $resource_id ) ) ) . "'></div>" .
362 $shortcode_calendar_html .
363 '</div>';
364
365 return $shortcode_html;
366 }
367
368
369 /**
370 * Shortcode [bookingform ...]
371 *
372 * @param array $attr
373 *
374 * @return string
375 */
376 public static function bookingform_shortcode( $attr ) {
377
378 if ( wpbc_is_on_edit_page() ) {
379 return wpbc_get_preview_for_shortcode( 'bookingform', $attr );
380 }
381
382 if ( WPBC_GET_Request::has_non_empty_get( 'booking_hash' ) ) {
383 /* translators: 1: URL to FAQ page. */
384 return __( 'You need to use special shortcode [bookingedit] for booking editing.', 'booking' ) . ' ' . sprintf( __( 'Please check FAQ instruction how to configure it here %s', 'booking' ), '<a href="https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/">https://wpbookingcalendar.com/faq/configure-editing-cancel-payment-bookings-for-visitors/</a>' );
385 }
386
387 $attr = self::force_attr_array( $attr );
388 $attr = self::maybe_escape_shortcode_params( $attr );
389
390 // Parse, sanitize and normilize shortcode paramaters.
391 $shortcode_params = array(
392 'is_echo' => 0,
393 'resource_id' => WPBC_FE_Shortcode_Params::get_from_attr__resource_id_with_aggregate( $attr, WPBC_FE_Attr_Postprocessor::get_default_booking_resource_id() ),
394 'selected_dates_without_calendar' => WPBC_FE_Shortcode_Params::get_from_attr__selected_dates_without_calendar( $attr, '' ),
395 'custom_booking_form' => WPBC_FE_Shortcode_Params::get_from_attr__custom_form( $attr, 'standard' ),
396 'shortcode_param__options' => WPBC_FE_Shortcode_Params::get_from_attr__options( $attr, '' ),
397 'cal_count' => 1,
398 'start_month_calendar' => false,
399 'calendar_dates_start' => '',
400 'calendar_dates_end' => '',
401 );
402
403 $shortcode_result = WPBC_FE_Render::render_booking_form( $shortcode_params );
404
405 return $shortcode_result;
406 }
407
408 }
409
410 WPBC_FE_Shortcodes::init();
411