PluginProbe
Booking Calendar / 11.8.4
Booking Calendar v11.8.4
11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 All 204 releases
← All changes | includes/page-bookings/bookings__sql.php +376 -11 11.811.8.4 View file →
@@ -1638,9 +1638,9 @@
1638 1638 * )
1639 1639 * [181] => stdClass Object (
1640 1640 * ....
1641 1641 */
1642 - function wpbc_ajx_include_bookingdates_in_the_bookings( $bookings_obj, $booking_dates_obj ){
1642 + function wpbc_ajx_include_bookingdates_in_the_bookings( $bookings_obj, $booking_dates_obj ){
1643 1643
1644 1644 $bookings_arr = array();
1645 1645 foreach ( $bookings_obj as $booking ) {
1646 1646
@@ -1744,14 +1744,376 @@
1744 1744 }
1745 1745
1746 1746 } // bookings loop: $booking_id => $booking
1747 1747
1748 - return $bookings_arr;
1749 - }
1748 + return $bookings_arr;
1749 + }
1750 +
1751 +
1752 + /**
1753 + * Build an empty Booking Listing Resource-allocation DTO.
1754 + *
1755 + * A stable empty shape keeps the browser template data-only and avoids
1756 + * edition-specific checks in the presentation layer.
1757 + *
1758 + * @return array<string,mixed> Empty Resource-allocation DTO.
1759 + */
1760 + function wpbc_booking_listing_get_empty_resource_allocation() {
1761 +
1762 + return array(
1763 + 'is_multiple_resources' => false,
1764 + 'resource_count' => 0,
1765 + 'resource_count_label' => '',
1766 + 'date_summary_label' => '',
1767 + 'segment_count' => 0,
1768 + 'segments' => array(),
1769 + );
1770 + }
1771 +
1772 +
1773 + /**
1774 + * Return a localized time label for one allocation day.
1775 + *
1776 + * Duplicate timestamps are expected when a quantity booking reserves
1777 + * several Resources. Midnight represents a full day and is omitted. When
1778 + * two or more non-midnight values exist, the first and last values describe
1779 + * the booked time interval without repeating it for every Resource.
1780 + *
1781 + * @param array<string,mixed> $allocation_day Normalized day record.
1782 + *
1783 + * @return string Localized time or time-range label. Empty for full days.
1784 + */
1785 + function wpbc_booking_listing_get_allocation_day_time_label( $allocation_day ) {
1786 +
1787 + if ( empty( $allocation_day['times'] ) || ! is_array( $allocation_day['times'] ) ) {
1788 + return '';
1789 + }
1790 +
1791 + $sql_times = array_values( array_unique( array_filter( array_map( 'strval', $allocation_day['times'] ) ) ) );
1792 + sort( $sql_times, SORT_STRING );
1793 +
1794 + $formatted_times = array();
1795 + foreach ( $sql_times as $sql_time ) {
1796 + if ( '00:00:00' === $sql_time ) {
1797 + continue;
1798 + }
1799 +
1800 + list( , $formatted_time ) = wpbc_get_date_in_correct_format( $allocation_day['date'] . ' ' . $sql_time );
1801 + $formatted_time = trim( $formatted_time );
1802 + if ( '' !== $formatted_time ) {
1803 + $formatted_times[] = $formatted_time;
1804 + }
1805 + }
1806 +
1807 + $formatted_times = array_values( array_unique( $formatted_times ) );
1808 + if ( empty( $formatted_times ) ) {
1809 + return '';
1810 + }
1811 +
1812 + if ( 1 === count( $formatted_times ) ) {
1813 + return $formatted_times[0];
1814 + }
1815 +
1816 + return $formatted_times[0] . ' – ' . $formatted_times[ count( $formatted_times ) - 1 ];
1817 + }
1818 +
1819 +
1820 + /**
1821 + * Format one chronological Resource-allocation period.
1822 + *
1823 + * Common recurrent times are shown once after the date range. Check-in and
1824 + * check-out times that differ between the first and final day stay attached
1825 + * to their respective dates. Full-day periods contain dates only.
1826 + *
1827 + * @param string $start_date First date in Y-m-d format.
1828 + * @param string $end_date Final date in Y-m-d format.
1829 + * @param array<string,array> $allocation_days Normalized days keyed by Y-m-d.
1830 + *
1831 + * @return string Localized period label.
1832 + */
1833 + function wpbc_booking_listing_format_allocation_period_label( $start_date, $end_date, $allocation_days ) {
1834 +
1835 + list( $start_date_label ) = wpbc_get_date_in_correct_format( $start_date . ' 00:00:00' );
1836 + list( $end_date_label ) = wpbc_get_date_in_correct_format( $end_date . ' 00:00:00' );
1837 +
1838 + $period_days = array();
1839 + foreach ( $allocation_days as $date_key => $allocation_day ) {
1840 + if ( $date_key >= $start_date && $date_key <= $end_date ) {
1841 + $period_days[ $date_key ] = $allocation_day;
1842 + }
1843 + }
1844 +
1845 + $day_time_labels = array();
1846 + foreach ( $period_days as $date_key => $allocation_day ) {
1847 + $day_time_labels[ $date_key ] = wpbc_booking_listing_get_allocation_day_time_label( $allocation_day );
1848 + }
1849 +
1850 + if ( $start_date === $end_date ) {
1851 + $time_label = isset( $day_time_labels[ $start_date ] ) ? $day_time_labels[ $start_date ] : '';
1852 +
1853 + return '' !== $time_label ? $start_date_label . ', ' . $time_label : $start_date_label;
1854 + }
1855 +
1856 + $non_empty_time_labels = array_values( array_filter( $day_time_labels ) );
1857 + $unique_time_labels = array_values( array_unique( $non_empty_time_labels ) );
1858 + if ( count( $non_empty_time_labels ) === count( $period_days ) && 1 === count( $unique_time_labels ) ) {
1859 + return $start_date_label . ' – ' . $end_date_label . ', ' . $unique_time_labels[0];
1860 + }
1861 +
1862 + $start_time_label = isset( $day_time_labels[ $start_date ] ) ? $day_time_labels[ $start_date ] : '';
1863 + $end_time_label = isset( $day_time_labels[ $end_date ] ) ? $day_time_labels[ $end_date ] : '';
1864 + if ( '' !== $start_time_label ) {
1865 + $start_date_label .= ', ' . $start_time_label;
1866 + }
1867 + if ( '' !== $end_time_label ) {
1868 + $end_date_label .= ', ' . $end_time_label;
1869 + }
1870 +
1871 + return $start_date_label . ' – ' . $end_date_label;
1872 + }
1873 +
1874 +
1875 + /**
1876 + * Resolve authorized Resource presentation records for an allocation set.
1877 + *
1878 + * Only titles already present in the owner-filtered Booking Listing Resource
1879 + * collection are exposed. Missing or deleted Resources receive the existing
1880 + * neutral fallback instead of leaking an unavailable title.
1881 + *
1882 + * @param array<int> $resource_ids Resource IDs in display order.
1883 + * @param array<int,array> $booking_resources_arr Owner-filtered Resource records.
1884 + *
1885 + * @return array<int,array<string,mixed>> JSON-safe Resource presentation records.
1886 + */
1887 + function wpbc_booking_listing_get_allocation_resource_records( $resource_ids, $booking_resources_arr ) {
1888 +
1889 + $resource_records = array();
1890 + foreach ( $resource_ids as $resource_id ) {
1891 + $resource_id = absint( $resource_id );
1892 + $resource_title = isset( $booking_resources_arr[ $resource_id ]['title'] )
1893 + ? wp_strip_all_tags( wpbc_lang( $booking_resources_arr[ $resource_id ]['title'] ) )
1894 + : __( 'Resource not exist', 'booking' );
1895 +
1896 + $resource_records[] = array(
1897 + 'resource_id' => $resource_id,
1898 + 'resource_title' => $resource_title,
1899 + 'is_missing' => ! isset( $booking_resources_arr[ $resource_id ] ),
1900 + );
1901 + }
1902 +
1903 + return $resource_records;
1904 + }
1905 +
1906 +
1907 + /**
1908 + * Group normalized allocation days into chronological Resource-set segments.
1909 + *
1910 + * Consecutive dates are merged only while their complete Resource set stays
1911 + * identical. This represents both a booking that moves between Resources and
1912 + * a quantity booking that reserves several Resources for the same period.
1913 + *
1914 + * @param array<string,array> $allocation_days Normalized days keyed by Y-m-d.
1915 + * @param array<int,array> $booking_resources_arr Owner-filtered Resource records.
1916 + *
1917 + * @return array<int,array<string,mixed>> Ordered allocation segments.
1918 + */
1919 + function wpbc_booking_listing_group_allocation_days( $allocation_days, $booking_resources_arr ) {
1920 +
1921 + $segments = array();
1922 + $current_segment = array();
1923 +
1924 + foreach ( $allocation_days as $date_key => $allocation_day ) {
1925 + $resource_ids = array_values( array_unique( array_map( 'absint', array_keys( $allocation_day['resources'] ) ) ) );
1926 + sort( $resource_ids, SORT_NUMERIC );
1927 + $resource_signature = implode( ',', $resource_ids );
1928 +
1929 + $is_same_segment = ! empty( $current_segment )
1930 + && $resource_signature === $current_segment['resource_signature']
1931 + && wpbc_is_next_day( $date_key, $current_segment['end_date'] );
1932 +
1933 + if ( ! $is_same_segment ) {
1934 + if ( ! empty( $current_segment ) ) {
1935 + $current_segment['period_label'] = wpbc_booking_listing_format_allocation_period_label(
1936 + $current_segment['start_date'],
1937 + $current_segment['end_date'],
1938 + $allocation_days
1939 + );
1940 + $current_segment['resources'] = wpbc_booking_listing_get_allocation_resource_records( $current_segment['resource_ids'], $booking_resources_arr );
1941 + unset( $current_segment['resource_signature'], $current_segment['resource_ids'] );
1942 + $segments[] = $current_segment;
1943 + }
1944 +
1945 + $current_segment = array(
1946 + 'start_date' => $date_key,
1947 + 'end_date' => $date_key,
1948 + 'day_count' => 1,
1949 + 'resource_signature' => $resource_signature,
1950 + 'resource_ids' => $resource_ids,
1951 + );
1952 + continue;
1953 + }
1954 +
1955 + $current_segment['end_date'] = $date_key;
1956 + ++$current_segment['day_count'];
1957 + }
1958 +
1959 + if ( ! empty( $current_segment ) ) {
1960 + $current_segment['period_label'] = wpbc_booking_listing_format_allocation_period_label(
1961 + $current_segment['start_date'],
1962 + $current_segment['end_date'],
1963 + $allocation_days
1964 + );
1965 + $current_segment['resources'] = wpbc_booking_listing_get_allocation_resource_records( $current_segment['resource_ids'], $booking_resources_arr );
1966 + unset( $current_segment['resource_signature'], $current_segment['resource_ids'] );
1967 + $segments[] = $current_segment;
1968 + }
1969 +
1970 + return $segments;
1971 + }
1972 +
1973 +
1974 + /**
1975 + * Build compact chronological date periods independently of Resource changes.
1976 + *
1977 + * This summary is used in the narrow date column. It avoids repeating dates
1978 + * for quantity bookings while preserving gaps between non-consecutive dates.
1979 + *
1980 + * @param array<string,array> $allocation_days Normalized days keyed by Y-m-d.
1981 + *
1982 + * @return array<int,string> Localized date-period labels.
1983 + */
1984 + function wpbc_booking_listing_get_allocation_date_period_labels( $allocation_days ) {
1985 +
1986 + $period_labels = array();
1987 + $start_date = '';
1988 + $end_date = '';
1989 +
1990 + foreach ( array_keys( $allocation_days ) as $date_key ) {
1991 + if ( '' === $start_date ) {
1992 + $start_date = $date_key;
1993 + $end_date = $date_key;
1994 + continue;
1995 + }
1996 +
1997 + if ( wpbc_is_next_day( $date_key, $end_date ) ) {
1998 + $end_date = $date_key;
1999 + continue;
2000 + }
2001 +
2002 + $period_labels[] = wpbc_booking_listing_format_allocation_period_label( $start_date, $end_date, $allocation_days );
2003 + $start_date = $date_key;
2004 + $end_date = $date_key;
2005 + }
2006 +
2007 + if ( '' !== $start_date ) {
2008 + $period_labels[] = wpbc_booking_listing_format_allocation_period_label( $start_date, $end_date, $allocation_days );
2009 + }
2010 +
2011 + return $period_labels;
2012 + }
2013 +
2014 +
2015 + /**
2016 + * Build the Booking Listing Resource-allocation DTO from saved date rows.
2017 + *
2018 + * Business Large stores the booking's main Resource in `booking.booking_type`.
2019 + * A date on that Resource has an empty `bookingdates.type_id`; only dates on
2020 + * another Resource contain `type_id`. Normalizing that fallback before
2021 + * counting or grouping prevents both missing labels and repeated per-date
2022 + * child labels.
2023 + *
2024 + * @param object $booking Booking with `booking_db`, `dates`, and `child_id` values.
2025 + * @param array<int,array> $booking_resources_arr Owner-filtered Resource records.
2026 + *
2027 + * @return array<string,mixed> JSON-safe Resource-allocation DTO.
2028 + */
2029 + function wpbc_booking_listing_build_resource_allocation( $booking, $booking_resources_arr ) {
2030 +
2031 + $resource_allocation = wpbc_booking_listing_get_empty_resource_allocation();
2032 + if (
2033 + empty( $booking->booking_db->booking_type )
2034 + || empty( $booking->dates )
2035 + || ! is_array( $booking->dates )
2036 + ) {
2037 + return $resource_allocation;
2038 + }
2039 +
2040 + $main_resource_id = absint( $booking->booking_db->booking_type );
2041 + $allocation_days = array();
2042 + $all_resource_ids = array();
2043 +
2044 + foreach ( $booking->dates as $date_index => $sql_booking_date ) {
2045 + $sql_booking_date = is_scalar( $sql_booking_date ) ? trim( (string) $sql_booking_date ) : '';
2046 + if ( ! preg_match( '/^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{2}):(\d{2}):(\d{2}))?$/', $sql_booking_date, $date_parts ) ) {
2047 + continue;
2048 + }
2049 +
2050 + $year = (int) $date_parts[1];
2051 + $month = (int) $date_parts[2];
2052 + $day = (int) $date_parts[3];
2053 + $hour = isset( $date_parts[4] ) ? (int) $date_parts[4] : 0;
2054 + $minute = isset( $date_parts[5] ) ? (int) $date_parts[5] : 0;
2055 + $second = isset( $date_parts[6] ) ? (int) $date_parts[6] : 0;
2056 + if (
2057 + ! checkdate( $month, $day, $year )
2058 + || $hour > 23
2059 + || $minute > 59
2060 + || $second > 59
2061 + ) {
2062 + continue;
2063 + }
2064 +
2065 + $date_key = sprintf( '%04d-%02d-%02d', $year, $month, $day );
2066 + $sql_time = isset( $date_parts[4], $date_parts[5], $date_parts[6] )
2067 + ? sprintf( '%02d:%02d:%02d', $hour, $minute, $second )
2068 + : '00:00:00';
2069 + $date_resource_id = isset( $booking->child_id[ $date_index ] ) && ! empty( $booking->child_id[ $date_index ] )
2070 + ? absint( $booking->child_id[ $date_index ] )
2071 + : $main_resource_id;
2072 +
2073 + if ( empty( $date_resource_id ) ) {
2074 + continue;
2075 + }
2076 +
2077 + if ( ! isset( $allocation_days[ $date_key ] ) ) {
2078 + $allocation_days[ $date_key ] = array(
2079 + 'date' => $date_key,
2080 + 'times' => array(),
2081 + 'resources' => array(),
2082 + );
2083 + }
2084 +
2085 + $allocation_days[ $date_key ]['times'][] = $sql_time;
2086 + $allocation_days[ $date_key ]['resources'][ $date_resource_id ] = true;
2087 + $all_resource_ids[ $date_resource_id ] = true;
2088 + }
2089 +
2090 + if ( empty( $allocation_days ) ) {
2091 + return $resource_allocation;
2092 + }
2093 +
2094 + ksort( $allocation_days, SORT_STRING );
2095 + $resource_count = count( $all_resource_ids );
2096 + $period_labels = wpbc_booking_listing_get_allocation_date_period_labels( $allocation_days );
2097 + $segments = wpbc_booking_listing_group_allocation_days( $allocation_days, $booking_resources_arr );
2098 +
2099 + $resource_allocation['is_multiple_resources'] = ( $resource_count > 1 );
2100 + $resource_allocation['resource_count'] = $resource_count;
2101 + $resource_allocation['resource_count_label'] = sprintf(
2102 + /* translators: %d: Number of booking Resources reserved by one booking. */
2103 + _n( '%d booking resource', '%d booking resources', $resource_count, 'booking' ),
2104 + $resource_count
2105 + );
2106 + $resource_allocation['date_summary_label'] = implode( ', ', $period_labels );
2107 + $resource_allocation['segment_count'] = count( $segments );
2108 + $resource_allocation['segments'] = $segments;
2109 +
2110 + return $resource_allocation;
2111 + }
2112 +
2113 +
2114 + function wpbc_ajx_parse_bookings( $bookings_arr, $resources_arr ) {
1750 2115
1751 -
1752 - function wpbc_ajx_parse_bookings( $bookings_arr, $resources_arr ) {
1753 -
1754 2116 $user_id = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing
1755 2117
1756 2118 foreach ( $bookings_arr as $booking_id => $booking ) {
1757 2119
@@ -1783,9 +2145,11 @@
1783 2145 $resource_id_listing = $child_resource_id_arr[0];
1784 2146 $resource_title_listing = wpbc_lang( $resources_arr[ $child_resource_id_arr[0] ]['title'] );
1785 2147 }
1786 2148 }
1787 - $show_child_resource_in_dates = ( count( $child_resource_id_arr ) > 1 );
2149 + $resource_allocation = class_exists( 'wpdev_bk_biz_l' )
2150 + ? wpbc_booking_listing_build_resource_allocation( $booking, $resources_arr )
2151 + : wpbc_booking_listing_get_empty_resource_allocation();
1788 2152
1789 2153 // Parse form fields only from $booking->booking_db->form ------------------------------------------
1790 2154 $booking_data_arr = wpbc_parse_booking_data_fields( $booking->booking_db->form,
1791 2155 array( 'resource_id' => $resource_id )
@@ -1832,12 +2196,12 @@
1832 2196 // Set dates and times in correct format ---------------------------------------------------------------
1833 2197 $booking_data_arr = wpbc_parse_booking_data_fields_formats( $booking_data_arr );
1834 2198
1835 2199 // Get SHORT / WIDE Dates showing data -----------------------------------------------------------------
1836 - $dates_attr = array(
1837 - 'date_html_tag' => 'span',
1838 - 'show_child_resource_in_dates' => $show_child_resource_in_dates,
1839 - );
2200 + $dates_attr = array(
2201 + 'date_html_tag' => 'span',
2202 + 'show_child_resource_in_dates' => false,
2203 + );
1840 2204 $short_dates_content = wpbc_get_formated_dates__short( $booking->short_dates, (bool) $booking->approved, $booking->short_dates_child_id, $resources_arr, $dates_attr );
1841 2205 $wide_dates_content = wpbc_get_formated_dates__wide( $booking->dates, (bool) $booking->approved, $booking->child_id, $resources_arr, $dates_attr );
1842 2206
1843 2207 //------------------------------------------------------------------------------------------------------
@@ -1941,8 +2305,9 @@
1941 2305 : '';
1942 2306 // =====================================================================================================
1943 2307 $booking_data_arr = apply_filters( 'wpbc_booking_listing_parsed_fields', $booking_data_arr, $booking_id, $booking );
1944 2308 $bookings_arr[ $booking_id ]->parsed_fields = $booking_data_arr;
2309 + $bookings_arr[ $booking_id ]->resource_allocation = $resource_allocation;
1945 2310
1946 2311
1947 2312 // =====================================================================================================
1948 2313 $bookings_arr[ $booking_id ]->templates = array(