/** * Output the calendar view */ public function output() { wp_enqueue_script('chosen'); $product_filter = isset($_REQUEST['filter_bookings']) ? absint($_REQUEST['filter_bookings']) : ''; $view = isset($_REQUEST['view']) && $_REQUEST['view'] == 'day' ? 'day' : 'month'; if ($view == 'day') { $day = isset($_REQUEST['calendar_day']) ? wc_clean($_REQUEST['calendar_day']) : date('Y-m-d'); $this->bookings = WC_Bookings_Controller::get_bookings_in_date_range(strtotime('midnight', strtotime($day)), strtotime('midnight +1 day', strtotime($day)), $product_filter); } else { $month = isset($_REQUEST['calendar_month']) ? absint($_REQUEST['calendar_month']) : date('n'); $year = isset($_REQUEST['calendar_year']) ? absint($_REQUEST['calendar_year']) : date('Y'); if ($year < date('Y') - 10 || $year > 2100) { $year = date('Y'); } if ($month > 12) { $month = 1; $year++; } if ($month < 1) { $month = 1; $year--; } $start_week = (int) date('W', strtotime("first day of {$year}-{$month}")); $end_week = (int) date('W', strtotime("last day of {$year}-{$month}")); if ($end_week == 1) { $end_week = 53; } $this->bookings = WC_Bookings_Controller::get_bookings_in_date_range(strtotime($year . 'W' . str_pad($start_week, 2, '0', STR_PAD_LEFT)), strtotime($year . 'W' . str_pad($end_week + 1, 2, '0', STR_PAD_LEFT)), $product_filter); } include 'views/html-calendar-' . $view . '.php'; wc_enqueue_js('$( "select#calendar-bookings-filter" ).chosen();'); }
/** * Show a users bookings */ public function my_bookings() { $bookings = WC_Bookings_Controller::get_bookings_for_user(get_current_user_id()); if ($bookings) { woocommerce_get_template('myaccount/my-bookings.php', array('bookings' => $bookings), 'woocommerce-bookings/', WC_BOOKINGS_TEMPLATE_PATH); } }
/** * Finds days which are fully booked already so they can be blocked on the date picker * @return array() */ protected function find_fully_booked_blocks() { // Bare existing bookings into consideration for datepicker $fully_booked_days = array(); $find_bookings_for = array($this->booking_form->product->id); if ($this->booking_form->product->has_resources()) { foreach ($this->booking_form->product->get_resources() as $resource) { $find_bookings_for[] = $resource->ID; } } $existing_bookings = WC_Bookings_Controller::get_bookings_for_objects($find_bookings_for, array('unpaid', 'pending', 'confirmed', 'paid', 'complete')); // Use the existing bookings to find days which are fully booked foreach ($existing_bookings as $existing_booking) { $start_date = $existing_booking->start; $end_date = $existing_booking->end; $product_id = $existing_booking->get_product_id(); $resource_id = $existing_booking->get_resource_id(); $check_date = $start_date; // Take it from the top // Loop over all booked days in this booking while ($check_date < $end_date) { if ($check_date >= current_time('timestamp')) { $js_date = date('Y-n-j', $check_date); if ($this->booking_form->product->has_resources()) { // Skip if we've already found this resource is unavailable if (!empty($fully_booked_days[$js_date][$resource_id])) { $check_date = strtotime("+1 day", $check_date); continue; } $availability = $this->booking_form->product->get_available_bookings(strtotime('midnight', $check_date), strtotime('tomorrow midnight', $check_date), 0); if (!$availability || is_wp_error($availability) || empty($availability[$resource_id])) { $fully_booked_days[$js_date][$resource_id] = true; } } else { // Skip if we've already found this product is unavailable if (!empty($fully_booked_days[$js_date])) { $check_date = strtotime("+1 day", $check_date); continue; } $availability = $this->booking_form->product->get_available_bookings(strtotime('midnight', $check_date), strtotime('tomorrow midnight', $check_date), 0); if (!$availability || is_wp_error($availability)) { $fully_booked_days[$js_date][0] = true; } } } $check_date = strtotime("+1 day", $check_date); } } $this->args['fully_booked_days'] = $fully_booked_days; }
/** * Output the calendar view */ public function output() { if (version_compare(WOOCOMMERCE_VERSION, '2.3', '<')) { wp_enqueue_script('chosen'); wc_enqueue_js('$( "select#calendar-bookings-filter" ).chosen();'); } else { wp_enqueue_script('wc-enhanced-select'); } $product_filter = isset($_REQUEST['filter_bookings']) ? absint($_REQUEST['filter_bookings']) : ''; $view = isset($_REQUEST['view']) && $_REQUEST['view'] == 'day' ? 'day' : 'month'; if ($view == 'day') { $day = isset($_REQUEST['calendar_day']) ? wc_clean($_REQUEST['calendar_day']) : date('Y-m-d'); $this->bookings = WC_Bookings_Controller::get_bookings_in_date_range(strtotime('midnight', strtotime($day)), strtotime('midnight +1 day', strtotime($day)), $product_filter); } else { $month = isset($_REQUEST['calendar_month']) ? absint($_REQUEST['calendar_month']) : date('n'); $year = isset($_REQUEST['calendar_year']) ? absint($_REQUEST['calendar_year']) : date('Y'); if ($year < date('Y') - 10 || $year > 2100) { $year = date('Y'); } if ($month > 12) { $month = 1; $year++; } if ($month < 1) { $month = 1; $year--; } $start_of_week = absint(get_option('start_of_week', 1)); $last_day = date('t', strtotime("{$year}-{$month}-01")); $start_date_w = absint(date('w', strtotime("{$year}-{$month}-01"))); $end_date_w = absint(date('w', strtotime("{$year}-{$month}-{$last_day}"))); // Calc day offset $day_offset = $start_date_w - $start_of_week; $day_offset = $day_offset >= 0 ? $day_offset : 7 - abs($day_offset); // Cald end day offset $end_day_offset = 7 - $last_day % 7 - $day_offset; $end_day_offset = $end_day_offset >= 0 && $end_day_offset < 7 ? $end_day_offset : 7 - abs($end_day_offset); $start_timestamp = strtotime("-{$day_offset} day", strtotime("{$year}-{$month}-01")); $end_timestamp = strtotime("+{$end_day_offset} day", strtotime("{$year}-{$month}-{$last_day}")); $this->bookings = WC_Bookings_Controller::get_bookings_in_date_range($start_timestamp, $end_timestamp, $product_filter); } include 'views/html-calendar-' . $view . '.php'; }
/** * Get existing bookings in a given date range * * @param string $start-date * @param string $end_date * @param int $resource_id * @return array */ public function get_bookings_in_date_range($start_date, $end_date, $resource_id = null) { if ($this->has_resources() && $resource_id) { return WC_Bookings_Controller::get_bookings_in_date_range($start_date, $end_date, $resource_id); } else { return WC_Bookings_Controller::get_bookings_in_date_range($start_date, $end_date, $this->id); } }
function sync_bookings($original_product_id, $product_id, $lang) { global $wpdb; $all_bookings_for_product = WC_Bookings_Controller::get_bookings_for_product($original_product_id, array('in-cart', 'unpaid', 'confirmed', 'paid')); foreach ($all_bookings_for_product as $booking) { $check_if_exists = $wpdb->get_row($wpdb->prepare("SELECT pm3.* FROM {$wpdb->postmeta} AS pm1\n LEFT JOIN {$wpdb->postmeta} AS pm2 ON pm1.post_id = pm2.post_id\n LEFT JOIN {$wpdb->postmeta} AS pm3 ON pm1.post_id = pm3.post_id\n WHERE pm1.meta_key = '_booking_duplicate_of' AND pm1.meta_value = %s AND pm2.meta_key = '_language_code' AND pm2.meta_value = %s AND pm3.meta_key = '_booking_product_id'", $booking->id, $lang)); if (is_null($check_if_exists)) { $this->duplicate_booking_for_translations($booking->id, $lang); } elseif ($check_if_exists->meta_value === '') { update_post_meta($check_if_exists->post_id, '_booking_product_id', $this->get_translated_booking_product_id($booking->id, $lang)); update_post_meta($check_if_exists->post_id, '_booking_resource_id', $this->get_translated_booking_resource_id($booking->id, $lang)); update_post_meta($check_if_exists->post_id, '_booking_persons', $this->get_translated_booking_persons_ids($booking->id, $lang)); } } }
/** * Finds days which are fully booked already so they can be blocked on the date picker * @return array() */ protected function find_fully_booked_blocks() { // Bare existing bookings into consideration for datepicker $fully_booked_days = array(); $partially_booked_days = array(); $find_bookings_for = array($this->booking_form->product->id); $resource_count = 0; if ($this->booking_form->product->has_resources()) { foreach ($this->booking_form->product->get_resources() as $resource) { $find_bookings_for[] = $resource->ID; $resource_count++; } } $booking_statuses = apply_filters('woocommerce_bookings_fully_booked_statuses', array('unpaid', 'pending-confirmation', 'confirmed', 'paid', 'complete', 'in-cart')); $existing_bookings = WC_Bookings_Controller::get_bookings_for_objects($find_bookings_for, $booking_statuses); // Use the existing bookings to find days which are fully booked foreach ($existing_bookings as $existing_booking) { $start_date = $existing_booking->start; $end_date = $existing_booking->is_all_day() ? strtotime('tomorrow midnight', $existing_booking->end) : $existing_booking->end; $resource_id = $existing_booking->get_resource_id(); $check_date = $start_date; // Take it from the top // Loop over all booked days in this booking while ($check_date < $end_date) { $js_date = date('Y-n-j', $check_date); if ($check_date < current_time('timestamp')) { $check_date = strtotime("+1 day", $check_date); continue; } if ($this->booking_form->product->has_resources()) { // Skip if we've already found this resource is unavailable if (!empty($fully_booked_days[$js_date][$resource_id])) { $check_date = strtotime("+1 day", $check_date); continue; } $blocks_in_range = $this->booking_form->product->get_blocks_in_range(strtotime('midnight', $check_date), strtotime('tomorrow midnight', $check_date), array(), $resource_id); $available_blocks = $this->booking_form->product->get_available_blocks($blocks_in_range, array(), $resource_id); if (sizeof($available_blocks) < sizeof($blocks_in_range)) { $partially_booked_days[$js_date][$resource_id] = true; if (1 === $resource_count || sizeof($partially_booked_days[$js_date]) === $resource_count) { $partially_booked_days[$js_date][0] = true; } } if (!$available_blocks) { $fully_booked_days[$js_date][$resource_id] = true; if (1 === $resource_count || sizeof($fully_booked_days[$js_date]) === $resource_count) { $fully_booked_days[$js_date][0] = true; } } } else { // Skip if we've already found this product is unavailable if (!empty($fully_booked_days[$js_date])) { $check_date = strtotime("+1 day", $check_date); continue; } $blocks_in_range = $this->booking_form->product->get_blocks_in_range(strtotime('midnight', $check_date), strtotime('tomorrow midnight', $check_date)); $available_blocks = $this->booking_form->product->get_available_blocks($blocks_in_range); if (sizeof($available_blocks) < sizeof($blocks_in_range)) { $partially_booked_days[$js_date][0] = true; } if (!$available_blocks) { $fully_booked_days[$js_date][0] = true; } } $check_date = strtotime("+1 day", $check_date); } } $this->args['partially_booked_days'] = $partially_booked_days; $this->args['fully_booked_days'] = $fully_booked_days; }
/** * Provides an email notification form */ public function notifications_page() { global $woocommerce; if (!empty($_POST) && check_admin_referer('send_booking_notification')) { $notification_product_id = absint($_POST['notification_product_id']); $notification_subject = wc_clean(stripslashes($_POST['notification_subject'])); $notification_message = wp_kses_post(stripslashes($_POST['notification_message'])); try { if (!$notification_product_id) { throw new Exception(__('Please choose a product', 'woocommerce-bookings')); } if (!$notification_message) { throw new Exception(__('Please enter a message', 'woocommerce-bookings')); } if (!$notification_subject) { throw new Exception(__('Please enter a subject', 'woocommerce-bookings')); } $bookings = WC_Bookings_Controller::get_bookings_for_product($notification_product_id); $mailer = $woocommerce->mailer(); $notification = $mailer->emails['WC_Email_Booking_Notification']; $attachments = array(); foreach ($bookings as $booking) { // Add .ics file if (isset($_POST['notification_ics'])) { $generate = new WC_Bookings_ICS_Exporter(); $attachments[] = $generate->get_booking_ics($booking); } $notification->trigger($booking->id, $notification_subject, $notification_message, $attachments); } echo '<div class="updated fade"><p>' . __('Notification sent successfully', 'woocommerce-bookings') . '</p></div>'; } catch (Exception $e) { echo '<div class="error"><p>' . $e->getMessage() . '</p></div>'; } } $booking_products = WC_Bookings_Admin::get_booking_products(); include 'views/html-notifications-page.php'; }
/** * Return an array of resources which can be booked for a defined start/end date * @param string $start_date * @param string $end_date * @param int $resource_id * @param integer $qty being booked * @return bool|WP_ERROR if no blocks available, or int count of bookings that can be made, or array of available resources */ public function get_available_bookings($start_date, $end_date, $resource_id = '', $qty = 1) { // Check the date is not in the past if (date('Ymd', $start_date) < date('Ymd', current_time('timestamp'))) { return false; } // Check we have a resource if needed $booking_resource = $resource_id ? $this->get_resource($resource_id) : null; if ($this->has_resources() && !is_numeric($resource_id)) { return false; } $min_date = $this->get_min_date(); $max_date = $this->get_max_date(); $check_from = strtotime("midnight +{$min_date['value']} {$min_date['unit']}", current_time('timestamp')); $check_to = strtotime("midnight +{$max_date['value']} {$max_date['unit']}", current_time('timestamp')); // Min max checks if ('month' === $this->get_duration_unit()) { $check_to = strtotime('midnight', strtotime(date('Y-m-t', $check_to))); } if ($end_date < $check_from || $start_date > $check_to) { return false; } // Get availability of each resource - no resource has been chosen yet if ($this->has_resources() && !$resource_id) { $resources = $this->get_resources(); $available_resources = array(); foreach ($resources as $resource) { $availability = $this->get_available_bookings($start_date, $end_date, $resource->ID, $qty); if ($availability && !is_wp_error($availability)) { $available_resources[$resource->ID] = $availability; } } if (empty($available_resources)) { return new WP_Error('Error', __('This block cannot be booked.', 'woocommerce-bookings')); } return $available_resources; // If we are checking for bookings for a specific resource, or have none... } else { $available_qtys = array(); $check_date = $start_date; while ($check_date < $end_date) { if (!$this->check_availability_rules_against_date($check_date, $resource_id)) { return false; } if ('start' === $this->wc_booking_check_availability_against) { break; // Only need to check first day } $check_date = strtotime("+1 day", $check_date); } if (in_array($this->get_duration_unit(), array('minute', 'hour')) && !$this->check_availability_rules_against_time($start_date, $end_date, $resource_id)) { return false; } $blocks = $this->get_blocks_in_range($start_date, $end_date, '', $resource_id); if (!$blocks) { return false; } /** * Grab all existing bookings for the date range * @var array */ if ($this->has_resources() && $resource_id) { $existing_bookings = WC_Bookings_Controller::get_bookings_in_date_range($start_date, $end_date, $resource_id); } else { $existing_bookings = WC_Bookings_Controller::get_bookings_in_date_range($start_date, $end_date, $this->id); } foreach ($blocks as $block) { $available_qty = $this->has_resources() && $booking_resource->has_qty() ? $booking_resource->get_qty() : $this->get_qty(); $qty_booked_in_block = 0; foreach ($existing_bookings as $existing_booking) { if ($existing_booking->is_booked_on_day($block)) { $qty_to_add = $this->has_person_qty_multiplier() ? max(1, array_sum($existing_booking->get_persons())) : 1; if ($this->has_resources()) { if ($existing_booking->get_resource_id() === absint($resource_id) || !$booking_resource->has_qty() && $existing_booking->get_resource() && !$existing_booking->get_resource()->has_qty()) { $qty_booked_in_block += $qty_to_add; } } else { $qty_booked_in_block += $qty_to_add; } } } $available_qty = $available_qty - $qty_booked_in_block; if ($available_qty < $qty) { if (in_array($this->get_duration_unit(), array('hour', 'minute'))) { return new WP_Error('Error', sprintf(_n('There is %d place remaining', 'There are %d places remaining', $available_qty, 'woocommerce-bookings'), $available_qty)); } elseif (!$available_qtys) { return new WP_Error('Error', sprintf(_n('There is %d place remaining on %s', 'There are %d places remaining on %s', $available_qty, 'woocommerce-bookings'), $available_qty, date_i18n(get_option('date_format'), $block))); } else { return new WP_Error('Error', sprintf(_n('There is %d place remaining on %s', 'There are %d places remaining on %s', $available_qty, 'woocommerce-bookings'), max($available_qtys), date_i18n(get_option('date_format'), $block))); } } $available_qtys[] = $available_qty; } return min($available_qtys); } }