PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 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 All 205 releases
← All changes | includes/page-availability-general/availability_general__page.php +491 -88 11.0 → 11.9 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2 /**
3 3 * General availability admin page.
4 4 *
@@ -88,9 +88,11 @@
88 88 'ajax_url' => admin_url( 'admin-ajax.php' ),
89 89 'nonce' => wp_create_nonce( 'wpbc_availability_general_ajax_nonce' ),
90 90 'action' => 'WPBC_AJX_AVAILABILITY_GENERAL_SAVE',
91 91 'preview_action' => 'WPBC_AJX_AVAILABILITY_GENERAL_PREVIEW',
92 + 'settings' => wpbc_availability_general__get_settings_response(),
92 93 'default_settings' => wpbc_availability_general__get_default_settings(),
94 + 'open_section' => wpbc_availability_general__get_open_section(),
93 95 'is_buffer_available' => class_exists( 'wpdev_bk_biz_m' ),
94 96 'is_available_limit_available' => class_exists( 'wpdev_bk_biz_m' ),
95 97 'i18n' => array(
96 98 'saving' => __( 'Saving', 'booking' ),
@@ -103,13 +105,17 @@
103 105 'loading' => __( 'Loading', 'booking' ),
104 106 'buffer_preview' => __( 'Buffer preview', 'booking' ),
105 107 'before_booking' => __( 'Before booking', 'booking' ),
106 108 'after_booking' => __( 'After booking', 'booking' ),
107 - 'no_buffer' => __( 'No booking buffer is selected.', 'booking' ),
108 - ),
109 - )
110 - );
111 -}
109 + 'no_buffer' => __( 'No booking buffer is selected.', 'booking' ),
110 + 'add_interval' => __( 'Add interval', 'booking' ),
111 + 'remove_interval' => __( 'Remove working interval', 'booking' ),
112 + 'interval_limit' => __( 'No additional one-hour interval is available for this weekday.', 'booking' ),
113 + ),
114 + 'max_working_time_intervals' => wpbc_availability_general__get_max_working_time_intervals_per_day(),
115 + )
116 + );
117 +}
112 118 add_action( 'wpbc_enqueue_js_files', 'wpbc_availability_general_enqueue_js_files', 66 );
113 119
114 120 /**
115 121 * Print tooltip initializer for General Availability.
@@ -206,8 +212,21 @@
206 212 return in_array( $months, array( 1, 2, 3, 4, 6, 12 ), true ) ? $months : 6;
207 213 }
208 214
209 215 /**
216 + * Get the General Availability section that should be opened on page load.
217 + *
218 + * @return string
219 + */
220 +function wpbc_availability_general__get_open_section() {
221 +
222 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
223 + $open_section = isset( $_REQUEST['wpbc_ag_open'] ) ? sanitize_key( wp_unslash( $_REQUEST['wpbc_ag_open'] ) ) : '';
224 +
225 + return in_array( $open_section, array( 'weekdays', 'from_today', 'buffer', 'working_time' ), true ) ? $open_section : '';
226 +}
227 +
228 +/**
210 229 * Validate preview resource ID.
211 230 *
212 231 * @param int $resource_id Resource ID.
213 232 *
@@ -321,13 +340,13 @@
321 340
322 341 /**
323 342 * Get time options for future working-time controls.
324 343 *
325 - * @param string $selected Selected value.
344 + * @param string|string[] $selected Selected values that must remain available.
326 345 *
327 346 * @return array
328 347 */
329 -function wpbc_availability_general__get_working_time_options( $selected = '' ) {
348 +function wpbc_availability_general__get_working_time_options( $selected = '' ) {
330 349
331 350 $options = array();
332 351
333 352 for ( $minutes = 0; $minutes <= 1440; $minutes += 30 ) {
@@ -338,15 +357,79 @@
338 357
339 358 $options[ $value ] = $label;
340 359 }
341 360
342 - if ( '' !== $selected && ! isset( $options[ $selected ] ) ) {
343 - $options[ $selected ] = $selected;
344 - }
361 + $selected_values = is_array( $selected ) ? $selected : array( $selected );
362 + foreach ( $selected_values as $selected_value ) {
363 + $selected_value = is_scalar( $selected_value ) ? (string) $selected_value : '';
364 + if ( '' !== $selected_value && ! isset( $options[ $selected_value ] ) ) {
365 + $options[ $selected_value ] = $selected_value;
366 + }
367 + }
368 +
369 + uksort(
370 + $options,
371 + function ( $first_time, $second_time ) {
372 + return wpbc_working_time__time_to_seconds( $first_time ) - wpbc_working_time__time_to_seconds( $second_time );
373 + }
374 + );
375 +
376 + return $options;
377 +}
378 +
379 +/**
380 + * Return the maximum number of editable intervals for one weekday.
381 + *
382 + * @return int Maximum interval count.
383 + */
384 +function wpbc_availability_general__get_max_working_time_intervals_per_day() {
385 +
386 + return 8;
387 +}
388 +
389 +/**
390 + * Collect every stored endpoint needed by the current inspector selects.
391 + *
392 + * Resource switching happens without reloading the inspector. Including all
393 + * canonical endpoints prevents a minute-aligned custom time from disappearing
394 + * when the selected resource changes in the browser.
395 + *
396 + * @param array $working_time_settings Canonical Working Time settings.
397 + *
398 + * @return string[] Unique H:i endpoints.
399 + */
400 +function wpbc_availability_general__get_working_time_option_values( $working_time_settings ) {
401 +
402 + $selected_values = array();
403 + $schedules = array();
404 +
405 + if ( isset( $working_time_settings['default']['weekdays'] ) ) {
406 + $schedules[] = $working_time_settings['default']['weekdays'];
407 + }
408 + if ( isset( $working_time_settings['resources'] ) && is_array( $working_time_settings['resources'] ) ) {
409 + foreach ( $working_time_settings['resources'] as $resource_settings ) {
410 + if ( isset( $resource_settings['weekdays'] ) ) {
411 + $schedules[] = $resource_settings['weekdays'];
412 + }
413 + }
414 + }
415 +
416 + foreach ( $schedules as $weekdays ) {
417 + foreach ( (array) $weekdays as $day_intervals ) {
418 + foreach ( (array) $day_intervals as $interval ) {
419 + if ( isset( $interval['start_second'] ) ) {
420 + $selected_values[] = wpbc_working_time__seconds_to_time( $interval['start_second'] );
421 + }
422 + if ( isset( $interval['end_second'] ) ) {
423 + $selected_values[] = wpbc_working_time__seconds_to_time( $interval['end_second'] );
424 + }
425 + }
426 + }
427 + }
428 +
429 + return array_values( array_unique( $selected_values ) );
430 +}
345 431
346 - return $options;
347 -}
348 -
349 432 /**
350 433 * Render a select field.
351 434 *
352 435 * @param string $name Field name.
@@ -375,8 +458,130 @@
375 458 <?php
376 459 }
377 460
378 461 /**
462 + * Render Working Time weekday rows.
463 + *
464 + * @param string $prefix Field prefix.
465 + * @param array $weekdays Weekday intervals.
466 + * @param array $time_options Allowed time choices.
467 + *
468 + * @return void
469 + */
470 +function wpbc_availability_general__render_working_time_weekdays( $prefix, $weekdays, $time_options = array() ) {
471 +
472 + $days = array(
473 + 0 => _x( 'Sun', 'Short weekday name', 'booking' ),
474 + 1 => _x( 'Mon', 'Short weekday name', 'booking' ),
475 + 2 => _x( 'Tue', 'Short weekday name', 'booking' ),
476 + 3 => _x( 'Wed', 'Short weekday name', 'booking' ),
477 + 4 => _x( 'Thu', 'Short weekday name', 'booking' ),
478 + 5 => _x( 'Fri', 'Short weekday name', 'booking' ),
479 + 6 => _x( 'Sat', 'Short weekday name', 'booking' ),
480 + );
481 +
482 + $start_week_day = absint( get_bk_option( 'booking_start_day_weeek' ) );
483 + if ( $start_week_day > 6 ) {
484 + $start_week_day = 0;
485 + }
486 + $days_ordered = array_slice( $days, $start_week_day, null, true ) + array_slice( $days, 0, $start_week_day, true );
487 + if ( empty( $time_options ) ) {
488 + $time_options = wpbc_availability_general__get_working_time_options();
489 + }
490 + ?>
491 + <div
492 + class="wpbc_ag_working_time_rows"
493 + data-wpbc-working-time-weekdays="<?php echo esc_attr( $prefix ); ?>"
494 + data-max-intervals="<?php echo esc_attr( (string) wpbc_availability_general__get_max_working_time_intervals_per_day() ); ?>"
495 + >
496 + <?php foreach ( $days_ordered as $day_num => $day_title ) : ?>
497 + <?php
498 + $day_intervals = ! empty( $weekdays[ $day_num ] ) && is_array( $weekdays[ $day_num ] )
499 + ? array_values( $weekdays[ $day_num ] )
500 + : array();
501 + $is_open = ! empty( $day_intervals );
502 + $display_intervals = $is_open
503 + ? $day_intervals
504 + : array( array( 'start_second' => 9 * HOUR_IN_SECONDS, 'end_second' => 18 * HOUR_IN_SECONDS ) );
505 + /* translators: %s: Short weekday name. */
506 + $intervals_label = sprintf( __( '%s Working Time intervals', 'booking' ), $day_title );
507 + /* translators: %s: Short weekday name. */
508 + $add_interval_label = sprintf( __( 'Add %s Working Time interval', 'booking' ), $day_title );
509 + ?>
510 + <div class="wpbc_ag_working_time_row<?php echo $is_open ? ' is-enabled' : ''; ?>" data-wpbc-working-time-day="<?php echo esc_attr( (string) $day_num ); ?>">
511 + <label class="wpbc_ag_switch wpbc_ag_working_time_day">
512 + <input type="checkbox" name="<?php echo esc_attr( $prefix ); ?>_days[]" value="<?php echo esc_attr( $day_num ); ?>" data-wpbc-working-time-day-toggle="1" <?php checked( $is_open ); ?> />
513 + <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
514 + <span class="wpbc_ag_switch_label"><?php echo esc_html( $day_title ); ?></span>
515 + </label>
516 + <div class="wpbc_ag_working_time_intervals" data-wpbc-working-time-intervals="1" aria-label="<?php echo esc_attr( $intervals_label ); ?>">
517 + <?php foreach ( $display_intervals as $interval_index => $interval ) : ?>
518 + <?php wpbc_availability_general__render_working_time_interval( $prefix, $day_num, $day_title, $interval_index, $interval, $time_options ); ?>
519 + <?php endforeach; ?>
520 + </div>
521 + <button type="button" class="button-link wpbc_ag_working_time_add" data-wpbc-add-working-time-interval="1" aria-label="<?php echo esc_attr( $add_interval_label ); ?>">
522 + <i class="menu_icon icon-1x wpbc_icn_add" aria-hidden="true"></i>
523 + <span><?php esc_html_e( 'Add interval', 'booking' ); ?></span>
524 + </button>
525 + </div>
526 + <?php endforeach; ?>
527 + </div>
528 + <?php
529 +}
530 +
531 +/**
532 + * Render one editable Working Time interval.
533 + *
534 + * @param string $prefix Field prefix.
535 + * @param int $day_num Canonical weekday number from 0 through 6.
536 + * @param string $day_title Localized weekday title.
537 + * @param int $interval_index Zero-based interval index.
538 + * @param array $interval Canonical interval values.
539 + * @param array $time_options Allowed time choices.
540 + *
541 + * @return void
542 + */
543 +function wpbc_availability_general__render_working_time_interval( $prefix, $day_num, $day_title, $interval_index, $interval, $time_options ) {
544 +
545 + $start_id = $prefix . '_start_' . $day_num . '_' . $interval_index;
546 + $end_id = $prefix . '_end_' . $day_num . '_' . $interval_index;
547 + /* translators: %s: Short weekday name. */
548 + $remove_label = sprintf( __( 'Remove %s Working Time interval', 'booking' ), $day_title );
549 + ?>
550 + <div class="wpbc_ag_working_time_times" data-wpbc-working-time-interval="1" data-interval-index="<?php echo esc_attr( (string) $interval_index ); ?>">
551 + <label class="screen-reader-text" for="<?php echo esc_attr( $start_id ); ?>"><?php esc_html_e( 'Start time', 'booking' ); ?></label>
552 + <?php
553 + wpbc_availability_general__render_select(
554 + $prefix . '_start[' . $day_num . '][]',
555 + $time_options,
556 + wpbc_working_time__seconds_to_time( $interval['start_second'] ),
557 + array(
558 + 'id' => $start_id,
559 + 'class' => 'wpbc_ag_field_control wpbc_ag_working_time_start',
560 + )
561 + );
562 + ?>
563 + <span class="wpbc_ag_working_time_separator" aria-hidden="true">&ndash;</span>
564 + <label class="screen-reader-text" for="<?php echo esc_attr( $end_id ); ?>"><?php esc_html_e( 'End time', 'booking' ); ?></label>
565 + <?php
566 + wpbc_availability_general__render_select(
567 + $prefix . '_end[' . $day_num . '][]',
568 + $time_options,
569 + wpbc_working_time__seconds_to_time( $interval['end_second'] ),
570 + array(
571 + 'id' => $end_id,
572 + 'class' => 'wpbc_ag_field_control wpbc_ag_working_time_end',
573 + )
574 + );
575 + ?>
576 + <button type="button" class="wpbc_ag_working_time_remove" data-wpbc-remove-working-time-interval="1" aria-label="<?php echo esc_attr( $remove_label ); ?>">
577 + <i class="menu_icon icon-1x wpbc_icn_delete_outline" aria-hidden="true"></i>
578 + </button>
579 + </div>
580 + <?php
581 +}
582 +
583 +/**
379 584 * Get default General Availability settings.
380 585 *
381 586 * @return array
382 587 */
@@ -390,17 +595,140 @@
390 595 'booking_unavailable_extra_minutes_in' => '',
391 596 'booking_unavailable_extra_minutes_out' => '',
392 597 'booking_unavailable_extra_days_in' => '',
393 598 'booking_unavailable_extra_days_out' => '',
599 + 'working_time' => wpbc_working_time__get_default_settings(),
394 600 );
395 601 }
396 602
397 603 /**
604 + * Convert one posted Working Time value to canonical seconds.
605 + *
606 + * @param mixed $raw_time Untrusted H:i value.
607 + *
608 + * @return int|null Seconds from midnight, or null when invalid.
609 + */
610 +function wpbc_availability_general__validate_working_time_value( $raw_time ) {
611 +
612 + if ( ! is_scalar( $raw_time ) ) {
613 + return null;
614 + }
615 +
616 + $time_value = sanitize_text_field( (string) $raw_time );
617 + if ( ! preg_match( '/^(?:(?:[01]\d|2[0-3]):[0-5]\d|24:00)$/', $time_value ) ) {
618 + return null;
619 + }
620 +
621 + return wpbc_working_time__time_to_seconds( $time_value );
622 +}
623 +
624 +/**
625 + * Validate posted multi-interval Working Time weekdays.
626 + *
627 + * @param array $post_data Raw post data.
628 + * @param string $prefix Field prefix.
629 + *
630 + * @return array<int,array<int,array{start_second:int,end_second:int}>>|WP_Error Validated schedule or an error.
631 + */
632 +function wpbc_availability_general__validate_working_time_weekdays( $post_data, $prefix ) {
633 +
634 + $weekdays = array();
635 + $enabled_days = array();
636 + $start_values = isset( $post_data[ $prefix . '_start' ] ) && is_array( $post_data[ $prefix . '_start' ] ) ? wp_unslash( $post_data[ $prefix . '_start' ] ) : array();
637 + $end_values = isset( $post_data[ $prefix . '_end' ] ) && is_array( $post_data[ $prefix . '_end' ] ) ? wp_unslash( $post_data[ $prefix . '_end' ] ) : array();
638 +
639 + if ( isset( $post_data[ $prefix . '_days' ] ) && is_array( $post_data[ $prefix . '_days' ] ) ) {
640 + foreach ( wp_unslash( $post_data[ $prefix . '_days' ] ) as $day_num ) {
641 + if ( is_scalar( $day_num ) && preg_match( '/^[0-6]$/', (string) $day_num ) ) {
642 + $enabled_days[] = (int) $day_num;
643 + }
644 + }
645 + }
646 + $enabled_days = array_values( array_unique( $enabled_days ) );
647 +
648 + for ( $day = 0; $day <= 6; $day++ ) {
649 + $weekdays[ $day ] = array();
650 + if ( ! in_array( $day, $enabled_days, true ) ) {
651 + continue;
652 + }
653 +
654 + $day_start_values = isset( $start_values[ $day ] ) ? array_values( (array) $start_values[ $day ] ) : array();
655 + $day_end_values = isset( $end_values[ $day ] ) ? array_values( (array) $end_values[ $day ] ) : array();
656 + if ( empty( $day_start_values ) || count( $day_start_values ) !== count( $day_end_values ) ) {
657 + return new WP_Error( 'wpbc_availability_general_working_time_interval_missing', __( 'Every enabled weekday must contain a complete Working Time interval.', 'booking' ) );
658 + }
659 +
660 + foreach ( $day_start_values as $interval_index => $raw_start_value ) {
661 + $start_second = wpbc_availability_general__validate_working_time_value( $raw_start_value );
662 + $end_second = wpbc_availability_general__validate_working_time_value( $day_end_values[ $interval_index ] );
663 +
664 + if ( null === $start_second || null === $end_second ) {
665 + return new WP_Error( 'wpbc_availability_general_working_time_value_invalid', __( 'Enter a valid start and end time for every Working Time interval.', 'booking' ) );
666 + }
667 +
668 + $weekdays[ $day ][] = array(
669 + 'start_second' => $start_second,
670 + 'end_second' => $end_second,
671 + );
672 + }
673 + }
674 +
675 + return wpbc_working_time__validate_weekday_intervals( $weekdays, wpbc_availability_general__get_max_working_time_intervals_per_day() );
676 +}
677 +
678 +/**
679 + * Validate posted Working Time settings.
680 + *
681 + * @param array $post_data Raw post data.
682 + *
683 + * @return array|WP_Error Validated Working Time settings or an error.
684 + */
685 +function wpbc_availability_general__validate_working_time_data( $post_data ) {
686 +
687 + $current_settings = wpbc_working_time__get_settings();
688 + $resource_id = isset( $post_data['booking_working_time_resource_id'] ) ? absint( wp_unslash( $post_data['booking_working_time_resource_id'] ) ) : 0;
689 + $resource_mode = isset( $post_data['booking_working_time_resource_mode'] ) ? sanitize_key( wp_unslash( $post_data['booking_working_time_resource_mode'] ) ) : 'inherit';
690 +
691 + if ( ! in_array( $resource_mode, array( 'inherit', 'custom', 'disabled' ), true ) ) {
692 + $resource_mode = 'inherit';
693 + }
694 +
695 + $default_weekdays = wpbc_availability_general__validate_working_time_weekdays( $post_data, 'booking_working_time_default' );
696 + if ( is_wp_error( $default_weekdays ) ) {
697 + return $default_weekdays;
698 + }
699 +
700 + $resource_weekdays = isset( $current_settings['resources'][ $resource_id ]['weekdays'] )
701 + ? $current_settings['resources'][ $resource_id ]['weekdays']
702 + : $current_settings['default']['weekdays'];
703 + if ( 'custom' === $resource_mode ) {
704 + $resource_weekdays = wpbc_availability_general__validate_working_time_weekdays( $post_data, 'booking_working_time_resource' );
705 + if ( is_wp_error( $resource_weekdays ) ) {
706 + return $resource_weekdays;
707 + }
708 + }
709 +
710 + $current_settings['enabled'] = ( isset( $post_data['booking_working_time_enabled'] ) && 'On' === sanitize_text_field( wp_unslash( $post_data['booking_working_time_enabled'] ) ) ) ? 'On' : 'Off';
711 + $current_settings['default'] = array(
712 + 'weekdays' => $default_weekdays,
713 + );
714 +
715 + if ( $resource_id > 0 ) {
716 + $current_settings['resources'][ $resource_id ] = array(
717 + 'mode' => $resource_mode,
718 + 'weekdays' => $resource_weekdays,
719 + );
720 + }
721 +
722 + return wpbc_working_time__normalize_settings( $current_settings );
723 +}
724 +
725 +/**
398 726 * Validate posted settings.
399 727 *
400 728 * @param array $post_data Raw post data.
401 729 *
402 - * @return array
730 + * @return array|WP_Error Validated settings or an error.
403 731 */
404 732 function wpbc_availability_general__validate_data( $post_data ) {
405 733
406 734 $cleaned = wpbc_availability_general__get_default_settings();
@@ -444,8 +772,13 @@
444 772 $cleaned[ $key ] = preg_match( '/^\d+d$/', $value ) ? $value : '';
445 773 }
446 774 }
447 775
776 + $cleaned['working_time'] = wpbc_availability_general__validate_working_time_data( $post_data );
777 + if ( is_wp_error( $cleaned['working_time'] ) ) {
778 + return $cleaned['working_time'];
779 + }
780 +
448 781 return $cleaned;
449 782 }
450 783
451 784 /**
@@ -470,8 +803,10 @@
470 803 update_bk_option( 'booking_unavailable_extra_minutes_out', $cleaned_data['booking_unavailable_extra_minutes_out'] );
471 804 update_bk_option( 'booking_unavailable_extra_days_in', $cleaned_data['booking_unavailable_extra_days_in'] );
472 805 update_bk_option( 'booking_unavailable_extra_days_out', $cleaned_data['booking_unavailable_extra_days_out'] );
473 806 }
807 +
808 + wpbc_working_time__update_settings( $cleaned_data['working_time'] );
474 809 }
475 810
476 811 /**
477 812 * Get current settings prepared for JavaScript and AJAX responses.
@@ -497,8 +832,9 @@
497 832 'booking_unavailable_extra_minutes_in' => get_bk_option( 'booking_unavailable_extra_minutes_in' ),
498 833 'booking_unavailable_extra_minutes_out' => get_bk_option( 'booking_unavailable_extra_minutes_out' ),
499 834 'booking_unavailable_extra_days_in' => get_bk_option( 'booking_unavailable_extra_days_in' ),
500 835 'booking_unavailable_extra_days_out' => get_bk_option( 'booking_unavailable_extra_days_out' ),
836 + 'working_time' => wpbc_working_time__get_settings(),
501 837 'hints' => array(
502 838 'booking_unavailable_days_num_from_today__hint' => isset( $hints['booking_unavailable_days_num_from_today__hint'] ) ? $hints['booking_unavailable_days_num_from_today__hint'] : '',
503 839 'booking_available_days_num_from_today__hint' => isset( $hints['booking_available_days_num_from_today__hint'] ) ? $hints['booking_available_days_num_from_today__hint'] : '',
504 840 ),
@@ -693,9 +1029,9 @@
693 1029
694 1030 /**
695 1031 * General Availability tab.
696 1032 */
697 -class WPBC_Page_Availability_General_Dedicated extends WPBC_Page_Structure {
1033 +class WPBC_Page_Availability_General_Dedicated extends WPBC_Page_Structure {
698 1034
699 1035 /**
700 1036 * Whether settings were saved during this request.
701 1037 *
@@ -700,9 +1036,16 @@
700 1036 * Whether settings were saved during this request.
701 1037 *
702 1038 * @var bool
703 1039 */
704 - private $is_updated = false;
1040 + private $is_updated = false;
1041 +
1042 + /**
1043 + * Validation failure retained for the non-AJAX form fallback.
1044 + *
1045 + * @var WP_Error|null
1046 + */
1047 + private $validation_error = null;
705 1048
706 1049 /**
707 1050 * Menu slug.
708 1051 *
@@ -726,17 +1069,17 @@
726 1069
727 1070 $tabs = array();
728 1071
729 1072 $tabs['general_availability'] = array(
730 - 'is_show_top_path' => false,
1073 + 'is_show_top_path' => true,
731 1074 'right_vertical_sidebar__is_show' => true,
732 1075 'right_vertical_sidebar__default_view_mode' => '',
733 1076 'right_vertical_sidebar_compact__is_show' => true,
734 - 'left_navigation__default_view_mode' => 'compact',
735 - 'title' => __( 'General Availability', 'booking' ) .
736 - '<span class="wpbc_new_label" style="margin-left: auto;">' . esc_html__( 'New', 'booking' ) . '</span>',
1077 + 'left_navigation__default_view_mode' => '', // FixIn:11.7.1.1: 'compact',
1078 + 'top_path_title' => __( 'Schedule & Rules', 'booking' ),
1079 + 'title' => __( 'Schedule & Rules', 'booking' ),
737 1080 'hint' => __( 'Define global front-end availability rules for all calendars, including unavailable weekdays, booking buffers, and availability limits.', 'booking' ),
738 - 'page_title' => __( 'General Availability', 'booking' ),
1081 + 'page_title' => __( 'Schedule & Rules', 'booking' ),
739 1082 'link' => '',
740 1083 'position' => '',
741 1084 'css_classes' => 'wpbc_top_tab__general_availability',
742 1085 'icon' => '',
@@ -746,8 +1089,9 @@
746 1089 'disabled' => false,
747 1090 'hided' => false,
748 1091 'subtabs' => array(),
749 1092 'folder_style' => 'order:93;',
1093 + 'is_show_top_navigation' => true,
750 1094 );
751 1095
752 1096 return $tabs;
753 1097 }
@@ -771,10 +1115,15 @@
771 1115 if ( ! wpbc_is_mu_user_can_be_here( 'only_super_admin' ) || ! current_user_can( wpbc_availability_general__get_manage_cap() ) ) {
772 1116 return;
773 1117 }
774 1118
775 - $cleaned_data = wpbc_availability_general__validate_data( $_POST ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
776 - wpbc_availability_general__update_settings( $cleaned_data );
1119 + $cleaned_data = wpbc_availability_general__validate_data( $_POST ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1120 + if ( is_wp_error( $cleaned_data ) ) {
1121 + $this->validation_error = $cleaned_data;
1122 + return;
1123 + }
1124 +
1125 + wpbc_availability_general__update_settings( $cleaned_data );
777 1126
778 1127 $this->is_updated = true;
779 1128 }
780 1129
@@ -801,9 +1150,9 @@
801 1150 'icon' => 'wpbc-bi-info-circle',
802 1151 ),
803 1152 ),
804 1153 array(
805 - 'aria_label' => __( 'General Availability Panels', 'booking' ),
1154 + 'aria_label' => __( 'Schedule & Rules Panels', 'booking' ),
806 1155 'context' => 'general_availability',
807 1156 'class' => 'wpbc_ag_rightbar_tabs',
808 1157 )
809 1158 );
@@ -853,8 +1202,21 @@
853 1202 $buffer_type = (string) get_bk_option( 'booking_unavailable_extra_in_out' );
854 1203 $is_buffer_available = class_exists( 'wpdev_bk_biz_m' );
855 1204 $is_available_limit_available = class_exists( 'wpdev_bk_biz_m' );
856 1205 $unavailable_weekdays = array();
1206 + $working_time_settings = wpbc_working_time__get_settings();
1207 + $working_time_default_settings = wpbc_working_time__get_default_settings();
1208 + $working_time_options = wpbc_availability_general__get_working_time_options(
1209 + wpbc_availability_general__get_working_time_option_values( $working_time_settings )
1210 + );
1211 + $working_time_resource_id = wpbc_availability_general__get_preview_resource_id();
1212 + $open_section = wpbc_availability_general__get_open_section();
1213 + $working_time_resource = isset( $working_time_settings['resources'][ $working_time_resource_id ] )
1214 + ? $working_time_settings['resources'][ $working_time_resource_id ]
1215 + : array(
1216 + 'mode' => 'inherit',
1217 + 'weekdays' => $working_time_default_settings['default']['weekdays'],
1218 + );
857 1219
858 1220 for ( $i = 0; $i < 7; $i++ ) {
859 1221 if ( 'On' === get_bk_option( 'booking_unavailable_day' . $i ) ) {
860 1222 $unavailable_weekdays[] = $i;
@@ -860,58 +1222,125 @@
860 1222 $unavailable_weekdays[] = $i;
861 1223 }
862 1224 }
863 1225
864 - WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'General Availability', 'booking' ), __( 'Global front-end availability rules for all calendars.', 'booking' ) );
1226 + WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Schedule & Rules', 'booking' ), __( 'Availability rules for all calendars.', 'booking' ) );
865 1227 ?>
866 - <form method="post" id="<?php echo esc_attr( $form_name ); ?>" class="wpbc_ag_settings_form" data-wpbc-ag-settings-form="1">
1228 + <form method="post" id="<?php echo esc_attr( $form_name ); ?>" class="wpbc_ag_settings_form" data-wpbc-ag-settings-form="1">
867 1229 <?php wp_nonce_field( 'wpbc_settings_page_' . $form_name ); ?>
868 1230 <input type="hidden" name="is_form_submitted_<?php echo esc_attr( $form_name ); ?>" value="1" />
869 - <div class="wpbc_bfb__inspector__body wpbc_ag_inspector_body">
870 - <?php
1231 + <div class="wpbc_bfb__inspector__body wpbc_ag_inspector_body">
1232 + <?php if ( is_wp_error( $this->validation_error ) ) : ?>
1233 + <div class="wpbc_ag_notice wpbc_ag_notice_error" role="alert">
1234 + <?php echo esc_html( $this->validation_error->get_error_message() ); ?>
1235 + </div>
1236 + <?php endif; ?>
1237 + <?php
871 1238 WPBC_UI_Sidebar_Panels::render_collapsible_group(
872 1239 array(
873 1240 'id' => 'wpbc_ag_weekdays_group',
874 1241 'group' => 'general-availability-weekdays',
875 1242 'title' => __( 'Unavailable Weekdays', 'booking' ),
876 - 'open' => true,
877 - ),
878 - function () use ( $unavailable_weekdays ) {
879 - $days = array(
880 - 0 => __( 'Sunday', 'booking' ),
881 - 1 => __( 'Monday', 'booking' ),
882 - 2 => __( 'Tuesday', 'booking' ),
883 - 3 => __( 'Wednesday', 'booking' ),
884 - 4 => __( 'Thursday', 'booking' ),
885 - 5 => __( 'Friday', 'booking' ),
886 - 6 => __( 'Saturday', 'booking' ),
887 - );
888 - $start_week_day = absint( get_bk_option( 'booking_start_day_weeek' ) );
889 - if ( $start_week_day > 6 ) {
890 - $start_week_day = 0;
891 - }
892 - $days_ordered = array_slice( $days, $start_week_day, null, true ) + array_slice( $days, 0, $start_week_day, true );
893 - ?>
894 - <div class="wpbc_ag_weekday_grid">
895 - <?php foreach ( $days_ordered as $day_num => $day_title ) : ?>
896 - <label class="wpbc_ag_switch">
897 - <input type="checkbox" name="booking_unavailable_days[]" value="<?php echo esc_attr( $day_num ); ?>" <?php checked( in_array( $day_num, $unavailable_weekdays, true ) ); ?> />
898 - <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
899 - <span class="wpbc_ag_switch_label"><?php echo esc_html( $day_title ); ?></span>
900 - </label>
901 - <?php endforeach; ?>
902 - </div>
1243 + 'open' => ( 'working_time' !== $open_section ),
1244 + ),
1245 + function () use ( $unavailable_weekdays ) {
1246 + $days_ordered = array(
1247 + 1 => array(
1248 + 'short' => _x( 'Mo', 'Short weekday label', 'booking' ),
1249 + 'full' => __( 'Monday', 'booking' ),
1250 + ),
1251 + 2 => array(
1252 + 'short' => _x( 'Tu', 'Short weekday label', 'booking' ),
1253 + 'full' => __( 'Tuesday', 'booking' ),
1254 + ),
1255 + 3 => array(
1256 + 'short' => _x( 'We', 'Short weekday label', 'booking' ),
1257 + 'full' => __( 'Wednesday', 'booking' ),
1258 + ),
1259 + 4 => array(
1260 + 'short' => _x( 'Th', 'Short weekday label', 'booking' ),
1261 + 'full' => __( 'Thursday', 'booking' ),
1262 + ),
1263 + 5 => array(
1264 + 'short' => _x( 'Fr', 'Short weekday label', 'booking' ),
1265 + 'full' => __( 'Friday', 'booking' ),
1266 + ),
1267 + 6 => array(
1268 + 'short' => _x( 'Sa', 'Short weekday label', 'booking' ),
1269 + 'full' => __( 'Saturday', 'booking' ),
1270 + ),
1271 + 0 => array(
1272 + 'short' => _x( 'Su', 'Short weekday label', 'booking' ),
1273 + 'full' => __( 'Sunday', 'booking' ),
1274 + ),
1275 + );
1276 + ?>
1277 + <div class="wpbc_ag_weekday_grid">
1278 + <?php foreach ( $days_ordered as $day_num => $day_labels ) : ?>
1279 + <?php
1280 + $unavailable_day_aria_label = sprintf(
1281 + /* translators: %s: Full weekday name. */
1282 + __( 'Set %s as unavailable', 'booking' ),
1283 + $day_labels['full']
1284 + );
1285 + ?>
1286 + <label class="wpbc_ag_switch wpbc_ag_weekday_switch">
1287 + <input type="checkbox" name="booking_unavailable_days[]" value="<?php echo esc_attr( $day_num ); ?>" aria-label="<?php echo esc_attr( $unavailable_day_aria_label ); ?>" <?php checked( in_array( $day_num, $unavailable_weekdays, true ) ); ?> />
1288 + <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
1289 + <span class="wpbc_ag_switch_label"><?php echo esc_html( $day_labels['short'] ); ?></span>
1290 + </label>
1291 + <?php endforeach; ?>
1292 + </div>
903 1293 <p class="wpbc_ag_description"><?php esc_html_e( 'Selected weekdays will be unavailable in calendars and override other availability settings.', 'booking' ); ?></p>
904 1294 <?php
905 - }
906 - );
907 -
908 - WPBC_UI_Sidebar_Panels::render_collapsible_group(
1295 + }
1296 + );
1297 +
1298 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
1299 + array(
1300 + 'id' => 'wpbc_ag_working_time_group',
1301 + 'group' => 'general-availability-working-time',
1302 + 'title' => __( 'Working Time', 'booking' ),
1303 + 'open' => ( 'working_time' === $open_section ),
1304 + ),
1305 + function () use ( $working_time_settings, $working_time_resource_id, $working_time_resource, $working_time_options ) {
1306 + ?>
1307 + <div class="wpbc_ag_notice wpbc_ag_working_time_notice"><?php esc_html_e( 'Working Time restricts only time-based bookings, such as rangetime, start/end time, or start/duration time fields.', 'booking' ); ?></div>
1308 + <input type="hidden" name="booking_working_time_resource_id" data-wpbc-working-time-resource-id="1" value="<?php echo esc_attr( $working_time_resource_id ); ?>" />
1309 + <label class="wpbc_ag_switch wpbc_ag_switch_card">
1310 + <input type="checkbox" name="booking_working_time_enabled" value="On" <?php checked( $working_time_settings['enabled'], 'On' ); ?> />
1311 + <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
1312 + <span class="wpbc_ag_switch_label"><?php esc_html_e( 'Restrict time-based bookings to working time', 'booking' ); ?></span>
1313 + </label>
1314 +
1315 + <div class="wpbc_ag_working_time_block">
1316 + <div class="wpbc_ag_scope_title"><?php esc_html_e( 'Default working time', 'booking' ); ?></div>
1317 + <p class="wpbc_ag_description"><?php esc_html_e( 'Used by all booking resources unless a resource override is configured below.', 'booking' ); ?></p>
1318 + <?php wpbc_availability_general__render_working_time_weekdays( 'booking_working_time_default', $working_time_settings['default']['weekdays'], $working_time_options ); ?>
1319 + </div>
1320 +
1321 + <div class="wpbc_ag_working_time_block wpbc_ag_working_time_resource_block" data-wpbc-working-time-resource-block="1">
1322 + <div class="wpbc_ag_scope_title"><?php esc_html_e( 'Selected resource override', 'booking' ); ?></div>
1323 + <p class="wpbc_ag_description"><?php esc_html_e( 'This override follows the booking resource selected in the page toolbar.', 'booking' ); ?></p>
1324 + <div class="wpbc_ag_radio_stack wpbc_ag_working_time_mode">
1325 + <label><input type="radio" name="booking_working_time_resource_mode" value="inherit" <?php checked( $working_time_resource['mode'], 'inherit' ); ?> /> <?php esc_html_e( 'Inherit default working time', 'booking' ); ?></label>
1326 + <label><input type="radio" name="booking_working_time_resource_mode" value="custom" <?php checked( $working_time_resource['mode'], 'custom' ); ?> /> <?php esc_html_e( 'Use custom working time', 'booking' ); ?></label>
1327 + <label><input type="radio" name="booking_working_time_resource_mode" value="disabled" <?php checked( $working_time_resource['mode'], 'disabled' ); ?> /> <?php esc_html_e( 'Do not restrict this resource by working time', 'booking' ); ?></label>
1328 + </div>
1329 + <div class="wpbc_ag_working_time_custom" data-wpbc-working-time-resource-custom="1">
1330 + <?php wpbc_availability_general__render_working_time_weekdays( 'booking_working_time_resource', $working_time_resource['weekdays'], $working_time_options ); ?>
1331 + </div>
1332 + </div>
1333 + <?php
1334 + }
1335 + );
1336 +
1337 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
909 1338 array(
910 1339 'id' => 'wpbc_ag_today_group',
911 1340 'group' => 'general-availability-from-today',
912 1341 'title' => __( 'Availability From Today', 'booking' ),
913 - 'open' => true,
1342 + 'open' => false,
914 1343 ),
915 1344 function () use ( $hints, $is_available_limit_available ) {
916 1345 ?>
917 1346 <div class="wpbc_ag_field_row">
@@ -957,9 +1386,9 @@
957 1386 array(
958 1387 'id' => 'wpbc_ag_buffer_group',
959 1388 'group' => 'general-availability-buffer',
960 1389 'title' => __( 'Booking Buffer', 'booking' ),
961 - 'open' => true,
1390 + 'open' => false,
962 1391 ),
963 1392 function () use ( $buffer_type, $is_buffer_available ) {
964 1393 ?>
965 1394 <?php if ( ! $is_buffer_available ) : ?>
@@ -995,35 +1424,9 @@
995 1424 </div>
996 1425 <?php
997 1426 }
998 1427 );
999 -/* //TODO: implement it later.
1000 - WPBC_UI_Sidebar_Panels::render_collapsible_group(
1001 - array(
1002 - 'id' => 'wpbc_ag_working_time_group',
1003 - 'group' => 'general-availability-working-time',
1004 - 'title' => __( 'Working Time', 'booking' ),
1005 - ),
1006 - function () {
1007 - ?>
1008 - <div class="wpbc_ag_future_notice"><?php esc_html_e( 'Prepared for the future rule: time outside the working interval will become unavailable for booking resources.', 'booking' ); ?></div>
1009 - <label class="wpbc_ag_toggle wpbc_ag_toggle_full">
1010 - <input type="checkbox" disabled="disabled" />
1011 - <span><?php esc_html_e( 'Enable working time rule', 'booking' ); ?></span>
1012 - </label>
1013 - <div class="wpbc_ag_field_row">
1014 - <label class="wpbc_ag_field_label" for="wpbc_ag_working_time_start"><?php esc_html_e( 'Start time', 'booking' ); ?></label>
1015 - <?php wpbc_availability_general__render_select( 'wpbc_ag_working_time_start', wpbc_availability_general__get_working_time_options(), '09:00', array( 'disabled' => true ) ); ?>
1016 - </div>
1017 - <div class="wpbc_ag_field_row">
1018 - <label class="wpbc_ag_field_label" for="wpbc_ag_working_time_end"><?php esc_html_e( 'End time', 'booking' ); ?></label>
1019 - <?php wpbc_availability_general__render_select( 'wpbc_ag_working_time_end', wpbc_availability_general__get_working_time_options(), '18:00', array( 'disabled' => true ) ); ?>
1020 - </div>
1021 - <?php
1022 - }
1023 - );
1024 -*/
1025 - ?>
1428 + ?>
1026 1429 </div>
1027 1430 </form>
1028 1431 <?php
1029 1432 }