587 lines
21 KiB
PHP
587 lines
21 KiB
PHP
<?php
|
|
/**
|
|
* Simply Schedule Appointments Availability Functions.
|
|
*
|
|
* @since 0.0.3
|
|
* @package Simply_Schedule_Appointments
|
|
*/
|
|
use League\Period\Period;
|
|
|
|
/**
|
|
* Simply Schedule Appointments Availability Functions.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
class SSA_Availability_Functions {
|
|
/**
|
|
* Parent plugin class.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @var Simply_Schedule_Appointments
|
|
*/
|
|
protected $plugin = null;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @param Simply_Schedule_Appointments $plugin Main plugin object.
|
|
*/
|
|
public function __construct( $plugin ) {
|
|
$this->plugin = $plugin;
|
|
$this->hooks();
|
|
}
|
|
|
|
/**
|
|
* Initiate our hooks.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
public function hooks() {
|
|
add_filter( 'ssa/buffer_period/start_date', array( $this, 'get_period_with_buffered_start_date' ), 10, 3 );
|
|
add_filter( 'ssa/buffer_period/end_date', array( $this, 'get_period_with_buffered_end_date' ), 10, 3 );
|
|
}
|
|
|
|
public function get_args( $appointment_type, $args = array() ) {
|
|
$default_args = apply_filters( 'ssa/availability/default_args', array(
|
|
'start_date' => '',
|
|
'duration' => '',
|
|
'end_date' => '',
|
|
|
|
'start_date_min' => '',
|
|
'start_date_max' => '',
|
|
'end_date_min' => '',
|
|
'end_date_max' => '',
|
|
'buffered' => false,
|
|
|
|
'excluded_appointment_ids' => array(),
|
|
'appointment_statuses' => SSA_Appointment_Model::get_unavailable_statuses(),
|
|
), $appointment_type );
|
|
|
|
$args = shortcode_atts( $default_args, $args );
|
|
|
|
return $args;
|
|
}
|
|
|
|
public function get_period_with_buffered_start_date( $period, $original_period, $appointment_type ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( !empty( $appointment_type['buffer_before'] ) ) {
|
|
$buffer_before = '-' . absint( $appointment_type['buffer_before'] ) . ' MIN';
|
|
$period = $period->moveStartDate( $buffer_before );
|
|
}
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $period;
|
|
}
|
|
|
|
public function get_period_with_buffered_end_date( $period, $original_period, $appointment_type ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( !empty( $appointment_type['buffer_after'] ) ) {
|
|
$buffer_after = '+' . absint( $appointment_type['buffer_after'] ) . ' MIN';
|
|
$period = $period->moveEndDate( $buffer_after );
|
|
}
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $period;
|
|
}
|
|
|
|
public function is_period_available( $appointment_type, $args=array() ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return new WP_Error();
|
|
}
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
|
|
if ( empty( $args['duration'] ) ) {
|
|
$args['duration'] = $appointment_type['duration'];
|
|
}
|
|
|
|
if ( empty( $args['duration'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return false;
|
|
}
|
|
|
|
if ( empty( $args['end_date'] ) ) {
|
|
$args['end_date'] = ssa_datetime( $args['start_date'] )->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
if ( empty( $args['start_date_min'] ) ) {
|
|
$args['start_date_min'] = ssa_datetime( $args['start_date'] )->sub( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) )->sub( new DateInterval( 'P7D' ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
if ( empty( $args['start_date_max'] ) ) {
|
|
$args['start_date_max'] = ssa_datetime( $args['start_date'] )->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) )->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) )->add( new DateInterval( 'P7D' ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
$desired_period = new Period( ssa_datetime( $args['start_date'] ), ssa_datetime( $args['end_date'] ) );
|
|
|
|
$bookable_appointments = $this->get_bookable_appointments( $appointment_type, $args );
|
|
foreach ( $bookable_appointments as $bookable_appointment_key => $bookable_appointment ) {
|
|
if ( $bookable_appointment['period']->contains( $desired_period ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return false;
|
|
}
|
|
|
|
public function get_all_available_periods( $appointment_type, $args=array() ) {
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
return new WP_Error();
|
|
}
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
ssa_defensive_timezone_fix();
|
|
if ( $this->plugin->settings_installed->is_enabled( 'advanced_scheduling' ) ) {
|
|
if ( ! empty( $appointment_type['availability_start_date'] ) && $appointment_type['availability_start_date'] !== '0000-00-00 00:00:00' ) {
|
|
if ( empty( $args['start_date_min'] ) || $args['start_date_min'] < $appointment_type['availability_start_date'] ) {
|
|
$args['start_date_min'] = $appointment_type['availability_start_date'];
|
|
}
|
|
|
|
if ( $args['start_date_max'] < $args['start_date_min'] ) {
|
|
$args['start_date_max'] = ssa_datetime( $appointment_type['availability_start_date'] )->add( new DateInterval( 'P30D' ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( $this->plugin->settings_installed->is_enabled( 'advanced_scheduling' ) ) {
|
|
if ( ! empty( $appointment_type['availability_end_date'] ) && $appointment_type['availability_end_date'] !== '0000-00-00 00:00:00' && ( empty( $args['start_date_max'] ) || $args['start_date_max'] > $appointment_type['availability_end_date'] ) ) {
|
|
$args['start_date_max'] = $appointment_type['availability_end_date'];
|
|
if ( $args['start_date_min'] > $args['start_date_max'] ) {
|
|
ssa_defensive_timezone_reset();
|
|
return array();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( empty( $appointment_type['availability_type'] ) || $appointment_type['availability_type'] != 'custom') {
|
|
$available_periods = $this->get_default_available_periods( $appointment_type, $args );
|
|
} else {
|
|
// TODO: support custom available periods
|
|
// $available_periods = $this->get_custom_available_periods( $appointment_type['id'], $args );
|
|
}
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $available_periods;
|
|
}
|
|
|
|
public function get_custom_available_periods( $appointment_type, $args=array() ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return new WP_Error();
|
|
}
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
$custom_available_blocks = $this->plugin->availability_model->query( array_merge( array(
|
|
'appointment_type_id' => $appointment_type['id'],
|
|
'is_available' => 1,
|
|
'orderby' => 'start_date',
|
|
'order' => 'ASC',
|
|
), $args ) );
|
|
|
|
$custom_available_periods = array();
|
|
foreach ($custom_available_blocks as $key => $custom_available_block) {
|
|
$custom_available_periods[] = new Period( $custom_available_block['start_date'], $custom_available_block['end_date'] );
|
|
}
|
|
|
|
$custom_available_periods = $this->combine_abutting_periods( $custom_available_periods );
|
|
ssa_defensive_timezone_reset();
|
|
return $custom_available_periods;
|
|
}
|
|
|
|
public function get_default_available_periods( $appointment_type, $args ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return new WP_Error();
|
|
}
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
$availability_home_timezone = $this->plugin->utils->get_datetimezone( $appointment_type['id'] );
|
|
|
|
$default_available_periods = array();
|
|
|
|
$start_date = ssa_datetime( $args['start_date_min'] );
|
|
$end_date = ssa_datetime( $args['start_date_max'] );
|
|
$start_date = $start_date->sub( new DateInterval( 'P1D' ) ); // help avoid timezone issues, our get_bookable_appointments() function will remove ones we don't need
|
|
$end_date = $end_date->add( new DateInterval( 'P1D' ) ); // help avoid timezone issues, our get_bookable_appointments() function will remove ones we don't need
|
|
$range_period = new Period( $start_date, $end_date );
|
|
foreach ($range_period->getDatePeriod( '1 DAY' ) as $datetime) {
|
|
$day_of_week = $datetime->format( 'l' );
|
|
if ( empty( $appointment_type['availability'][$day_of_week][0]['time_start'] ) ) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($appointment_type['availability'][$day_of_week] as $time_block ) {
|
|
$start_date = SSA_Utils::get_datetime_in_utc( $datetime->format( 'Y-m-d '.$time_block['time_start'] ), $availability_home_timezone );
|
|
if ( 'start_times' === $appointment_type['availability_type'] ) {
|
|
$end_date = $start_date->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) );
|
|
} else {
|
|
// available_blocks (default behavior)
|
|
$end_date = SSA_Utils::get_datetime_in_utc( $datetime->format( 'Y-m-d '.$time_block['time_end'] ), $availability_home_timezone );
|
|
}
|
|
|
|
$new_period = new Period(
|
|
$start_date,
|
|
$end_date
|
|
);
|
|
|
|
$default_available_periods[] = $new_period;
|
|
}
|
|
}
|
|
|
|
$default_available_periods = $this->combine_abutting_periods( $default_available_periods );
|
|
ssa_defensive_timezone_reset();
|
|
return $default_available_periods;
|
|
}
|
|
|
|
public function get_booked_periods( $appointment_type, $args ) {
|
|
ssa_defensive_timezone_fix();
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return new WP_Error();
|
|
}
|
|
|
|
if ( apply_filters( 'ssa/get_booked_periods/should_separate_availability_for_appointment_types', false ) ) {
|
|
$args['appointment_type_id'] = $appointment_type['id'];
|
|
}
|
|
|
|
if ( empty( $args['appointment_type_id'] ) ) {
|
|
$developer_settings = $this->plugin->developer_settings->get();
|
|
if ( ! empty( $developer_settings['separate_appointment_type_availability'] ) ) {
|
|
$args['appointment_type_id'] = $appointment_type['id'];
|
|
}
|
|
}
|
|
|
|
$booked_blocks = $this->plugin->appointment_model->query( array_merge( array(
|
|
'status' => $args['appointment_statuses'],
|
|
'number' => -1,
|
|
), $args ) );
|
|
|
|
$booked_periods = array();
|
|
foreach ($booked_blocks as $key => $booked_block) {
|
|
if ( ! empty( $args['excluded_appointment_ids'] ) && is_array( $args['excluded_appointment_ids'] ) ) {
|
|
if ( in_array( $booked_block['id'], $args['excluded_appointment_ids'] ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( $booked_block['end_date'] <= $booked_block['start_date'] ) {
|
|
$booked_block['end_date'] = ssa_datetime( $booked_block['start_date'] )->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) )->format( 'Y-m-d H:i:s' );
|
|
}
|
|
|
|
|
|
if ( $booked_block['end_date'] < $booked_block['start_date'] ) {
|
|
ssa_debug_log( __CLASS__ . ' ' . __FUNCTION__ . '():' . __LINE__ );
|
|
ssa_debug_log( 'The ending datepoint must be greater or equal to the starting datepoint in appointment ID ' . $this->id, 10 );
|
|
|
|
$booked_period = new Period(
|
|
$booked_block['start_date'],
|
|
$booked_block['start_date']
|
|
);
|
|
} else {
|
|
$booked_period = new Period(
|
|
$booked_block['start_date'],
|
|
$booked_block['end_date']
|
|
);
|
|
}
|
|
|
|
|
|
if ( !empty( $args['buffered'] ) ) {
|
|
$booked_period = apply_filters( 'ssa/buffer_period/start_date', $booked_period, $booked_period, $appointment_type );
|
|
$booked_period = apply_filters( 'ssa/buffer_period/end_date', $booked_period, $booked_period, $appointment_type );
|
|
if ( empty( $booked_period ) ) {
|
|
continue;
|
|
}
|
|
}
|
|
$booked_periods[] = $booked_period;
|
|
}
|
|
|
|
$booked_periods = apply_filters( 'ssa/get_booked_periods/booked_periods', $booked_periods, $appointment_type );
|
|
ssa_defensive_timezone_reset();
|
|
return $booked_periods;
|
|
}
|
|
|
|
public function get_blocked_periods( $appointment_type, $args ) {
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
return new WP_Error();
|
|
}
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
$blocked_periods = array();
|
|
|
|
ssa_defensive_timezone_fix();
|
|
|
|
$blocked_periods = apply_filters( 'ssa/get_blocked_periods/blocked_periods', $blocked_periods, $appointment_type, $args );
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $blocked_periods;
|
|
}
|
|
|
|
public function get_bookable_appointments( $appointment_type, $args=array() ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( (int)$appointment_type == $appointment_type ) {
|
|
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type );
|
|
}
|
|
if ( empty( $appointment_type['id'] ) ) {
|
|
ssa_defensive_timezone_reset();
|
|
return new WP_Error();
|
|
}
|
|
|
|
|
|
$appointment_type = apply_filters( 'ssa/get_bookable_appointments/appointment_type', $appointment_type );
|
|
$availability_home_timezone = $this->plugin->utils->get_datetimezone( $appointment_type['id'] );
|
|
|
|
$args = $this->get_args( $appointment_type, $args );
|
|
|
|
if ( !empty( $args['start_date_min'] ) ) {
|
|
$start_date_min_datetime = ssa_datetime( $args['start_date_min'] );
|
|
$start_date_with_min_booking_notice_datetime = ssa_datetime();
|
|
if ( !empty( $appointment_type['min_booking_notice'] ) ) {
|
|
$start_date_with_min_booking_notice_datetime = ssa_datetime()->add( new DateInterval( 'PT'.$appointment_type['min_booking_notice'].'M' ) );
|
|
}
|
|
|
|
if ( $start_date_with_min_booking_notice_datetime > $start_date_min_datetime ) {
|
|
$start_date_min_datetime = $start_date_with_min_booking_notice_datetime;
|
|
}
|
|
}
|
|
|
|
if ( !empty( $args['start_date_max'] ) ) {
|
|
$start_date_max_datetime = ssa_datetime( $args['start_date_max'] );
|
|
}
|
|
|
|
if ( $this->plugin->settings_installed->is_enabled( 'advanced_scheduling' ) ) {
|
|
if ( !empty( $appointment_type['max_booking_notice'] ) ) {
|
|
$start_date_with_max_booking_notice_datetime = ssa_datetime()->add( new DateInterval( 'PT'.$appointment_type['max_booking_notice'].'M' ) );
|
|
if ( empty( $start_date_max_datetime ) || $start_date_with_max_booking_notice_datetime < $start_date_max_datetime ) {
|
|
$start_date_max_datetime = $start_date_with_max_booking_notice_datetime;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( empty( $appointment_type['availability_increment'] ) ) {
|
|
$appointment_type['availability_increment'] = 15;
|
|
}
|
|
if ( !empty( $args['end_date_min'] ) ) {
|
|
$end_date_min_datetime = ssa_datetime( $args['end_date_min'] );
|
|
}
|
|
|
|
if ( !empty( $args['end_date_max'] ) ) {
|
|
$end_date_max_datetime = ssa_datetime( $args['end_date_max'] );
|
|
}
|
|
|
|
if ( $this->plugin->settings_installed->is_enabled( 'advanced_scheduling' ) ) {
|
|
if ( !empty( $appointment_type['max_booking_notice'] ) ) {
|
|
$end_date_with_max_booking_notice_datetime = ssa_datetime()->add( new DateInterval( 'PT'.$appointment_type['max_booking_notice'].'M' ) )->add( new DateInterval( 'PT'.$appointment_type['duration'].'M' ) );
|
|
if ( empty( $end_date_max_datetime ) || $end_date_with_max_booking_notice_datetime < $end_date_max_datetime ) {
|
|
$end_date_max_datetime = $end_date_with_max_booking_notice_datetime;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
$available_periods = $this->get_all_available_periods( $appointment_type, $args );
|
|
$booked_periods = $this->get_booked_periods( $appointment_type, $args );
|
|
$blocked_periods = $this->get_blocked_periods( $appointment_type, $args );
|
|
|
|
$bookable_periods = array();
|
|
foreach ($available_periods as $key => $available_period) {
|
|
foreach ($available_period->getDatePeriod($appointment_type['availability_increment'].' MIN') as $start_datetime) {
|
|
/* Look at every bookable start time and determine if this slot is available */
|
|
|
|
/* Does this potential appointment start within min/max datetime? */
|
|
if ( !empty( $start_date_min_datetime ) && $start_datetime < $start_date_min_datetime ) {
|
|
continue;
|
|
}
|
|
if ( !empty( $start_date_max_datetime ) && $start_datetime > $start_date_max_datetime ) {
|
|
continue;
|
|
}
|
|
|
|
/* If this is a "start_times" availability type, is it a valid start time? */
|
|
if ( 'start_times' === $appointment_type['availability_type'] ) {
|
|
$local_start_datetime = $start_datetime->setTimezone( $availability_home_timezone );
|
|
$day_of_week = $local_start_datetime->format( 'l' );
|
|
if ( empty( $appointment_type['availability'][$day_of_week][0]['time_start'] ) ) {
|
|
continue; // no start times for this day of the week
|
|
}
|
|
|
|
$is_valid_start_time = false;
|
|
foreach ($appointment_type['availability'][$day_of_week] as $key => $slot) {
|
|
if ( $is_valid_start_time ) {
|
|
continue;
|
|
}
|
|
|
|
if ( $slot['time_start'] == $local_start_datetime->format( 'H:i:s' ) ) {
|
|
$is_valid_start_time = true;
|
|
}
|
|
|
|
if ( $slot['time_start'] == $local_start_datetime->format( 'G:i:s' ) ) {
|
|
$is_valid_start_time = true;
|
|
}
|
|
}
|
|
|
|
if ( ! $is_valid_start_time ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
$bookable_period = Period::after($start_datetime, new DateInterval('PT'.$appointment_type['duration'].'M'));
|
|
|
|
/* Does this potential appointment end within min/max datetime? */
|
|
if ( !empty( $end_date_min_datetime ) && $bookable_period->getEndDate() < $end_date_min_datetime ) {
|
|
continue;
|
|
}
|
|
if ( !empty( $end_date_max_datetime ) && $bookable_period->getEndDate() > $end_date_max_datetime ) {
|
|
continue;
|
|
}
|
|
|
|
/* Does this potential appointment fit within the available block? */
|
|
if ( !$available_period->contains( $bookable_period ) ) {
|
|
continue;
|
|
}
|
|
|
|
/* Does this potential appointment conflict with a blocked time? */
|
|
$buffered_bookable_period = apply_filters( 'ssa/buffer_period/start_date', $bookable_period, $bookable_period, $appointment_type );
|
|
$buffered_bookable_period = apply_filters( 'ssa/buffer_period/end_date', $buffered_bookable_period, $bookable_period, $appointment_type );
|
|
|
|
foreach ($blocked_periods as $key => $blocked_period) {
|
|
$buffered_blocked_period = apply_filters( 'ssa/buffer_period/blocked_period', $blocked_period, $blocked_period, $appointment_type );
|
|
if ( $buffered_bookable_period->overlaps( $buffered_blocked_period ) ) {
|
|
/* They overlap, but let's make sure that our double-buffering didn't exclude a valid bookable period (collapse margins) */
|
|
if ( $bookable_period->overlaps( $blocked_period ) ) {
|
|
// even unbuffered they overlap
|
|
continue 2;
|
|
}
|
|
|
|
$gap = $bookable_period->gap( $blocked_period );
|
|
$minimum_gap_in_seconds = 60 * max( absint( $appointment_type['buffer_before'] ), absint( $appointment_type['buffer_after'] ) );
|
|
if ( $gap->getTimestampInterval() < $minimum_gap_in_seconds ) {
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Does this potential appointment conflict with a previously-booked time? */
|
|
foreach ($booked_periods as $key => $booked_period) {
|
|
$buffered_booked_period = apply_filters( 'ssa/buffer_period/start_date', $booked_period, $booked_period, $appointment_type );
|
|
$buffered_booked_period = apply_filters( 'ssa/buffer_period/end_date', $buffered_booked_period, $booked_period, $appointment_type );
|
|
if ( $buffered_bookable_period->overlaps( $buffered_booked_period ) ) {
|
|
/* They overlap, but let's make sure that our double-buffering didn't exclude a valid bookable period (collapse margins) */
|
|
if ( $bookable_period->overlaps( $booked_period ) ) {
|
|
// even unbuffered they overlap
|
|
continue 2;
|
|
}
|
|
|
|
$gap = $bookable_period->gap( $booked_period );
|
|
$minimum_gap_in_seconds = 60 * max( absint( $appointment_type['buffer_before'] ), absint( $appointment_type['buffer_after'] ) );
|
|
if ( $gap->getTimestampInterval() < $minimum_gap_in_seconds ) {
|
|
continue 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
$bookable_periods[] = $bookable_period;
|
|
}
|
|
}
|
|
|
|
$bookable_appointments = array();
|
|
foreach ($bookable_periods as $key => $bookable_period) {
|
|
$bookable_appointments[] = array(
|
|
'period' => $bookable_period,
|
|
);
|
|
}
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $bookable_appointments;
|
|
}
|
|
|
|
/** Utility Functions **/
|
|
public function get_inverse_periods( $periods_array ) {
|
|
$inverse_array = array();
|
|
$last_period = null;
|
|
foreach ($periods_array as $key => $period) {
|
|
if ( ! empty( $last_period ) ) {
|
|
$gap_period = $last_period->gap( $period );
|
|
$inverse_array[] = $gap_period;
|
|
}
|
|
|
|
$last_period = $period;
|
|
}
|
|
|
|
return $inverse_array;
|
|
}
|
|
|
|
public function combine_abutting_periods( $periods_array ) {
|
|
ssa_defensive_timezone_fix();
|
|
if ( !is_array( $periods_array ) ) {
|
|
return $periods_array;
|
|
}
|
|
|
|
$combined_periods = array();
|
|
while( count( $periods_array ) ) {
|
|
$next_period = array_shift( $periods_array );
|
|
if ( count( $combined_periods ) === 0 ) {
|
|
$combined_periods[] = $next_period;
|
|
continue;
|
|
}
|
|
|
|
$last_period = current(array_slice($combined_periods, -1));
|
|
if ( $last_period->abuts( $next_period ) ) {
|
|
if ( $last_period->isAfter( $next_period ) ) {
|
|
$tmp_last_period = $last_period;
|
|
$last_period = $next_period;
|
|
$next_period = $tmp_last_period;
|
|
}
|
|
|
|
array_pop( $combined_periods );
|
|
$combined_periods[] = new Period( $last_period->getStartDate(), $next_period->getEndDate() );
|
|
continue;
|
|
}
|
|
|
|
$combined_periods[] = $next_period;
|
|
continue;
|
|
}
|
|
|
|
$combined_periods = array_filter($combined_periods, function($value) {
|
|
ssa_defensive_timezone_reset();
|
|
return $value !== '';
|
|
});
|
|
|
|
ssa_defensive_timezone_reset();
|
|
return $combined_periods;
|
|
}
|
|
|
|
}
|