?
<?php
defined( 'ABSPATH' ) or exit; // Exit if accessed directly
class GA_Calendar {
private $month;
private $year;
private $selected_date;
private $selected_slot;
private $days_of_week = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
private $week_starts;
private $num_days;
private $date_info;
private $day_of_week;
private $time_zone;
private $service_id;
private $provider_id;
private $form_id = 0;
public function __construct( $form_id, $month, $year, $service_id, $provider_id, $selected_date = false, $selected_slot = false ) {
// FORM ID
$this->form_id = $form_id ? $form_id : 0;
// MONTH & DATE
$this->month = $month;
$this->year = $year;
$this->selected_date = $selected_date ? $selected_date : false;
$this->selected_slot = $selected_slot ? $selected_slot : false;
// SERVICE & PROVIDER ID
$this->service_id = (int) $service_id;
$this->provider_id = (int) $provider_id;
// TIMEZONE
$this->time_zone = ga_time_zone();
// DATEINFO
$date = new DateTime();
$date->setTimezone( new DateTimeZone( $this->time_zone ) );
$this->date_info = $date->setDate( (int) $this->year, (int) $this->month, 1 );
$this->num_days = $date->format('t');
$this->day_of_week = $this->date_info->format('w');
// Days of week translated
$this->days_of_week = ga_get_form_translated_data($this->form_id, 'weeks');
// Week starts on
$calendar = get_option('ga_appointments_calendar');
$this->week_starts = isset( $calendar['week_starts'] ) ? $calendar['week_starts'] : 'sunday';
}
/**
* Get Available Days from Schedule
*/
private function get_available_days($array, $timestamp) {
// SERVICE PERIOD TYPE
$period_type = (string) get_post_meta($this->service_id, 'ga_service_period_type', true);
if( $period_type == 'date_range' ) {
$range = (array) get_post_meta($this->service_id, 'ga_service_date_range', true);
$dates = array();
if( isset($range['from']) && ga_valid_date_format($range['from']) && isset($range['to']) && ga_valid_date_format($range['to']) ) {
$period = new DatePeriod(
new DateTime($range['from']),
new DateInterval('P1D'),
new DateTime($range['to'])
);
foreach ($period as $key => $value) {
$dates[] = $value->format('Y-m-j');
}
$dates[] = $range['to'];
}
return $dates;
}
if( $period_type == 'custom_dates' ) {
$custom_dates = (array) get_post_meta($this->service_id, 'ga_service_custom_dates', true);
return $custom_dates;
}
$array = (array) $array;
$weeks = array('sunday' ,'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
$dates = array();
foreach( $array as $week ) {
if( in_array($week, $weeks) ) {
$date = new DateTime();
$date->setTimezone( new DateTimeZone( $this->time_zone ) );
$date->setTimestamp($timestamp);
$date->modify("first $week of this month");
$thisMonth = $date->format('m');
while ($date->format('m') == $thisMonth) {
$dates[] = $date->format('Y-m-j');
$date->modify("next $week");
}
}
}
return $dates;
}
/**
* Get Date Timestamp
*/
private function get_date_timestamp($year, $month, $day = false) {
$date = new DateTime();
$date->setTimezone( new DateTimeZone( $this->time_zone ) );
$day = $day ? $day : 1;
$date->setDate( (int) $year, (int) $month, $day );
return $date->getTimestamp();
}
/**
* Display Calendar
*/
public function show() {
// Calendar caption header
$output = '<div class="ga_appointments_calendar_header">' . PHP_EOL;
// Previous Month
if( $this->previous_month() ) {
$output .= '<a class="arrow-left" date-go="'. $this->date_info->format('Y-m') .'" service_id="'.$this->service_id.'" provider_id="'.$this->provider_id.'" id="ga_calendar_prev_month"><i class="fa fa-caret-left" aria-hidden="true"></i></a>' . PHP_EOL;
}
// Translation: Month/Year Caption
$month = $this->date_info->format('F');
$year = $this->date_info->format('Y');
$lang = ga_get_form_translated_month( $this->form_id, $month, $year );
$output .= '<h3>'. $lang .'</h3>' . PHP_EOL;
// Next Month
if( $this->next_month() ) {
$output .= '<a class="arrow-right" date-go="'. $this->date_info->format('Y-m') .'" service_id="'.$this->service_id.'" provider_id="'.$this->provider_id.'" id="ga_calendar_next_month"><i class="fa fa-caret-right" aria-hidden="true"></i></a>' . PHP_EOL;
}
$output .= '</div>' . PHP_EOL;
// Table start
$output .= '<table class="table_fixed" width="100%">' . PHP_EOL;
$output .= '<thead>' . PHP_EOL;
$output .= '<tr>' . PHP_EOL;
// Week starts on monday
if( $this->week_starts == 'monday' ) {
$sunday = $this->days_of_week['sun'];
unset( $this->days_of_week['sun'] );
$this->days_of_week['sun'] = $sunday;
$this->day_of_week = $this->day_of_week - 1;
if ($this->day_of_week < 0) {
$this->day_of_week = 6;
}
}
// Days of the week header
foreach( $this->days_of_week as $day ) {
$output .= '<th class="ga_header">' . $day . '</th>' . PHP_EOL;
}
// Close header row and open first row of days
$output .= '</tr>' . PHP_EOL;
$output .= '</thead>' . PHP_EOL;
$output .= '<tbody id="service-working-days">' . PHP_EOL;
$output .= '<tr>' . PHP_EOL;
// If first day of a month does not fall on a Sunday, then we need to fill
if( $this->day_of_week > 0 ) {
$output .= str_repeat( '<td class="ga_day_past"></td>', $this->day_of_week );
}
// start num_days counter
$current_day = 1;
// available days
$available_days = $this->get_available_days( array_keys( $this->get_week_day_schedule() ), $this->get_date_timestamp($this->year, $this->month) );
if( $this->month_passed() ) {
$available_days = array();
}
$service_price = $this->service_price(); // false for array
// Loop and build days
while( $current_day <= $this->num_days ) {
// Reset 'days of week' counter and close each row if end of row
$current_date = new DateTime( $this->date_info->format("Y-m-$current_day"), new DateTimeZone( $this->time_zone ) );
$slots_booked = count( $this->get_slots( $current_date ) ) > 0 ? false : true;
$classes = in_array($current_date->format('Y-m-j'), $available_days)
&& !in_array( $current_date->format("Y-m-j"), $this->get_holidays() )
&& !$slots_booked
&& !$this->date_passed($current_date)
&& !$this->max_schedule_days( $current_date )
? 'day_available ga_time_slots' : 'day_unavailable ga_time_slots';
// Date selected from form submission
$selected = '';
if( $this->selected_date && in_array($this->selected_date->format('Y-m-j'), $available_days) ) {
// Add selected class
if( $this->selected_date->format('Y-m-j') == $current_date->format('Y-m-j') ) {
$selected = ' selected';
$classes .= $selected;
}
// Date Slots Mode
if( $this->available_times_mode() == 'no_slots' ) {
# do nothing
} else {
// Time Slots Mode
$placed_it = clone $this->selected_date;
$placed_it->modify("next {$this->week_starts}");
if( $current_date->format('Y-m-j') == $placed_it->format('Y-m-j') && $this->day_of_week = 7 ) {
$output .= '</tr><tr id="gappointments_calendar_slots"><td colspan="7" class="calendar_slots"><div class="calendar_time_slots">';
$output .= $this->calendar_time_slots($this->selected_date, $this->selected_slot);
$output .= '</div></td>';
}
}
}
// Date selected from form submission
// Reset 'days of week' counter and close each row if end of row
if( $this->day_of_week == 7 ) {
$output .= '</tr><tr>' . PHP_EOL;
$this->day_of_week = 0;
}
// Current date is today
if( $this->is_today( $current_date ) ) {
$today = ' ga_today';
$classes .= $today;
} else {
$today = '';
}
// Date Slots Mode
if( $this->available_times_mode() == 'no_slots' ) {
if( $this->is_date_available($current_date) && !$this->max_schedule_days($current_date) ) {
// Translation Support
$month = $current_date->format("F");
$day = $current_date->format("j");
$year = $current_date->format("Y");
$translate = ga_get_form_translated_slots_date( $this->form_id, $month, $day, $year );
$lang_slot = ' lang_slot="'.$translate.'"';
// Translation Support
// Date is available
$classes = "day_available{$today} ga_date_slots{$selected}";
// Multiple Slots selection
$multi_select = (string) get_post_meta($this->service_id, 'ga_service_multiple_selection', true);
$max_bookings = ga_get_service_max_bookings($this->service_id);
$max_total = ga_get_service_max_selection($this->service_id);
$double = ga_get_service_double_bookings($this->service_id);
$multiple = $multi_select == 'yes' ? ' multi-select="enabled" select-total="'.$max_total.'" no_double="'.$double.'"' : '';
// Capacity available
if( $count = $this->date_capacity_text($current_date) ) {
$capacity = $count == 1 ? ga_get_form_translated_space($this->form_id, $count) : ga_get_form_translated_spaces($this->form_id, $count);
$classes .= ' ga_tooltip';
$output .= '<td class="'.$classes.'" ga-tooltip="'.$capacity.'" date-go="'.$this->date_info->format("Y-m-$current_day").'" service_cost="'.$service_price.'" service_id="'.$this->service_id.'" provider_id="'.$this->provider_id.'"'. $multiple . $lang_slot . 'capacity="'.$count.'"><span>'. $current_day .'</span></td>' . PHP_EOL;
} else {
$output .= '<td class="'.$classes.'" date-go="'.$this->date_info->format("Y-m-$current_day").'" service_cost="'.$service_price.'" service_id="'.$this->service_id.'" provider_id="'.$this->provider_id.'"'. $multiple . $lang_slot .' capacity="1"><span>'. $current_day .'</span></td>' . PHP_EOL;
}
} else {
// Date not available
$classes = "day_unavailable{$today} ga_date_slots";
$output .= '<td class="'.$classes.'" date-go="'.$this->date_info->format("Y-m-$current_day").'" service_cost="'.$service_price.'"><span>'. $current_day .'</span></td>' . PHP_EOL;
}
} else {
// Time Slots Mode
$output .= '<td class="'.$classes.'" date-go="'.$this->date_info->format("Y-m-$current_day").'" service_id="'.$this->service_id.'" provider_id="'.$this->provider_id.'" service_cost="'.$service_price.'"><span>'. $current_day .'</span></td>' . PHP_EOL;
}
// Increment counters
$current_day++;
$this->day_of_week++;
}
// Once num_days counter stops, if day of week counter is not 7, then we
// need to fill remaining space on the row using colspan
if( $this->day_of_week != 7 ) {
$remaining_days = 7 - $this->day_of_week;
$output .= str_repeat( '<td class="ga_day_future"></td>', $remaining_days );
}
// Date selected from form submission
// Place at the end
if( $this->available_times_mode() == 'no_slots' ) {
# do nothing
} else {
if( $this->selected_date && in_array($this->selected_date->format('Y-m-j'), $available_days) ) {
// Last day of month from selected date
$month_end = clone $this->selected_date;
$month_end->modify("last day of this month");
// Next sunday from selected date
$placed_end = clone $this->selected_date;
$placed_end->modify("next {$this->week_starts}");
//var_dump( $placed_end > $month_end );
if( $placed_end > $month_end ) {
$output .= '</tr><tr id="gappointments_calendar_slots"><td colspan="7" class="calendar_slots"><div class="calendar_time_slots">';
$output .= $this->calendar_time_slots($this->selected_date, $this->selected_slot);
$output .= '</div></td>';
}
}
}
// Date selected from form submission
// Close final row and table
$output .= '</tr>' . PHP_EOL;
$output .= '</tbody>' . PHP_EOL;
$output .= '</table>' . PHP_EOL;
return $output;
//echo $output;
}
/**
* Slot Available Query
*/
public function slot_availability_query( $dateTime, $slot ) {
$slot_start = $slot['start'];
if( $this->schedule_lead_time( $dateTime, $slot_start ) ) {
# valid time & date
} else {
return false;
}
$date = $dateTime->format("Y-m-j");
$slot_end = ga_get_time_end($slot_start, $this->service_id);
global $wpdb;
$querystr = "
SELECT $wpdb->posts.ID
FROM
$wpdb->posts,
$wpdb->postmeta AS app_date,
$wpdb->postmeta AS provider,
$wpdb->postmeta AS time1,
$wpdb->postmeta AS time2
WHERE
$wpdb->posts.ID = app_date.post_id
AND
$wpdb->posts.ID = provider.post_id
AND
$wpdb->posts.ID = time1.post_id
AND
$wpdb->posts.ID = time2.post_id
AND $wpdb->posts.post_type = 'ga_appointments'
AND $wpdb->posts.post_status IN ('completed', 'publish', 'payment', 'pending')
AND app_date.meta_key = 'ga_appointment_date'
AND app_date.meta_value = %s
AND provider.meta_key = 'ga_appointment_provider'
AND provider.meta_value = %s
AND time1.meta_key = 'ga_appointment_time_end'
AND time1.meta_value > %s
AND time2.meta_key = 'ga_appointment_time'
AND time2.meta_value < %s
";
$sql_prepare = $wpdb->prepare($querystr, $date, $this->provider_id, $slot_start, $slot_end);
$appointments = $wpdb->get_results( $sql_prepare, ARRAY_A );
//var_dump( count($appointments) );
return count($appointments);
}
/**
* Is slot available
*/
public function is_slot_available( $dateTime, $slot ) {
$appointments = $this->slot_availability_query( $dateTime, $slot );
if( $appointments === false ) {
return false;
}
if( $appointments == 0 ) {
return true;
}
if( $this->available_times_mode() == 'custom' ) {
$capacity = $slot['capacity'];
} else {
$capacity = $this->service_capacity();
}
if ( $appointments >= $capacity ) {
return false;
}
return true;
}
/**
* Capacity Available Text
*/
public function slot_capacity_text( $dateTime, $slot ) {
$appointments = $this->slot_availability_query( $dateTime, $slot );
if( $appointments === false ) {
return false;
}
if( $this->available_times_mode() == 'custom' ) {
$capacity = $slot['capacity'];
} else {
$capacity = $this->service_capacity();
}
if( $appointments > 0 && $appointments < $capacity ) {
return ($capacity - $appointments);
} elseif( $appointments == 0 && $capacity > 1 ) {
return $capacity;
}
return false;
}
/**
* Date Available Query
*/
public function date_availability_query( $dateTime ) {
if( $this->schedule_lead_time_days( $dateTime ) ) {
# valid date
} else {
return false;
}
$date = $dateTime->format("Y-m-j");
$appointments = new WP_QUERY(
array(
'post_type' => 'ga_appointments',
'posts_per_page' => -1,
'post_status' => array( 'completed', 'publish', 'payment', 'pending' ),
'orderby' => 'meta_value',
'order' => 'ASC',
'meta_query' => array( 'relation' => 'AND',
array( 'key' => 'ga_appointment_date', 'value' => $date ),
array( 'key' => 'ga_appointment_provider', 'value' => $this->provider_id ),
),
)
);
return $appointments;
}
/**
* Date Available
*/
public function is_date_available( $dateTime ) {
// Week day shedule is not out
$week_day = (string) strtolower( $dateTime->format('l') );
$schedule = $this->get_week_day_schedule();
if( isset($schedule[$week_day]) ) {
$schedule = $schedule[$week_day];
} else {
return false;
}
$appointments = $this->date_availability_query( $dateTime );
if( !$appointments || $this->date_slot_passed($dateTime) ) {
return false;
}
$available_days = $this->get_available_days( array_keys( $this->get_week_day_schedule() ), $this->get_date_timestamp($this->year, $this->month) );
if( $this->month_passed() ) {
$available_days = array();
}
if( in_array($dateTime->format('Y-m-j'), $available_days) && !in_array($dateTime->format("Y-m-j"), $this->get_holidays()) ) {
# valid
} else {
return false;
}
$capacity = $this->service_capacity();
if ( $appointments->have_posts() && $capacity <= $appointments->post_count ) {
return false;
}
return true;
}
/**
* Date Capacity Text
*/
public function date_capacity_text( $dateTime ) {
$appointments = $this->date_availability_query( $dateTime );
if( !$appointments ) {
return false;
}
$capacity = $this->service_capacity();
if( $appointments->post_count > 0 && $appointments->post_count < $capacity ) {
return ($capacity - $appointments->post_count);
} elseif( $appointments->post_count == 0 && $capacity > 1 ) {
return $capacity;
}
return false;
}
/**
* New appointment lead time require for new appointments
* @ returns true if valid
*/
public function schedule_lead_time( $dateTime, $slot_start ) {
// Lead time minutes
$service_lead_time = get_post_meta($this->service_id, 'ga_service_schedule_lead_time_minutes', true);
$lead_time = $service_lead_time && array_key_exists( $service_lead_time, ga_schedule_lead_time_minutes() ) ? $service_lead_time : '240';
// Today's date
$today = ga_current_date_with_timezone();
//var_dump( $lead_time );
// Slot time with date
$slot_time = new DateTime( $slot_start, new DateTimeZone( $this->time_zone ) );
$slot_time->setDate( $dateTime->format("Y"), $dateTime->format("m"), $dateTime->format("j") );
// If no lead time, is slot dateTime less or equal then today
if( $lead_time == 'no' ) {
if( $slot_time <= $today ) {
return false;
}
return true;
}
$lead = $today;
$lead = $lead->modify("+{$lead_time} minutes");
//print_r( "Start: {$slot_time->format('Y-m-j H:i')} | End: {$lead->format('Y-m-j H:i')}" . '<br>');
if( $slot_time <= $lead ) {
//echo "Not valid: {$slot_time->format('Y-m-j H:i')} | Lead end: {$lead->format('Y-m-j H:i')}<br>";
return false;
}
return true;
}
/**
* New appointment lead time require for bookable dates
* @ returns true if valid
*/
public function schedule_lead_time_days( $dateTime ) {
// Lead time minutes
$service_lead_time = get_post_meta($this->service_id, 'ga_service_schedule_lead_time_minutes', true);
$lead_time = $service_lead_time && array_key_exists( $service_lead_time, ga_schedule_lead_time_minutes() ) ? $service_lead_time : '240';
// Today's date
$today = ga_current_date_with_timezone();
//var_dump( $lead_time );
// If no lead time, is slot dateTime less or equal then today
if( $lead_time == 'no' ) {
$dateTime->setTime(23, 59);
if( $dateTime <= $today ) {
return false;
}
return true;
}
$lead = $today;
$lead = $lead->modify("+{$lead_time} minutes");
if( $dateTime <= $lead ) {
//echo "Not valid: {$slot_time->format('Y-m-j H:i')} | Lead end: {$lead->format('Y-m-j H:i')}<br>";
return false;
}
return true;
}
/**
* Display Slots
*/
public function get_slots( $date ) {
switch ( $this->available_times_mode() ) {
case 'interval':
return $this->generate_time_slots( $date );
case 'custom':
return $this->get_custom_slots( $date );
case 'no_slots':
return array();
default:
return array();
}
}
/**
* Get Date Schedule
*/
public function get_date_schedule( $date ) {
$week_day = (string) strtolower( $date->format('l') );
$schedule = $this->get_week_day_schedule();
$available_days = $this->get_available_days( array_keys( $this->get_week_day_schedule() ), $this->get_date_timestamp($this->year, $this->month) );
if( !in_array($date->format('Y-m-j'), $available_days) || $this->date_passed($date) || $this->max_schedule_days($date) ) {
return false;
}
if( isset($schedule[$week_day]) ) {
return $schedule[$week_day];
} else {
return false;
}
}
/**
* Custom Time Slots
*/
public function get_custom_slots( $date ) {
$week_day = (string) strtolower( $date->format('l') );
if( !is_array( $this->custom_slots() ) || $this->get_date_schedule( $date ) === false ) {
return array();
}
$slots = array();
foreach( $this->custom_slots() as $slot_id => $slot ) {
if( isset($slot['availability']) && in_array($week_day, $slot['availability']) ) {
if( $this->is_slot_available( $date, $slot ) ) {
$startTime = new DateTime( $slot['start'] );
$endTime = new DateTime( $slot['end'] );
$slots[$slot['start']] = $slot;
$slots[$slot['start']]['text'] = $this->slot_text( $startTime, $endTime );
$slots[$slot['start']]['duration'] = $this->get_slot_duration( $startTime, $endTime );
}
//echo '<pre>'; print_r( $slots ); echo '</pre>';
//die();
}
}
//echo '<pre>'; print_r( $slots ); echo '</pre>';
return $slots;
}
public function get_slot_duration( $startTime, $endTime ) {
$diff = $startTime->diff( $endTime );
$minutes = ($diff->days * 24 * 60) + ($diff->h * 60) + $diff->i;
return $minutes;
}
/**
* Generate Time Slots
*/
public function generate_time_slots( $date ) {
$week_day = (string) strtolower( $date->format('l') );
if( $this->get_date_schedule( $date ) === false ) {
return array();
} else {
$schedule = $this->get_date_schedule( $date );
}
$duration = (int) get_post_meta($this->service_id, 'ga_service_duration', true);
$cleanup = (int) get_post_meta($this->service_id, 'ga_service_cleanup', true);
$start = (string) $schedule['begin'];
$end = (string) $schedule['end'];
$start = new DateTime($start);
$end = new DateTime($end);
$interval = new DateInterval("PT" . $duration . "M");
$cleanupInterval = new DateInterval("PT" . $cleanup . "M");
$slots = array();
$exclude_slots = array();
for ($intStart = $start; $intStart < $end; $intStart->add($interval)->add($cleanupInterval)) {
$endPeriod = clone $intStart;
$endPeriod->add($interval);
if( $endPeriod > $end ) {
break;
}
if( $this->get_breaks( $week_day ) ) {
foreach( $this->get_breaks( $week_day ) as $break_start => $break_end ) {
$break_start = new DateTime( $break_start );
$break_end = new DateTime( $break_end );
if( $intStart >= $break_end || $endPeriod <= $break_start ) {
// Slot available
if( $this->is_slot_available( $date, $this->generate_slot_data($intStart, $endPeriod) ) ) {
$slots[$intStart->format('H:i')] = $this->generate_slot_data($intStart, $endPeriod);
}
} else {
$exclude_slots[$intStart->format('H:i')] = $this->generate_slot_data($intStart, $endPeriod);
if( $this->reduce_gaps() ) {
$intStart = $break_end;
$resetInterval = new DateInterval("PT0M");
$intStart->add($resetInterval)->add($cleanupInterval);
$endPeriod = clone $intStart;
$endPeriod->add($interval);
// Reset interval greater than end time
if( $endPeriod > $end ) {
break;
}
// Slot available
if( $this->is_slot_available($date, $this->generate_slot_data($intStart, $endPeriod)) ) {
$slots[$intStart->format('H:i')] = $this->generate_slot_data($intStart, $endPeriod);
}
}
}
} // endforeach
} else {
// Slot available
if( $this->is_slot_available($date, $this->generate_slot_data($intStart, $endPeriod)) ) {
$slots[$intStart->format('H:i')] = $this->generate_slot_data($intStart, $endPeriod);
}
}
}
//echo '<pre>'; print_r( $slots ); echo '</pre>';
//$slots = array_unique($slots);
//$exclude_slots = array_unique($exclude_slots);
return array_diff_key($slots, $exclude_slots);
}
private function generate_slot_data( $start, $end ) {
$slot = array();
$slot['start'] = $start->format('H:i');
$slot['end'] = $end->format('H:i');
$slot['text'] = $this->slot_text($start, $end);
return $slot;
}
/**
* Schedule Available Week Days
*/
public function get_week_day_schedule() {
$provider_schedule = $this->get_calendar_schedule();
if( $this->provider_id == 0 ) {
# do nothing
} else {
$provider_calendar = (string) get_post_meta( $this->provider_id, 'ga_provider_calendar', true );
if( $provider_calendar == 'on' ) {
$provider_schedule = (array) get_post_meta( $this->provider_id, 'ga_provider_work_schedule', true );
}
}
foreach( $provider_schedule as $key => $schedule ) {
if( $schedule['begin'] == 'out' ) {
unset( $provider_schedule[$key] );
}
}
return $provider_schedule;
}
/**
* Schedule Available Breaks
*/
private function get_breaks( $week_day ) {
$sort = array();
$breaks = $this->get_calendar_breaks();
if( $this->provider_id == 0 ) {
# do nothing
} else {
$provider_calendar = (string) get_post_meta( $this->provider_id, 'ga_provider_calendar', true );
if( $provider_calendar == 'on' ) {
$breaks = (array) get_post_meta( $this->provider_id, 'ga_provider_breaks', true );
}
}
if( isset( $breaks[$week_day] ) && count($breaks[$week_day]) > 0 ) {
$breaks = $breaks[$week_day];
} else {
return array();
}
foreach($breaks as $key => $part) {
$sort[$key] = new DateTime($part);
}
array_multisort($sort, SORT_ASC, $breaks);
//$print = '<pre>' . print_r($breaks, true);
//wp_die( $print );
return $breaks;
}
/**
* Holidays
*/
public function get_holidays() {
$provider_holidays = $this->get_calendar_holidays();
if( $this->provider_id == 0 ) {
# do nothing
} else {
$provider_calendar = (string) get_post_meta( $this->provider_id, 'ga_provider_calendar', true );
if( $provider_calendar == 'on' ) {
$provider_holidays = (array) get_post_meta( $this->provider_id, 'ga_provider_holidays', true );
}
}
return $provider_holidays;
}
private function get_calendar_schedule() {
$options = get_option( 'ga_appointments_work_schedule' );
$work_schedule = $options && is_array($options) ? $options : $this->get_calendar_defauls();
return $work_schedule;
}
private function get_calendar_breaks() {
$options = get_option( 'ga_appointments_schedule_breaks' );
$breaks = (array) $options;
return $breaks;
}
private function get_calendar_holidays() {
$options = get_option( 'ga_appointments_holidays' );
$holidays = (array) $options;
return $holidays;
}
/**
* Default Schedule
*/
private function get_calendar_defauls() {
$schedule = array( 'sunday' => array('begin' => 'out', 'end' => 'out'),
'monday' => array('begin' => '09:00', 'end' => '17:00'),
'tuesday' => array('begin' => '09:00', 'end' => '17:00'),
'wednesday' => array('begin' => '09:00', 'end' => '17:00'),
'thursday' => array('begin' => '09:00', 'end' => '17:00'),
'friday' => array('begin' => 'out', 'end' => 'out'),
'saturday' => array('begin' => 'out', 'end' => 'out'),
);
return $schedule;
}
/**
* Reduce gaps
*/
private function reduce_gaps() {
$reduce_gaps = get_post_meta( $this->service_id, 'ga_service_reduce_gaps', true );
// Reduce Gaps
if( $reduce_gaps && in_array($reduce_gaps, array('yes', 'no')) ) {
$gaps = $reduce_gaps;
} else {
$gaps = 'yes'; // days
}
if( $gaps == 'yes' ) {
return true;
}
return false;
}
/**
* Prior Days To Book Appointment
*/
private function max_schedule_days( $date_input ) {
$max_days = get_post_meta( $this->service_id, 'ga_service_schedule_max_future_days', true );
$max_days = $max_days && array_key_exists( $max_days, ga_schedule_max_future_days() ) ? $max_days : 90;
// SERVICE PERIOD TYPE
$period_type = (string) get_post_meta($this->service_id, 'ga_service_period_type', true);
if( $period_type == 'date_range' ) {
$range = (array) get_post_meta($this->service_id, 'ga_service_date_range', true);
if( isset($range['from']) && ga_valid_date_format($range['from']) && isset($range['to']) && ga_valid_date_format($range['to']) ) {
$end_range = new DateTime($range['to'], new DateTimeZone( $this->time_zone ));
$end_range->setTime(24,00);
//print_r( $end_range );
return $date_input > $end_range;
}
return true;
}
if( $period_type == 'custom_dates' ) {
$custom_dates = (array) get_post_meta($this->service_id, 'ga_service_custom_dates', true);
if( is_array($custom_dates) && count($custom_dates) > 0 && ga_valid_date_format(end($custom_dates)) ) {
$end_custom_date = new DateTime(end($custom_dates), new DateTimeZone( $this->time_zone ));
$end_custom_date->setTime(24,00);
return $date_input > $end_custom_date;
}
return true;
}
// Future Days Period
$date = new DateTime();
$date->setTimezone( new DateTimeZone( $this->time_zone ) );
$date->add(new DateInterval( "P" . $max_days . "D" ));
$date->setTime(00, 00);
$calendar = $date_input; // DateTime Object
$calendar->setTimezone( new DateTimeZone( $this->time_zone ) );
$calendar->setTime(00, 00);
return $calendar > $date;
}
public function calendar_time_slots($date, $sel_slot = false) {
$ga_slots = $this->get_slots( $date );
// Translation Support
$month = $date->format("F");
$week = $date->format("l");
$day = $date->format("j");
$year = $date->format("Y");
$text = ga_get_form_translated_slots_date($this->form_id, $month, $day, $year);
// Translation Support
// Slot size
$slots_size = $this->show_end_times() ? 'slot_large grid-lg-12 grid-md-12 grid-sm-12 grid-xs-12' : 'slot_small grid-lg-3 grid-md-3 grid-sm-3 grid-xs-6';
// Time Format Display
$time_display = ga_service_time_format_display($this->service_id);
// Multiple Slots selection
$multi_select = (string) get_post_meta($this->service_id, 'ga_service_multiple_selection', true);
$max_bookings = ga_get_service_max_bookings($this->service_id);
$max_total = ga_get_service_max_selection($this->service_id);
$double = ga_get_service_double_bookings($this->service_id);
$time_format = (string) get_post_meta($this->service_id, 'ga_service_time_format', true);
$remove_am_pm = (string) get_post_meta($this->service_id, 'ga_service_remove_am_pm', true);
$multiple = $multi_select == 'yes' ? ' multi-select="enabled" select-max="'.$max_bookings.'" select-total="'.$max_total.'" time_format="'.$time_format.'" remove_am_pm="'.$remove_am_pm.'" no_double="'.$double.'"' : '';
$out = '';
if( count($ga_slots) > 0 ) {
$out .= '<h3 class="slots-title">' .$text. '</h3>'; // eg. February 24, 2018
$out .= '<div class="grid-row grid_no_pad">';
foreach( $ga_slots as $slot ) {
$sel_class = $sel_slot && $sel_slot == $slot['start'] ? ' time_selected' : '';
$slot_cost = $this->available_times_mode() == 'custom' ? $slot['price'] : $this->service_price();
// Slot Language
if( $multi_select == 'yes' ) {
$time = new DateTime($slot['start'], new DateTimeZone( $this->time_zone ));
$slot_time = $time->format($time_display);
$translate = ga_get_form_translated_date_time( $this->form_id, $month, $week, $day, $year, $slot_time );
$lang_slot = ' lang_slot="'. esc_html($translate) .'"';
} else {
$lang_slot = '';
}
// Slot Language
if( $this->slot_capacity_text( $date, $slot ) ) {
$count = $this->slot_capacity_text( $date, $slot );
$capacity = $count == 1 ? ga_get_form_translated_space($this->form_id, $count) : ga_get_form_translated_spaces($this->form_id, $count);
$out .= '<div class="'.$slots_size.' grid_no_pad">
<label class="time_slot ga_tooltip'.$sel_class.'" time_slot="'.$slot['start'].'" ga-tooltip="'.$capacity.'"'.$multiple . $lang_slot.' capacity="'.$count.'" service_id="'.$this->service_id.'" slot_cost="'.$slot_cost.'"><div>'.$slot['text'].'</div></label>
</div>';
} else {
$out .= '<div class="'.$slots_size.' grid_no_pad">
<label class="time_slot'.$sel_class.'" time_slot="'.$slot['start'].'" '.$multiple . $lang_slot.' capacity="1" service_id="'.$this->service_id.'" slot_cost="'.$slot_cost.'"><div>'.$slot['text'].'</div></label>
</div>';
}
}
$out .= '</div>';
} else {
$out .= '<div id="no_time_slots"><i class="fa fa-calendar-times-o" aria-hidden="true"></i><div>No time slots</div></div>';
}
return $out;
}
/**
* Slot Text
*/
private function slot_text($intStart, $endPeriod) {
$remove_am_pm = $this->remove_am_pm() == 'no' ? 'A' : '';
$time_format = $this->time_format() == '24h' ? "G:i {$remove_am_pm}" : "g:i {$remove_am_pm}";
$start = ga_get_form_translated_am_pm($this->form_id, $intStart->format($time_format));
$end = ga_get_form_translated_am_pm($this->form_id, $endPeriod->format($time_format));
if( $this->show_end_times() ) {
return $start . ' - ' . $end;
} else {
return $start;
}
}
/**
* Show End Times
*/
public function show_end_times() {
$show_end_times = get_post_meta( $this->service_id, 'ga_service_show_end_times', true );
// Show End Times
if( $show_end_times && in_array($show_end_times, array('yes', 'no')) ) {
$end_times = $show_end_times;
} else {
$end_times = 'no'; // days
}
if( $end_times == 'yes' ) {
return true;
}
return false;
}
/**
* Time Format
*/
public function time_format() {
$time_format = get_post_meta( $this->service_id, 'ga_service_time_format', true );
// Time Format
if( $time_format && in_array($time_format, array('12h', '24h')) ) {
$format = $time_format;
} else {
$format = '12h'; // 12h format
}
return $format;
}
/**
* Time Format
*/
public function remove_am_pm() {
$remove_am_pm = get_post_meta( $this->service_id, 'ga_service_remove_am_pm', true );
// Time Format
if( $remove_am_pm && in_array($remove_am_pm, array('no', 'yes')) ) {
$remove = $remove_am_pm;
} else {
$remove = 'no'; // 12h format
}
return $remove;
}
/**
* Current Date Today
*/
private function is_today( $current_date ) {
$today = ga_current_date_with_timezone();
return $today->format('Y-m-j') == $current_date->format('Y-m-j');
}
/**
* Date Passed
*/
private function date_passed($dateTime) {
$day = $dateTime->format('j');
$now = ga_current_date_with_timezone();
$calendar = new DateTime();
$calendar->setTimezone( new DateTimeZone( $this->time_zone ) );
$calendar->setDate( (int) $this->year, (int) $this->month, $day );
return $now > $calendar;
}
/**
* Date Slot Passed
*/
private function date_slot_passed($dateTime) {
$now = ga_current_date_with_timezone();
return $now > $dateTime;
}
/**
* Month Passed
*/
private function month_passed() {
$month = new DateTime();
$month->setTimezone( new DateTimeZone( $this->time_zone ) );
$month->setDate( $month->format('Y'), $month->format('n'), 1 );
$now = ga_current_date_with_timezone();;
return $month > $now;
}
/**
* Previous Month
*/
private function previous_month() {
$previous = new DateTime();
$previous->setTimezone( new DateTimeZone( $this->time_zone ) );
$previous->setDate( $this->date_info->format('Y'), $this->date_info->format('n'), 1 );
$previous->modify( 'last day of previous month' );
// SERVICE PERIOD TYPE
$period_type = (string) get_post_meta($this->service_id, 'ga_service_period_type', true);
if( $period_type == 'date_range' ) {
$range = (array) get_post_meta($this->service_id, 'ga_service_date_range', true);
if( isset($range['from']) && ga_valid_date_format($range['from']) && isset($range['to']) && ga_valid_date_format($range['to']) ) {
$begin_range = new DateTime($range['from'], new DateTimeZone( $this->time_zone ));
return $previous > $begin_range;
}
return false;
}
if( $period_type == 'custom_dates' ) {
$custom_dates = (array) get_post_meta($this->service_id, 'ga_service_custom_dates', true);
if( is_array($custom_dates) && count($custom_dates) > 0 && ga_valid_date_format(reset($custom_dates)) ) {
$begin_custom_date = new DateTime(reset($custom_dates), new DateTimeZone( $this->time_zone ));
return $previous > $begin_custom_date;
}
return false;
}
return $previous >= ga_current_date_with_timezone();
}
/**
* Next Month
*/
private function next_month() {
$next = new DateTime();
$next->setTimezone( new DateTimeZone( $this->time_zone ) );
$next->setDate( $this->date_info->format('Y'), $this->date_info->format('n'), 1 );
$next->modify( 'first day of next month' );
if( $this->max_schedule_days($next) ) {
return false;
}
return true;
}
/**
* Month future
*/
private function month_future() {
$now = new DateTime();
$now->setTimezone( new DateTimeZone( $this->time_zone ) );
$now->setDate( $now->format('Y'), $now->format('n'), 1 );
return $now > $this->date_info;
}
private function available_times_mode() {
return (string) get_post_meta( $this->service_id, 'ga_service_available_times_mode', true );
}
private function service_capacity() {
return (int) get_post_meta( $this->service_id, 'ga_service_capacity', true );
}
private function custom_slots() {
return get_post_meta( $this->service_id, 'ga_service_custom_slots', true );
}
private function service_price() {
return get_post_meta( $this->service_id, 'ga_service_price', true );
}
} // end class