PluginProbe
Booking Calendar / 11.8.4
Booking Calendar v11.8.4
11.8.4 11.8.3 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 All 204 releases
booking / includes / page-add-booking / add_booking__component.php

add_booking__component.php in Booking Calendar 11.8.4, at includes/page-add-booking/add_booking__component.php

517 lines 19.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php /**
2 * @version 1.0
3 * @package Booking Calendar
4 * @category Reusable Add Booking component
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly.
8
9 /**
10 * Reusable renderer for the Booking > Add Booking experience.
11 *
12 * The admin page calls this without args, so legacy $_GET driven behavior stays intact.
13 * Future embedded contexts can pass explicit args, which are always preferred over $_GET.
14 */
15 class WPBC_Add_Booking_Component {
16
17 /**
18 * Check if current user can access the Add Booking workflow.
19 *
20 * @return bool
21 */
22 public static function current_user_can_add_booking() {
23
24 $user_role = get_bk_option( 'booking_user_role_addbooking' );
25 $cap = 'read';
26
27 if ( class_exists( 'WPBC_Admin_Menus' ) && isset( WPBC_Admin_Menus::$capability[ $user_role ] ) ) {
28 $cap = WPBC_Admin_Menus::$capability[ $user_role ];
29 } elseif ( class_exists( 'WPBC_Admin_Menus' ) && isset( WPBC_Admin_Menus::$capability['subscriber'] ) ) {
30 $cap = WPBC_Admin_Menus::$capability['subscriber'];
31 }
32
33 return current_user_can( $cap );
34 }
35
36
37 /**
38 * Create the administrator booking nonce for an authorized page context.
39 *
40 * The public booking endpoint accepts signed-out requests, so administrator
41 * behavior must be enabled by a user-bound nonce and the same capability and
42 * MultiUser checks that the server repeats during booking creation.
43 *
44 * @return string Administrator booking nonce, or an empty string when the
45 * current user cannot use the administrator booking workflow.
46 */
47 public static function get_admin_booking_nonce() {
48
49 if (
50 ! is_user_logged_in()
51 || ! self::current_user_can_add_booking()
52 || ! function_exists( 'wpbc_is_mu_user_can_be_here' )
53 || ! wpbc_is_mu_user_can_be_here( 'activated_user' )
54 ) {
55 return '';
56 }
57
58 return wp_create_nonce( 'wpbc_admin_booking_create' );
59 }
60
61 /**
62 * Render the component and echo by default.
63 *
64 * @param array $args Component options.
65 *
66 * @return string|void
67 */
68 public static function render( $args = array() ) {
69
70 $args = self::normalize_args( $args );
71 $is_echo = ( ! empty( $args['is_echo'] ) );
72
73 ob_start();
74
75 ?><span class="wpdevelop"><?php // BS UI CSS Class.
76
77 if ( ! empty( $args['is_booking_page_js'] ) ) {
78 wpbc_js_for_bookings_page(); // JavaScript functions.
79 }
80
81 if ( ! empty( $args['is_toolbar_visible'] ) ) {
82 wpbc_add_new_booking_toolbar(
83 array(
84 'resource_id' => $args['_is_explicit_resource_id'] ? $args['resource_id'] : null,
85 'booking_form' => $args['_is_explicit_booking_form'] ? $args['custom_booking_form'] : null,
86 )
87 );
88 }
89
90 ?></span><!-- wpdevelop class --><?php
91
92 if ( ! empty( $args['is_show_before_content_spacer'] ) ) {
93 ?><div class="clear" style="height:40px;"></div><?php
94 }
95
96 ?><div class="<?php echo esc_attr( $args['content_css_class'] ); ?>" style="<?php echo esc_attr( $args['content_style'] ); ?>"><?php
97
98 self::print_context_js( $args );
99
100 WPBC_FE_Render::render_booking_form(
101 array(
102 'resource_id' => $args['resource_id'],
103 'cal_count' => $args['cal_count'],
104 'is_echo' => 1,
105 'custom_booking_form' => $args['custom_booking_form'],
106 'selected_dates_without_calendar' => $args['selected_dates_without_calendar'],
107 'start_month_calendar' => $args['start_month_calendar'],
108 'shortcode_param__options' => $args['shortcode_param__options'],
109 'calendar_dates_start' => $args['calendar_dates_start'],
110 'calendar_dates_end' => $args['calendar_dates_end'],
111 'booking_hash' => $args['booking_hash'],
112 )
113 );
114
115 ?></div><?php
116
117 if ( ! empty( $args['is_show_footer_email_toggle'] ) ) {
118 ?><hr /><?php
119 wpbc_toolbar_is_send_emails_btn_duplicated();
120 }
121
122 if ( ! empty( $args['is_booking_page_popover'] ) ) {
123 wpbc_bs_javascript_popover(); // JS Popover.
124 }
125
126 $html = ob_get_clean();
127
128 if ( $is_echo ) {
129 echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
130 return;
131 }
132
133 return $html;
134 }
135
136
137 /**
138 * Normalize component args.
139 *
140 * Explicit args are preferred first. Empty/non-provided args fall back to legacy $_GET behavior.
141 *
142 * @param array $args Component options.
143 *
144 * @return array
145 */
146 private static function normalize_args( $args ) {
147
148 $defaults = array(
149 'resource_id' => null,
150 'booking_type' => null,
151 'booking_form' => null,
152 'custom_booking_form' => null,
153 'calendar_options' => null,
154 'cal_count' => null,
155 'calendar_months_count' => null,
156 'shortcode_param__options' => null,
157 'selected_dates_without_calendar' => null,
158 'start_month_calendar' => false,
159 'calendar_dates_start' => '',
160 'calendar_dates_end' => '',
161 'booking_hash' => '',
162 'allow_past' => null,
163 'selected_dates' => '',
164 'selected_date' => '',
165 'selected_time' => '',
166 'time_override_enabled' => 0,
167 'time_override_source' => '',
168 'time_override_start' => '',
169 'time_override_end' => '',
170 'is_toolbar_visible' => true,
171 'is_booking_page_js' => true,
172 'is_booking_page_popover' => true,
173 'is_show_before_content_spacer' => true,
174 'is_show_footer_email_toggle' => true,
175 'content_css_class' => 'add_booking_page_content',
176 'content_style' => 'width:100%;margin-bottom:100px;',
177 'is_echo' => 1,
178 );
179 $args = wp_parse_args( $args, $defaults );
180
181 $args['_is_explicit_resource_id'] = ( ( null !== $args['resource_id'] ) && ( absint( $args['resource_id'] ) > 0 ) )
182 || ( ( null !== $args['booking_type'] ) && ( absint( $args['booking_type'] ) > 0 ) );
183 $args['_is_explicit_booking_form'] = ( ( null !== $args['custom_booking_form'] ) && ( '' !== (string) $args['custom_booking_form'] ) )
184 || ( ( null !== $args['booking_form'] ) && ( '' !== (string) $args['booking_form'] ) );
185
186 $resource_id = self::get_explicit_or_get_resource_id( $args );
187 $form_name = self::get_explicit_or_get_booking_form( $args, $resource_id );
188
189 $calendar_params = self::get_calendar_params( $args );
190
191 $args['resource_id'] = $resource_id;
192 $args['custom_booking_form'] = $form_name;
193 $args['cal_count'] = $calendar_params['months_number'];
194 $args['shortcode_param__options'] = $calendar_params['shortcode_param__options'];
195
196 $args['selected_dates_without_calendar'] = ( null !== $args['selected_dates_without_calendar'] ) ? (string) $args['selected_dates_without_calendar'] : '';
197 $args['calendar_dates_start'] = (string) $args['calendar_dates_start'];
198 $args['calendar_dates_end'] = (string) $args['calendar_dates_end'];
199 $args['booking_hash'] = ( '' !== (string) $args['booking_hash'] ) ? sanitize_text_field( wp_unslash( (string) $args['booking_hash'] ) ) : ( isset( $_GET['booking_hash'] ) ? sanitize_text_field( wp_unslash( $_GET['booking_hash'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
200 $args['allow_past'] = ( null !== $args['allow_past'] ) ? ( ! empty( $args['allow_past'] ) ? 1 : 0 ) : ( isset( $_GET['allow_past'] ) ? 1 : 0 ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
201 $args['selected_dates'] = sanitize_text_field( wp_unslash( (string) $args['selected_dates'] ) );
202 $args['selected_date'] = sanitize_text_field( wp_unslash( (string) $args['selected_date'] ) );
203 $args['selected_time'] = sanitize_text_field( wp_unslash( (string) $args['selected_time'] ) );
204 $args['time_override_enabled'] = ! empty( $args['time_override_enabled'] ) ? 1 : 0;
205 $args['time_override_source'] = sanitize_key( wp_unslash( (string) $args['time_override_source'] ) );
206 $args['time_override_start'] = sanitize_text_field( wp_unslash( (string) $args['time_override_start'] ) );
207 $args['time_override_end'] = sanitize_text_field( wp_unslash( (string) $args['time_override_end'] ) );
208
209 return $args;
210 }
211
212
213 /**
214 * Print per-render context that legacy front-end JavaScript reads from the global _wpbc object.
215 *
216 * @param array $args Component options.
217 *
218 * @return void
219 */
220 private static function print_context_js( $args ) {
221
222 $booking_hash = isset( $args['booking_hash'] ) ? (string) $args['booking_hash'] : '';
223 $admin_booking_nonce = self::get_admin_booking_nonce();
224 $allow_past_date_arr = self::get_allow_past_min_date_arr( $args );
225 $context = array(
226 'resource_id' => absint( $args['resource_id'] ),
227 'selected_dates_without_calendar' => (string) $args['selected_dates_without_calendar'],
228 'selected_dates' => (string) $args['selected_dates'],
229 'selected_date' => (string) $args['selected_date'],
230 'selected_time' => (string) $args['selected_time'],
231 'time_override_enabled' => absint( $args['time_override_enabled'] ),
232 'time_override_source' => (string) $args['time_override_source'],
233 'time_override_start' => (string) $args['time_override_start'],
234 'time_override_end' => (string) $args['time_override_end'],
235 'allow_past' => ! empty( $args['allow_past'] ) ? 1 : 0,
236 );
237 $booking_context_js = "_wpbc.set_other_param( 'this_page_booking_hash', " . wp_json_encode( $booking_hash ) . ' );';
238 $booking_context_js .= "_wpbc.set_other_param( 'this_page_allow_past', " . wp_json_encode( ! empty( $args['allow_past'] ) ? 1 : 0 ) . ' );';
239 $booking_context_js .= "_wpbc.set_other_param( 'this_page_allow_past_arr', " . wp_json_encode( $allow_past_date_arr ) . ' );';
240 $booking_context_js .= "_wpbc.set_other_param( 'this_page_admin_booking_nonce', " . wp_json_encode( $admin_booking_nonce ) . ' );';
241
242 $is_context_queued = false;
243 if ( ! wp_doing_ajax() && class_exists( 'WPBC_FE_Assets' ) ) {
244 $is_context_queued = WPBC_FE_Assets::add_jq_ready_js_to_wp_script(
245 'wpbc_all',
246 $booking_context_js,
247 'wpbc:add-booking-admin-context:' . md5( $booking_context_js )
248 );
249 }
250 ?>
251 <script type="text/javascript">
252 window.wpbc_add_booking_component_context = <?php echo wp_json_encode( $context ); ?>;
253 <?php if ( ! $is_context_queued ) : ?>
254 ( function () {
255 function apply_booking_context() {
256 if ( 'undefined' === typeof _wpbc ) {
257 return;
258 }
259
260 <?php echo $booking_context_js; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Values are JSON encoded above. ?>
261 }
262
263 if ( 'undefined' !== typeof _wpbc ) {
264 apply_booking_context();
265 } else {
266 document.addEventListener( 'DOMContentLoaded', apply_booking_context );
267 }
268 }() );
269 <?php endif; ?>
270 </script>
271 <?php
272 }
273
274
275 /**
276 * Get minimum date array for modal/admin contexts that allow selecting past days.
277 *
278 * @param array $args Component options.
279 *
280 * @return array
281 */
282 public static function get_allow_past_min_date_arr( $args ) {
283
284 if ( empty( $args['allow_past'] ) && empty( $args['booking_hash'] ) ) {
285 return array();
286 }
287
288 if (
289 ! function_exists( 'wpbc_get_max_visible_days_in_calendar' )
290 || ! function_exists( 'wpbc_datetime_localized__use_wp_timezone' )
291 || ! function_exists( 'wpbc_csv_numbers_to_int_array' )
292 ) {
293 return array();
294 }
295
296 $gmt_time = gmdate( 'Y-m-d H:i:s', strtotime( '-' . intval( wpbc_get_max_visible_days_in_calendar() ) . ' days' ) );
297 $local_csv = wpbc_datetime_localized__use_wp_timezone( $gmt_time, 'Y,m,d,H,i' );
298
299 return wpbc_csv_numbers_to_int_array( $local_csv );
300 }
301
302
303 /**
304 * Resolve booking resource id: explicit component args first, then legacy $_GET.
305 *
306 * @param array $args Component options.
307 *
308 * @return int
309 */
310 private static function get_explicit_or_get_resource_id( $args ) {
311
312 if ( ( null !== $args['resource_id'] ) && ( absint( $args['resource_id'] ) > 0 ) ) {
313 return absint( $args['resource_id'] );
314 }
315
316 if ( ( null !== $args['booking_type'] ) && ( absint( $args['booking_type'] ) > 0 ) ) {
317 return absint( $args['booking_type'] );
318 }
319
320 if ( isset( $_GET['booking_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
321 return intval( $_GET['booking_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
322 }
323
324 return 1;
325 }
326
327
328 /**
329 * Resolve default booking form for specific booking resource.
330 *
331 * @param int $resource_id Booking resource ID.
332 * @param string $default_form Fallback form name.
333 *
334 * @return string
335 */
336 public static function get_default_booking_form_for_resource( $resource_id, $default_form = 'standard' ) {
337
338 $resource_id = absint( $resource_id );
339 $default_form = ( '' !== (string) $default_form ) ? (string) $default_form : 'standard';
340
341 if ( $resource_id <= 0 ) {
342 return sanitize_text_field( wp_unslash( $default_form ) );
343 }
344
345 $default_custom_form_name = '';
346 if ( function_exists( 'apply_bk_filter' ) ) {
347 $default_custom_form_name = apply_bk_filter( 'wpbc_get_default_custom_form', '', $resource_id );
348 }
349
350 if (
351 ( '' === (string) $default_custom_form_name )
352 && class_exists( 'WPBC_FE_Custom_Form_Helper' )
353 && method_exists( 'WPBC_FE_Custom_Form_Helper', 'get_default_custom_form__for__booking_resource' )
354 ) {
355 $default_custom_form_name = WPBC_FE_Custom_Form_Helper::get_default_custom_form__for__booking_resource( $resource_id );
356 }
357
358 $default_custom_form_name = sanitize_text_field( wp_unslash( (string) $default_custom_form_name ) );
359
360 return ( '' !== $default_custom_form_name ) ? $default_custom_form_name : sanitize_text_field( wp_unslash( $default_form ) );
361 }
362
363
364 /**
365 * Resolve booking form: explicit component args first, then legacy $_GET, then resource default.
366 *
367 * @param array $args Component options.
368 * @param int $resource_id Booking resource ID.
369 *
370 * @return string
371 */
372 private static function get_explicit_or_get_booking_form( $args, $resource_id = 0 ) {
373
374 if ( ( null !== $args['custom_booking_form'] ) && ( '' !== (string) $args['custom_booking_form'] ) ) {
375 return sanitize_text_field( wp_unslash( (string) $args['custom_booking_form'] ) );
376 }
377
378 if ( ( null !== $args['booking_form'] ) && ( '' !== (string) $args['booking_form'] ) ) {
379 return sanitize_text_field( wp_unslash( (string) $args['booking_form'] ) );
380 }
381
382 if ( WPBC_GET_Request::has_non_empty_get( 'booking_form' ) ) {
383 return WPBC_GET_Request::get_sanitized( 'booking_form' );
384 }
385
386 return self::get_default_booking_form_for_resource( $resource_id, 'standard' );
387 }
388
389
390 /**
391 * Resolve calendar settings for the component.
392 *
393 * @param array $args Component options.
394 *
395 * @return array
396 */
397 private static function get_calendar_params( $args ) {
398
399 if ( null !== $args['calendar_options'] && is_array( $args['calendar_options'] ) ) {
400 $calendar_options = self::format_calendar_options( $args['calendar_options'] );
401 } else {
402 $calendar_options = self::get_saved_user_calendar_options();
403 }
404
405 if ( null !== $args['calendar_months_count'] ) {
406 $calendar_options['months_number'] = absint( $args['calendar_months_count'] );
407 }
408
409 if ( null !== $args['cal_count'] ) {
410 $calendar_options['months_number'] = absint( $args['cal_count'] );
411 }
412
413 if ( empty( $calendar_options['months_number'] ) ) {
414 $calendar_options['months_number'] = 1;
415 }
416
417 if ( null !== $args['shortcode_param__options'] ) {
418 $shortcode_param__options = (string) $args['shortcode_param__options'];
419 } else {
420 $shortcode_param__options = '{calendar' . $calendar_options['options_param'] . '}';
421 }
422
423 return array(
424 'months_number' => absint( $calendar_options['months_number'] ),
425 'shortcode_param__options' => $shortcode_param__options,
426 );
427 }
428
429
430 /**
431 * Get Calendar Options of specific User.
432 *
433 * @return array Number of months and calendar options parameter.
434 */
435 public static function get_saved_user_calendar_options() {
436
437 return self::format_calendar_options( self::get_saved_user_calendar_settings() );
438 }
439
440
441 /**
442 * Get the raw per-user Add Booking calendar settings.
443 *
444 * The inspector needs the individual saved fields, while
445 * get_saved_user_calendar_options() preserves the renderer-formatted public
446 * result used by the Booking Form component.
447 *
448 * @return array<string,mixed> Raw saved calendar settings.
449 */
450 public static function get_saved_user_calendar_settings() {
451
452 $user_calendar_options = get_user_option( 'booking_custom_' . 'add_booking_calendar_options', wpbc_get_current_user_id() );
453
454 if ( false === $user_calendar_options ) {
455 $user_calendar_options = array();
456 } else {
457 $user_calendar_options = maybe_unserialize( $user_calendar_options );
458 }
459
460 return is_array( $user_calendar_options ) ? $user_calendar_options : array();
461 }
462
463
464 /**
465 * Convert calendar option values to the shortcode option string used by the renderer.
466 *
467 * @param array $user_calendar_options Raw calendar options.
468 *
469 * @return array
470 */
471 private static function format_calendar_options( $user_calendar_options ) {
472
473 $defaults = array(
474 'calendar_months_count' => 1,
475 'calendar_months_num_in_1_row' => 0,
476 'calendar_width' => '',
477 'calendar_widthunits' => 'px',
478 'calendar_cell_height' => '',
479 'calendar_cell_heightunits' => 'px',
480 );
481
482 $user_calendar_options = wp_parse_args( (array) $user_calendar_options, $defaults );
483
484 if ( ! empty( $user_calendar_options['calendar_months_count'] ) ) {
485 $selected_calendar_months_count = intval( $user_calendar_options['calendar_months_count'] );
486 } else {
487 $selected_calendar_months_count = 1;
488 }
489
490 if ( ! empty( $user_calendar_options['calendar_months_num_in_1_row'] ) ) {
491 $option_months_num_in_row = ' months_num_in_row=' . intval( $user_calendar_options['calendar_months_num_in_1_row'] );
492 } else {
493 $option_months_num_in_row = '';
494 }
495
496 if ( ! empty( $user_calendar_options['calendar_width'] ) ) {
497 $unit_value = ( esc_attr( $user_calendar_options['calendar_widthunits'] ) == 'percent' ) ? '%' : esc_attr( $user_calendar_options['calendar_widthunits'] );
498 $option_width = ' width=' . intval( $user_calendar_options['calendar_width'] ) . $unit_value;
499 $option_width .= ' strong_width=' . intval( $user_calendar_options['calendar_width'] ) . $unit_value; // FixIn: 9.3.1.6.
500 } else {
501 $option_width = '';
502 }
503
504 if ( ! empty( $user_calendar_options['calendar_cell_height'] ) ) {
505 $unit_value = ( esc_attr( $user_calendar_options['calendar_cell_heightunits'] ) == 'percent' ) ? '%' : esc_attr( $user_calendar_options['calendar_cell_heightunits'] );
506 $option_cell_height = ' cell_height=' . intval( $user_calendar_options['calendar_cell_height'] ) . $unit_value;
507 } else {
508 $option_cell_height = '';
509 }
510
511 return array(
512 'months_number' => $selected_calendar_months_count,
513 'options_param' => $option_months_num_in_row . $option_width . $option_cell_height,
514 );
515 }
516 }
517