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 +406 -191 11.2 → 11.9 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2 /**
3 3 * General availability admin page.
4 4 *
@@ -105,13 +105,17 @@
105 105 'loading' => __( 'Loading', 'booking' ),
106 106 'buffer_preview' => __( 'Buffer preview', 'booking' ),
107 107 'before_booking' => __( 'Before booking', 'booking' ),
108 108 'after_booking' => __( 'After booking', 'booking' ),
109 - 'no_buffer' => __( 'No booking buffer is selected.', 'booking' ),
110 - ),
111 - )
112 - );
113 -}
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 +}
114 118 add_action( 'wpbc_enqueue_js_files', 'wpbc_availability_general_enqueue_js_files', 66 );
115 119
116 120 /**
117 121 * Print tooltip initializer for General Availability.
@@ -336,13 +340,13 @@
336 340
337 341 /**
338 342 * Get time options for future working-time controls.
339 343 *
340 - * @param string $selected Selected value.
344 + * @param string|string[] $selected Selected values that must remain available.
341 345 *
342 346 * @return array
343 347 */
344 -function wpbc_availability_general__get_working_time_options( $selected = '' ) {
348 +function wpbc_availability_general__get_working_time_options( $selected = '' ) {
345 349
346 350 $options = array();
347 351
348 352 for ( $minutes = 0; $minutes <= 1440; $minutes += 30 ) {
@@ -353,15 +357,79 @@
353 357
354 358 $options[ $value ] = $label;
355 359 }
356 360
357 - if ( '' !== $selected && ! isset( $options[ $selected ] ) ) {
358 - $options[ $selected ] = $selected;
359 - }
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 +}
360 431
361 - return $options;
362 -}
363 -
364 432 /**
365 433 * Render a select field.
366 434 *
367 435 * @param string $name Field name.
@@ -392,14 +460,15 @@
392 460
393 461 /**
394 462 * Render Working Time weekday rows.
395 463 *
396 - * @param string $prefix Field prefix.
397 - * @param array $weekdays Weekday intervals.
464 + * @param string $prefix Field prefix.
465 + * @param array $weekdays Weekday intervals.
466 + * @param array $time_options Allowed time choices.
398 467 *
399 468 * @return void
400 469 */
401 -function wpbc_availability_general__render_working_time_weekdays( $prefix, $weekdays ) {
470 +function wpbc_availability_general__render_working_time_weekdays( $prefix, $weekdays, $time_options = array() ) {
402 471
403 472 $days = array(
404 473 0 => _x( 'Sun', 'Short weekday name', 'booking' ),
405 474 1 => _x( 'Mon', 'Short weekday name', 'booking' ),
@@ -414,49 +483,103 @@
414 483 if ( $start_week_day > 6 ) {
415 484 $start_week_day = 0;
416 485 }
417 486 $days_ordered = array_slice( $days, $start_week_day, null, true ) + array_slice( $days, 0, $start_week_day, true );
418 - $time_options = wpbc_availability_general__get_working_time_options();
419 - ?>
420 - <div class="wpbc_ag_working_time_rows" data-wpbc-working-time-weekdays="<?php echo esc_attr( $prefix ); ?>">
421 - <?php foreach ( $days_ordered as $day_num => $day_title ) : ?>
422 - <?php
423 - $interval = ! empty( $weekdays[ $day_num ][0] ) ? $weekdays[ $day_num ][0] : array( 'start_second' => 9 * HOUR_IN_SECONDS, 'end_second' => 18 * HOUR_IN_SECONDS );
424 - $is_open = ! empty( $weekdays[ $day_num ] );
425 - ?>
426 - <div class="wpbc_ag_working_time_row">
427 - <label class="wpbc_ag_switch wpbc_ag_working_time_day">
428 - <input type="checkbox" name="<?php echo esc_attr( $prefix ); ?>_days[]" value="<?php echo esc_attr( $day_num ); ?>" <?php checked( $is_open ); ?> />
429 - <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
430 - <span class="wpbc_ag_switch_label"><?php echo esc_html( $day_title ); ?></span>
431 - </label>
432 - <div class="wpbc_ag_working_time_times">
433 - <?php
434 - wpbc_availability_general__render_select(
435 - $prefix . '_start[' . $day_num . ']',
436 - $time_options,
437 - wpbc_working_time__seconds_to_time( $interval['start_second'] ),
438 - array(
439 - 'id' => $prefix . '_start_' . $day_num,
440 - 'class' => 'wpbc_ag_field_control wpbc_ag_working_time_start',
441 - )
442 - );
443 - wpbc_availability_general__render_select(
444 - $prefix . '_end[' . $day_num . ']',
445 - $time_options,
446 - wpbc_working_time__seconds_to_time( $interval['end_second'] ),
447 - array(
448 - 'id' => $prefix . '_end_' . $day_num,
449 - 'class' => 'wpbc_ag_field_control wpbc_ag_working_time_end',
450 - )
451 - );
452 - ?>
453 - </div>
454 - </div>
455 - <?php endforeach; ?>
456 - </div>
457 - <?php
458 -}
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 +}
459 582
460 583 /**
461 584 * Get default General Availability settings.
462 585 *
@@ -477,16 +600,37 @@
477 600 );
478 601 }
479 602
480 603 /**
481 - * Validate posted one-interval-per-weekday Working Time schedule.
482 - *
483 - * @param array $post_data Raw post data.
484 - * @param string $prefix Field prefix.
485 - *
486 - * @return array
487 - */
488 -function wpbc_availability_general__validate_working_time_weekdays( $post_data, $prefix ) {
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 ) {
489 633
490 634 $weekdays = array();
491 635 $enabled_days = array();
492 636 $start_values = isset( $post_data[ $prefix . '_start' ] ) && is_array( $post_data[ $prefix . '_start' ] ) ? wp_unslash( $post_data[ $prefix . '_start' ] ) : array();
@@ -492,44 +636,54 @@
492 636 $start_values = isset( $post_data[ $prefix . '_start' ] ) && is_array( $post_data[ $prefix . '_start' ] ) ? wp_unslash( $post_data[ $prefix . '_start' ] ) : array();
493 637 $end_values = isset( $post_data[ $prefix . '_end' ] ) && is_array( $post_data[ $prefix . '_end' ] ) ? wp_unslash( $post_data[ $prefix . '_end' ] ) : array();
494 638
495 639 if ( isset( $post_data[ $prefix . '_days' ] ) && is_array( $post_data[ $prefix . '_days' ] ) ) {
496 - foreach ( $post_data[ $prefix . '_days' ] as $day_num ) {
497 - $day_num = absint( $day_num );
498 - if ( $day_num >= 0 && $day_num <= 6 ) {
499 - $enabled_days[] = $day_num;
500 - }
501 - }
502 - }
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 +}
503 677
504 - for ( $day = 0; $day <= 6; $day++ ) {
505 - $weekdays[ $day ] = array();
506 - if ( ! in_array( $day, $enabled_days, true ) ) {
507 - continue;
508 - }
509 -
510 - $start = isset( $start_values[ $day ] ) ? wpbc_working_time__time_to_seconds( sanitize_text_field( $start_values[ $day ] ) ) : 0;
511 - $end = isset( $end_values[ $day ] ) ? wpbc_working_time__time_to_seconds( sanitize_text_field( $end_values[ $day ] ) ) : 0;
512 -
513 - if ( $start < $end ) {
514 - $weekdays[ $day ][] = array(
515 - 'start_second' => $start,
516 - 'end_second' => $end,
517 - );
518 - }
519 - }
520 -
521 - return $weekdays;
522 -}
523 -
524 678 /**
525 679 * Validate posted Working Time settings.
526 680 *
527 681 * @param array $post_data Raw post data.
528 682 *
529 - * @return array
530 - */
531 -function wpbc_availability_general__validate_working_time_data( $post_data ) {
683 + * @return array|WP_Error Validated Working Time settings or an error.
684 + */
685 +function wpbc_availability_general__validate_working_time_data( $post_data ) {
532 686
533 687 $current_settings = wpbc_working_time__get_settings();
534 688 $resource_id = isset( $post_data['booking_working_time_resource_id'] ) ? absint( wp_unslash( $post_data['booking_working_time_resource_id'] ) ) : 0;
535 689 $resource_mode = isset( $post_data['booking_working_time_resource_mode'] ) ? sanitize_key( wp_unslash( $post_data['booking_working_time_resource_mode'] ) ) : 'inherit';
@@ -537,18 +691,33 @@
537 691 if ( ! in_array( $resource_mode, array( 'inherit', 'custom', 'disabled' ), true ) ) {
538 692 $resource_mode = 'inherit';
539 693 }
540 694
541 - $current_settings['enabled'] = ( isset( $post_data['booking_working_time_enabled'] ) && 'On' === sanitize_text_field( wp_unslash( $post_data['booking_working_time_enabled'] ) ) ) ? 'On' : 'Off';
542 - $current_settings['default'] = array(
543 - 'weekdays' => wpbc_availability_general__validate_working_time_weekdays( $post_data, 'booking_working_time_default' ),
544 - );
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 + );
545 714
546 715 if ( $resource_id > 0 ) {
547 - $current_settings['resources'][ $resource_id ] = array(
548 - 'mode' => $resource_mode,
549 - 'weekdays' => wpbc_availability_general__validate_working_time_weekdays( $post_data, 'booking_working_time_resource' ),
550 - );
716 + $current_settings['resources'][ $resource_id ] = array(
717 + 'mode' => $resource_mode,
718 + 'weekdays' => $resource_weekdays,
719 + );
551 720 }
552 721
553 722 return wpbc_working_time__normalize_settings( $current_settings );
554 723 }
@@ -557,9 +726,9 @@
557 726 * Validate posted settings.
558 727 *
559 728 * @param array $post_data Raw post data.
560 729 *
561 - * @return array
730 + * @return array|WP_Error Validated settings or an error.
562 731 */
563 732 function wpbc_availability_general__validate_data( $post_data ) {
564 733
565 734 $cleaned = wpbc_availability_general__get_default_settings();
@@ -603,9 +772,12 @@
603 772 $cleaned[ $key ] = preg_match( '/^\d+d$/', $value ) ? $value : '';
604 773 }
605 774 }
606 775
607 - $cleaned['working_time'] = wpbc_availability_general__validate_working_time_data( $post_data );
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 + }
608 780
609 781 return $cleaned;
610 782 }
611 783
@@ -857,9 +1029,9 @@
857 1029
858 1030 /**
859 1031 * General Availability tab.
860 1032 */
861 -class WPBC_Page_Availability_General_Dedicated extends WPBC_Page_Structure {
1033 +class WPBC_Page_Availability_General_Dedicated extends WPBC_Page_Structure {
862 1034
863 1035 /**
864 1036 * Whether settings were saved during this request.
865 1037 *
@@ -864,9 +1036,16 @@
864 1036 * Whether settings were saved during this request.
865 1037 *
866 1038 * @var bool
867 1039 */
868 - 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;
869 1048
870 1049 /**
871 1050 * Menu slug.
872 1051 *
@@ -890,17 +1069,17 @@
890 1069
891 1070 $tabs = array();
892 1071
893 1072 $tabs['general_availability'] = array(
894 - 'is_show_top_path' => false,
1073 + 'is_show_top_path' => true,
895 1074 'right_vertical_sidebar__is_show' => true,
896 1075 'right_vertical_sidebar__default_view_mode' => '',
897 1076 'right_vertical_sidebar_compact__is_show' => true,
898 - 'left_navigation__default_view_mode' => 'compact',
899 - 'title' => __( 'General Availability', 'booking' ) .
900 - '<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' ),
901 1080 'hint' => __( 'Define global front-end availability rules for all calendars, including unavailable weekdays, booking buffers, and availability limits.', 'booking' ),
902 - 'page_title' => __( 'General Availability', 'booking' ),
1081 + 'page_title' => __( 'Schedule & Rules', 'booking' ),
903 1082 'link' => '',
904 1083 'position' => '',
905 1084 'css_classes' => 'wpbc_top_tab__general_availability',
906 1085 'icon' => '',
@@ -910,8 +1089,9 @@
910 1089 'disabled' => false,
911 1090 'hided' => false,
912 1091 'subtabs' => array(),
913 1092 'folder_style' => 'order:93;',
1093 + 'is_show_top_navigation' => true,
914 1094 );
915 1095
916 1096 return $tabs;
917 1097 }
@@ -935,10 +1115,15 @@
935 1115 if ( ! wpbc_is_mu_user_can_be_here( 'only_super_admin' ) || ! current_user_can( wpbc_availability_general__get_manage_cap() ) ) {
936 1116 return;
937 1117 }
938 1118
939 - $cleaned_data = wpbc_availability_general__validate_data( $_POST ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
940 - 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 );
941 1126
942 1127 $this->is_updated = true;
943 1128 }
944 1129
@@ -965,9 +1150,9 @@
965 1150 'icon' => 'wpbc-bi-info-circle',
966 1151 ),
967 1152 ),
968 1153 array(
969 - 'aria_label' => __( 'General Availability Panels', 'booking' ),
1154 + 'aria_label' => __( 'Schedule & Rules Panels', 'booking' ),
970 1155 'context' => 'general_availability',
971 1156 'class' => 'wpbc_ag_rightbar_tabs',
972 1157 )
973 1158 );
@@ -1018,9 +1203,12 @@
1018 1203 $is_buffer_available = class_exists( 'wpdev_bk_biz_m' );
1019 1204 $is_available_limit_available = class_exists( 'wpdev_bk_biz_m' );
1020 1205 $unavailable_weekdays = array();
1021 1206 $working_time_settings = wpbc_working_time__get_settings();
1022 - $working_time_default_settings = wpbc_working_time__get_default_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 + );
1023 1211 $working_time_resource_id = wpbc_availability_general__get_preview_resource_id();
1024 1212 $open_section = wpbc_availability_general__get_open_section();
1025 1213 $working_time_resource = isset( $working_time_settings['resources'][ $working_time_resource_id ] )
1026 1214 ? $working_time_settings['resources'][ $working_time_resource_id ]
@@ -1034,15 +1222,20 @@
1034 1222 $unavailable_weekdays[] = $i;
1035 1223 }
1036 1224 }
1037 1225
1038 - 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' ) );
1039 1227 ?>
1040 - <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">
1041 1229 <?php wp_nonce_field( 'wpbc_settings_page_' . $form_name ); ?>
1042 1230 <input type="hidden" name="is_form_submitted_<?php echo esc_attr( $form_name ); ?>" value="1" />
1043 - <div class="wpbc_bfb__inspector__body wpbc_ag_inspector_body">
1044 - <?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
1045 1238 WPBC_UI_Sidebar_Panels::render_collapsible_group(
1046 1239 array(
1047 1240 'id' => 'wpbc_ag_weekdays_group',
1048 1241 'group' => 'general-availability-weekdays',
@@ -1047,40 +1240,102 @@
1047 1240 'id' => 'wpbc_ag_weekdays_group',
1048 1241 'group' => 'general-availability-weekdays',
1049 1242 'title' => __( 'Unavailable Weekdays', 'booking' ),
1050 1243 'open' => ( 'working_time' !== $open_section ),
1051 - ),
1052 - function () use ( $unavailable_weekdays ) {
1053 - $days = array(
1054 - 0 => __( 'Sunday', 'booking' ),
1055 - 1 => __( 'Monday', 'booking' ),
1056 - 2 => __( 'Tuesday', 'booking' ),
1057 - 3 => __( 'Wednesday', 'booking' ),
1058 - 4 => __( 'Thursday', 'booking' ),
1059 - 5 => __( 'Friday', 'booking' ),
1060 - 6 => __( 'Saturday', 'booking' ),
1061 - );
1062 - $start_week_day = absint( get_bk_option( 'booking_start_day_weeek' ) );
1063 - if ( $start_week_day > 6 ) {
1064 - $start_week_day = 0;
1065 - }
1066 - $days_ordered = array_slice( $days, $start_week_day, null, true ) + array_slice( $days, 0, $start_week_day, true );
1067 - ?>
1068 - <div class="wpbc_ag_weekday_grid">
1069 - <?php foreach ( $days_ordered as $day_num => $day_title ) : ?>
1070 - <label class="wpbc_ag_switch">
1071 - <input type="checkbox" name="booking_unavailable_days[]" value="<?php echo esc_attr( $day_num ); ?>" <?php checked( in_array( $day_num, $unavailable_weekdays, true ) ); ?> />
1072 - <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
1073 - <span class="wpbc_ag_switch_label"><?php echo esc_html( $day_title ); ?></span>
1074 - </label>
1075 - <?php endforeach; ?>
1076 - </div>
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>
1077 1293 <p class="wpbc_ag_description"><?php esc_html_e( 'Selected weekdays will be unavailable in calendars and override other availability settings.', 'booking' ); ?></p>
1078 1294 <?php
1079 - }
1080 - );
1081 -
1082 - 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(
1083 1338 array(
1084 1339 'id' => 'wpbc_ag_today_group',
1085 1340 'group' => 'general-availability-from-today',
1086 1341 'title' => __( 'Availability From Today', 'booking' ),
@@ -1169,49 +1424,9 @@
1169 1424 </div>
1170 1425 <?php
1171 1426 }
1172 1427 );
1173 -
1174 - WPBC_UI_Sidebar_Panels::render_collapsible_group(
1175 - array(
1176 - 'id' => 'wpbc_ag_working_time_group',
1177 - 'group' => 'general-availability-working-time',
1178 - 'title' => __( 'Working Time', 'booking' ),
1179 - 'open' => ( 'working_time' === $open_section ),
1180 - ),
1181 - function () use ( $working_time_settings, $working_time_resource_id, $working_time_resource ) {
1182 - ?>
1183 - <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>
1184 - <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 ); ?>" />
1185 - <label class="wpbc_ag_switch wpbc_ag_switch_card">
1186 - <input type="checkbox" name="booking_working_time_enabled" value="On" <?php checked( $working_time_settings['enabled'], 'On' ); ?> />
1187 - <span class="wpbc_ag_switch_control" aria-hidden="true"><span class="wpbc_ag_switch_knob"></span></span>
1188 - <span class="wpbc_ag_switch_label"><?php esc_html_e( 'Restrict time-based bookings to working time', 'booking' ); ?></span>
1189 - </label>
1190 -
1191 - <div class="wpbc_ag_working_time_block">
1192 - <div class="wpbc_ag_scope_title"><?php esc_html_e( 'Default working time', 'booking' ); ?></div>
1193 - <p class="wpbc_ag_description"><?php esc_html_e( 'Used by all booking resources unless a resource override is configured below.', 'booking' ); ?></p>
1194 - <?php wpbc_availability_general__render_working_time_weekdays( 'booking_working_time_default', $working_time_settings['default']['weekdays'] ); ?>
1195 - </div>
1196 -
1197 - <div class="wpbc_ag_working_time_block wpbc_ag_working_time_resource_block" data-wpbc-working-time-resource-block="1">
1198 - <div class="wpbc_ag_scope_title"><?php esc_html_e( 'Selected resource override', 'booking' ); ?></div>
1199 - <p class="wpbc_ag_description"><?php esc_html_e( 'This override follows the booking resource selected in the page toolbar.', 'booking' ); ?></p>
1200 - <div class="wpbc_ag_radio_stack wpbc_ag_working_time_mode">
1201 - <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>
1202 - <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>
1203 - <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>
1204 - </div>
1205 - <div class="wpbc_ag_working_time_custom" data-wpbc-working-time-resource-custom="1">
1206 - <?php wpbc_availability_general__render_working_time_weekdays( 'booking_working_time_resource', $working_time_resource['weekdays'] ); ?>
1207 - </div>
1208 - </div>
1209 - <?php
1210 - }
1211 - );
1212 -
1213 - ?>
1428 + ?>
1214 1429 </div>
1215 1430 </form>
1216 1431 <?php
1217 1432 }