public function __construct() { global $PHP_SELF; if (is_admin() && (strpos($PHP_SELF, 'post-new.php') !== false || strpos($PHP_SELF, 'post.php') !== false)) { wp_enqueue_script('backbone'); wp_enqueue_script('underscore'); add_action('admin_footer', array(&$this, 'renderPopup')); add_filter('media_buttons', array(&$this, 'addButton'), 50); $configuration = new AB_BookingConfiguration(); $this->vars['categoriesJson'] = $configuration->getCategoriesJson(); $this->vars['staffJson'] = $configuration->getStaffJson(); $this->vars['servicesJson'] = $configuration->getServicesJson(); } }
/** * Constructor. * * @param array $options */ public function __construct(array $options = array()) { // Handle widget options. $options = array_merge(array('use_empty' => true, 'empty_value' => null, 'exclude_last_slot' => false), $options); // Insert empty value if required. if ($options['use_empty']) { $this->values[null] = $options['empty_value']; } $ts_length = AB_BookingConfiguration::getTimeSlotLength(); $time_start = AB_StaffScheduleItem::WORKING_START_TIME; $time_end = AB_StaffScheduleItem::WORKING_END_TIME; if ($options['exclude_last_slot']) { $time_end -= $ts_length; } // Run the loop. while ($time_start <= $time_end) { $this->values[AB_DateTimeUtils::buildTimeString($time_start)] = AB_DateTimeUtils::formatTime($time_start); $time_start += $ts_length; } }
/** * @param string $field_name * @param bool $is_start * @return string */ public function renderField($field_name = 'ab_settings_monday', $is_start = true) { $ts_length = AB_BookingConfiguration::getTimeSlotLength(); $time_output = AB_StaffScheduleItem::WORKING_START_TIME; $time_end = AB_StaffScheduleItem::WORKING_END_TIME; $option_name = $field_name . ($is_start ? '_start' : '_end'); $class_name = $is_start ? 'select_start' : 'select_end'; $selected_value = get_option($option_name); $output = "<select class='form-control ab-auto-w {$class_name}' name={$option_name}>"; if ($is_start) { $output .= "<option value=''>" . __('OFF', 'bookly') . "</option>"; $time_end -= $ts_length; } while ($time_output <= $time_end) { $value = AB_DateTimeUtils::buildTimeString($time_output, false); $op_name = AB_DateTimeUtils::formatTime($time_output); $output .= "<option value='{$value}'" . selected($value, $selected_value, false) . ">{$op_name}</option>"; $time_output += $ts_length; } $output .= '</select>'; return $output; }
/** * Contructor. * * @param $form_id */ public function __construct($form_id) { $this->form_id = $form_id; // Set up default parameters. $prior_time = AB_BookingConfiguration::getMinimumTimePriorBooking(); $this->set('date_from', date('Y-m-d', current_time('timestamp') + $prior_time)); $schedule_item = AB_StaffScheduleItem::query('ssi')->select('SUBSTRING_INDEX(MIN(ssi.start_time), ":", 2) AS min_end_time')->whereNot('start_time', null)->fetchArray(); $this->set('time_from', $schedule_item[0]['min_end_time']); $schedule_item = AB_StaffScheduleItem::query('ssi')->select('SUBSTRING_INDEX(MAX(end_time), ":", 2) AS max_end_time')->whereNot('start_time', null)->fetchArray(); $this->set('time_to', $schedule_item[0]['max_end_time']); // If logged in then set name, email and if existing customer then also phone. $current_user = wp_get_current_user(); if ($current_user && $current_user->ID) { $customer = new AB_Customer(); if ($customer->loadBy(array('wp_user_id' => $current_user->ID))) { $this->set('name', $customer->get('name')); $this->set('email', $customer->get('email')); $this->set('phone', $customer->get('phone')); } else { $this->set('name', $current_user->display_name); $this->set('email', $current_user->user_email); } } }
/** * Render progress tracker into a variable. * * @param int $booking_step * @param int|bool $price */ private function _prepareProgressTracker($booking_step, $price = false) { if (get_option('ab_appearance_show_progress_tracker') == 1) { $payment_disabled = AB_BookingConfiguration::isPaymentDisabled() || $price !== false && $price <= 0; $tracker = $this->render('_progress_tracker', array('booking_step' => $booking_step, 'payment_disabled' => $payment_disabled), false); } else { $tracker = ''; } $this->progress_tracker = $tracker; }
?> , attributes : <?php echo $attributes; ?> , form_id : <?php echo json_encode($form_id); ?> , start_of_week : <?php echo (int) get_option('start_of_week'); ?> , date_min : <?php echo json_encode(AB_BookingConfiguration::getDateMin()); ?> , date_format : <?php echo json_encode(AB_DateTimeUtils::convertFormat('date', AB_DateTimeUtils::FORMAT_PICKADATE)); ?> , custom_fields : <?php echo get_option('ab_custom_fields'); ?> , show_calendar : <?php echo intval(get_option('ab_appearance_show_calendar')); ?> , woocommerce : <?php
/** * Render progress tracker into a variable. * * @param int $booking_step * @param int|null $price */ private function _prepareProgressTracker($booking_step, $price = null) { $payment_disabled = AB_BookingConfiguration::isPaymentDisabled() || $price !== null && $price <= 0; $this->progress_tracker = $this->render('_progress_tracker', array('booking_step' => $booking_step, 'payment_disabled' => $payment_disabled), false); }
/** * Find array of time slots available for booking * for given date. * * @access private * @param DateTime $date * @return array */ private function _findAvailableTime(DateTime $date) { $result = array(); $time_slot_length = AB_BookingConfiguration::getTimeSlotLength(); $prior_time = AB_BookingConfiguration::getMinimumTimePriorBooking(); $show_blocked_slots = AB_BookingConfiguration::showBlockedTimeSlots(); $current_timestamp = (int) current_time('timestamp') + $prior_time; $current_date = date_create('@' . $current_timestamp)->setTime(0, 0); if ($date < $current_date) { return array(); } $day_of_week = $date->format('w') + 1; // 1-7 $start_time = date('H:i:s', ceil($current_timestamp / $time_slot_length) * $time_slot_length); foreach ($this->staffData as $staff_id => $staff) { if ($staff['capacity'] < $this->userData->get('number_of_persons')) { continue; } if (isset($staff['working_hours'][$day_of_week]) && $this->isWorkingDay($date, $staff_id)) { if ($this->isWholeDayService()) { // For whole day services do not check staff working hours. $intersections = array(array('start' => 0, 'end' => 86400)); } else { // Find intersection between working and requested hours //(excluding time slots in the past). $working_start_time = $date == $current_date && $start_time > $staff['working_hours'][$day_of_week]['start_time'] ? $start_time : $staff['working_hours'][$day_of_week]['start_time']; $intersections = $this->_findIntersections($this->_timeToSecs($working_start_time), $this->_timeToSecs($staff['working_hours'][$day_of_week]['end_time']), $this->_timeToSecs($this->userData->get('time_from')), $this->_timeToSecs($this->userData->get('time_to'))); } foreach ($intersections as $intersection) { if ($intersection['end'] - $intersection['start'] >= $this->service_duration) { // Initialize time frames. $frames = array(array('start' => $intersection['start'], 'end' => $intersection['end'], 'staff_id' => $staff_id)); if (!$this->isWholeDayService()) { // Remove breaks from time frames for non whole day services only. foreach ($staff['working_hours'][$day_of_week]['breaks'] as $break) { $frames = $this->_removeTimePeriod($frames, $this->_timeToSecs($break['start']), $this->_timeToSecs($break['end'])); } } // Remove bookings from time frames. foreach ($staff['bookings'] as $booking) { // Work with bookings for the current day only. if ($date->format('Y-m-d') == substr($booking['start_date'], 0, 10)) { $booking_start = $this->_timeToSecs(substr($booking['start_date'], 11)); $booking_end = $this->_timeToSecs(substr($booking['end_date'], 11)); $frames = $this->_removeTimePeriod($frames, $booking_start, $booking_end, $removed); if ($removed) { // Handle not full bookings (when number of bookings is less than capacity). if ($booking['from_google'] == false && $booking['service_id'] == $this->userData->get('service_id') && $booking_start >= $intersection['start'] && $staff['capacity'] - $booking['number_of_bookings'] >= $this->userData->get('number_of_persons')) { // Show only the first slot as available. $frames[] = array('start' => $booking_start, 'end' => $booking_start + $time_slot_length, 'staff_id' => $staff_id, 'not_full' => true); if ($this->isWholeDayService()) { // For whole day services there can be just one not full appointment // for a day, thus we break the loop and do not add 'booked' slots. break; } if ($show_blocked_slots) { // When displaying blocked slots then the rest must be shown as blocked. $frames[] = array('start' => $booking_start + $time_slot_length, 'end' => $booking_end <= $intersection['end'] ? $booking_end : $intersection['end'], 'staff_id' => $staff_id, 'booked' => true); } } else { if ($show_blocked_slots) { // Show removed slots as blocked. if ($this->isWholeDayService()) { $frames[] = array('start' => 0, 'end' => $time_slot_length, 'staff_id' => $staff_id, 'booked' => true); // For whole day services we break the loop since the day is not available // and we do not need more 'booked' slots. break; } else { $frames[] = array('start' => $booking_start >= $intersection['start'] ? $booking_start : $intersection['start'], 'end' => $booking_end <= $intersection['end'] ? $booking_end : $intersection['end'], 'staff_id' => $staff_id, 'booked' => true); } } } } } } $result = array_merge($result, $frames); } } } } usort($result, function ($a, $b) { return $a['start'] - $b['start']; }); return $result; }
/** * Returns a collection of Google calendar events * * @param DateTime $startDate * @return array|false */ public function getCalendarEvents(DateTime $startDate) { // get all events from calendar, without timeMin filter (the end of the event can be later then the start of searched time period) $result = array(); try { $calendar_access = $this->getCalendarAccess(); $time_slot_length = AB_BookingConfiguration::getTimeSlotLength(); $limit_events = get_option('ab_settings_google_limit_events'); $timeMin = clone $startDate; $timeMin = $timeMin->modify('-1 day')->format(DateTime::RFC3339); $events = $this->service->events->listEvents($this->getCalendarID(), array('singleEvents' => true, 'orderBy' => 'startTime', 'timeMin' => $timeMin, 'maxResults' => $limit_events ?: self::EVENTS_PER_REQUEST)); while (true) { foreach ($events->getItems() as $event) { /** @var Google_Service_Calendar_Event $event */ if ($event->getStatus() !== 'cancelled') { // Skip events created by Bookly in non freeBusyReader calendar. if ($calendar_access != 'freeBusyReader') { $ext_properties = $event->getExtendedProperties(); if ($ext_properties !== null) { $private = $ext_properties->private; if ($private !== null && array_key_exists('service_id', $private)) { continue; } } } // Get start/end dates of event and transform them into WP timezone (Google doesn't transform whole day events into our timezone). $event_start = $event->getStart(); $event_end = $event->getEnd(); if ($event_start->dateTime == null) { // All day event. $eventStartDate = new DateTime($event_start->date, new DateTimeZone($this->getCalendarTimezone())); $eventEndDate = new DateTime($event_end->date, new DateTimeZone($this->getCalendarTimezone())); } else { // Regular event. $eventStartDate = new DateTime($event_start->dateTime); $eventEndDate = new DateTime($event_end->dateTime); } $eventStartDate->setTimezone(new DateTimeZone(AB_Utils::getTimezoneString())); $eventEndDate->setTimezone(new DateTimeZone(AB_Utils::getTimezoneString())); // Check if event intersects with out datetime interval. if ($eventEndDate > $startDate) { // If event lasts for more than 1 day, then divide it. for ($loop_start = $eventStartDate; $loop_start < $eventEndDate; $loop_start->modify('+1 day')->setTime(0, 0, 0)) { // Start date. if ($loop_start->format('Ymd') > $eventStartDate->format('Ymd')) { $start_date = $loop_start->format('Y-m-d 00:00:00'); } else { $start_date = $loop_start->format('Y-m-d H:i:s'); } // End date. if ($loop_start->format('Ymd') < $eventEndDate->format('Ymd')) { $end_date = '10_symbols 24:00:00'; } else { $loop_end = clone $eventEndDate; $end_date = $loop_end->getTimestamp(); // Round end time to a multiple of the default time slot length. $extra = $end_date % $time_slot_length; if ($extra) { $end_date += $time_slot_length - $extra; } $loop_end->setTimestamp($end_date); $end_date = $loop_end->format('Y-m-d H:i:s'); if (substr($end_date, 11) == '00:00:00') { // Set time to 24:00:00 (date part does not matter, it just needs to be 10 characters length). $end_date = '10_symbols 24:00:00'; } } $result[] = array('staff_id' => $this->staff->get('id'), 'start_date' => $start_date, 'end_date' => $end_date, 'number_of_bookings' => 1, 'from_google' => true); } } } } if (!$limit_events && $events->getNextPageToken()) { $events = $this->service->events->listEvents($this->getCalendarID(), array('singleEvents' => true, 'orderBy' => 'startTime', 'timeMin' => $timeMin, 'pageToken' => $events->getNextPageToken())); } else { break; } } return $result; } catch (Exception $e) { $this->errors[] = $e->getMessage(); } return false; }
/** * Get data needed for appointment form initialisation. */ public function executeGetDataForAppointmentForm() { $result = array('staff' => array(), 'customers' => array(), 'custom_fields' => array(), 'time' => array(), 'time_interval' => get_option('ab_settings_time_slot_length') * 60); // Staff list. $staff_members = AB_Utils::isCurrentUserAdmin() ? AB_Staff::query()->sortBy('position')->find() : AB_Staff::query()->where('wp_user_id', get_current_user_id())->find(); /** @var AB_Staff $staff_member */ foreach ($staff_members as $staff_member) { $services = array(); foreach ($staff_member->getStaffServices() as $staff_service) { $services[] = array('id' => $staff_service->service->get('id'), 'title' => sprintf('%s (%s)', $staff_service->service->get('title'), AB_Service::durationToString($staff_service->service->get('duration'))), 'duration' => $staff_service->service->get('duration'), 'capacity' => $staff_service->get('capacity')); } $result['staff'][] = array('id' => $staff_member->get('id'), 'full_name' => $staff_member->get('full_name'), 'services' => $services); } // Customers list. foreach (AB_Customer::query()->sortBy('name')->find() as $customer) { $name = $customer->get('name'); if ($customer->get('email') != '' || $customer->get('phone') != '') { $name .= ' (' . trim($customer->get('email') . ', ' . $customer->get('phone'), ', ') . ')'; } $result['customers'][] = array('id' => $customer->get('id'), 'name' => $name, 'custom_fields' => array(), 'number_of_persons' => 1); } // Time list. $ts_length = AB_BookingConfiguration::getTimeSlotLength(); $time_start = AB_StaffScheduleItem::WORKING_START_TIME; $time_end = AB_StaffScheduleItem::WORKING_END_TIME; // Run the loop. while ($time_start <= $time_end) { $result['time'][] = array('value' => AB_DateTimeUtils::buildTimeString($time_start, false), 'title' => AB_DateTimeUtils::formatTime($time_start)); $time_start += $ts_length; } wp_send_json($result); }
/** * Find array of time slots available for booking * for given date. * * @access private * @param string $date * @return array */ private function _findAvailableTime($date) { $result = array(); $dayofweek = date('w', strtotime($date)) + 1; // 1-7 $is_date_today = $date == date('Y-m-d', current_time('timestamp')); $current_time = date('H:i:s', ceil(current_time('timestamp') / AB_BookingConfiguration::getTimeSlotLength()) * AB_BookingConfiguration::getTimeSlotLength()); foreach ($this->staff_working_hours as $staff_id => $hours) { if (isset($hours[$dayofweek]) && $this->isWorkingDay($date, $staff_id)) { // Find intersection between working and requested hours //(excluding time slots in the past). $working_start_time = $is_date_today && $current_time > $hours[$dayofweek]['start_time'] ? $current_time : $hours[$dayofweek]['start_time']; $intersection = $this->_findIntersection($this->_strToTime($working_start_time), $this->_strToTime($hours[$dayofweek]['end_time']), $this->_strToTime($this->_userData->getRequestedTimeFrom()), $this->_strToTime($this->_userData->getRequestedTimeTo())); if (is_array($intersection) && !array_key_exists('start', $intersection)) { $intersections = $intersection; foreach ($intersections as $intersection) { if ($intersection && $this->service_duration <= $intersection['end'] - $intersection['start']) { // Initialize time frames. $timeframes = array(array('start' => $intersection['start'], 'end' => $intersection['end'], 'staff_id' => $staff_id)); // Remove breaks from the time frames. foreach ($hours[$dayofweek]['breaks'] as $break) { $timeframes = $this->_removeTimePeriod($timeframes, $this->_strToTime($break['start']), $this->_strToTime($break['end'])); } // Remove bookings from the time frames. $bookings = isset($this->bookings[$staff_id]) ? $this->bookings[$staff_id] : array(); foreach ($bookings as $booking) { $bookingStart = new DateTime($booking->start_date); if ($date == $bookingStart->format('Y-m-d')) { $bookingEnd = new DateTime($booking->end_date); $booking_start = $bookingStart->format('U') % (24 * 60 * 60); $booking_end = $bookingEnd->format('U') % (24 * 60 * 60); $timeframes = $this->_removeTimePeriod($timeframes, $booking_start, $booking_end); } } $result = array_merge($result, $timeframes); } } } else { if ($intersection && $this->service_duration <= $intersection['end'] - $intersection['start']) { // Initialize time frames. $timeframes = array(array('start' => $intersection['start'], 'end' => $intersection['end'], 'staff_id' => $staff_id)); // Remove breaks from the time frames. foreach ($hours[$dayofweek]['breaks'] as $break) { $timeframes = $this->_removeTimePeriod($timeframes, $this->_strToTime($break['start']), $this->_strToTime($break['end'])); } // Remove bookings from the time frames. $bookings = isset($this->bookings[$staff_id]) ? $this->bookings[$staff_id] : array(); foreach ($bookings as $booking) { $bookingStart = new DateTime($booking->start_date); if ($date == $bookingStart->format('Y-m-d')) { $bookingEnd = new DateTime($booking->end_date); $booking_start = $bookingStart->format('U') % (24 * 60 * 60); $booking_end = $bookingEnd->format('U') % (24 * 60 * 60); $timeframes = $this->_removeTimePeriod($timeframes, $booking_start, $booking_end); } } $result = array_merge($result, $timeframes); } } } } usort($result, create_function('$a, $b', 'return $a[\'start\'] - $b[\'start\'];')); return $result; }