/**
  * 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);
     }
 }