PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 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 All 205 releases
booking / includes / _capacity / calendar_load.php

calendar_load.php in Booking Calendar 11.9, at includes/_capacity/calendar_load.php

573 lines 33.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @file: wp-content/plugins/booking/includes/_capacity/calendar_load.php
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
7
8 /**
9 * Define parameters for calendar -- required for JS calendar show
10 *
11 * @param array $params
12 *
13 * @return string JS code body (no <script> wrapper)
14 */
15 function wpbc__calendar__set_js_params__before_show( $params ) {
16
17 $start_script_code = '';
18
19 // Server Balancer.
20 $balancer_max_threads = intval( get_bk_option( 'booking_load_balancer_max_threads' ) );
21 $balancer_max_threads = ( empty( $balancer_max_threads ) ) ? 1 : $balancer_max_threads;
22 $start_script_code .= " _wpbc.balancer__set_max_threads( " . $balancer_max_threads . " ); ";
23
24 $resource_id = isset( $params['resource_id'] ) ? absint( $params['resource_id'] ) : 1;
25
26 if ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) ) {
27 $is_enabled_change_over = wpbc_is_booking_used_check_in_out_time( false, $resource_id );
28 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'is_enabled_change_over' , " . ( $is_enabled_change_over ? 'true' : 'false' ) . " ); ";
29 $start_script_code .= " _wpbc.set_other_param( 'is_enabled_change_over', " . ( $is_enabled_change_over ? 'true' : 'false' ) . " ); ";
30 }
31
32 // Start Month (require [year,month]).
33 $calendar_scroll_to = 'false';
34 if (
35 isset( $params['start_month_calendar'] )
36 && is_array( $params['start_month_calendar'] )
37 && ( count( $params['start_month_calendar'] ) > 1 )
38 ) {
39 $y = intval( $params['start_month_calendar'][0] );
40 $m = intval( $params['start_month_calendar'][1] );
41 if ( ( $y > 0 ) && ( $m > 0 ) ) {
42 $calendar_scroll_to = "[ " . (string) $y . "," . (string) $m . " ]";
43 }
44 }
45 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'calendar_scroll_to' , " . $calendar_scroll_to . " ); ";
46
47 // Set Start / End Date in Calendar (JSON encode => safe quotes).
48 $params['calendar_dates_start'] = ( empty( $params['calendar_dates_start'] ) ) ? '' : (string) $params['calendar_dates_start'];
49 $params['calendar_dates_end'] = ( empty( $params['calendar_dates_end'] ) ) ? '' : (string) $params['calendar_dates_end'];
50 $params['classic_booking_context_token'] = empty( $params['classic_booking_context_token'] ) ? '' : (string) $params['classic_booking_context_token'];
51
52 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'calendar_dates_start' , " . wp_json_encode( $params['calendar_dates_start'] ) . " ); ";
53 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'calendar_dates_end' , " . wp_json_encode( $params['calendar_dates_end'] ) . " ); ";
54 $start_script_code .= " _wpbc.booking__set_param_value( " . $resource_id . " , 'classic_booking_context_token' , " . wp_json_encode( $params['classic_booking_context_token'] ) . " ); ";
55
56 if ( ( '' !== $params['calendar_dates_start'] ) || ( '' !== $params['calendar_dates_end'] ) ) {
57 $start_script_code .= wpbc_get_localized_js__time_local( $params['calendar_dates_start'] );
58 }
59
60 // Max months to scroll.
61 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'booking_max_monthes_in_calendar' , " . wp_json_encode( (string) get_bk_option( 'booking_max_monthes_in_calendar' ) ) . " ); ";
62
63 // Start of week day.
64 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'booking_start_day_weeek' , " . wp_json_encode( (string) get_bk_option( 'booking_start_day_weeek' ) ) . " ); ";
65
66 // Number of visible months.
67 $months_num = isset( $params['calendar_number_of_months'] ) ? (int) $params['calendar_number_of_months'] : 1;
68 $months_num = max( 1, min( 36, $months_num ) );
69 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'calendar_number_of_months' , " . wp_json_encode( (string) $months_num ) . " ); ";
70
71 // Days Selection.
72 $days_selection_arr = wpbc__calendar__js_params__get_days_selection_arr();
73
74 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'days_select_mode', " . wp_json_encode( (string) $days_selection_arr['days_select_mode'] ) . " ); ";
75 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'fixed__days_num', " . intval( $days_selection_arr['fixed__days_num'] ) . " ); ";
76
77 // Keep legacy: these are stored as comma lists like "-1" or "0,1".
78 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'fixed__week_days__start', [" . $days_selection_arr['fixed__week_days__start'] . "] ); ";
79 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'dynamic__days_min', " . intval( $days_selection_arr['dynamic__days_min'] ) . " ); ";
80 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'dynamic__days_max', " . intval( $days_selection_arr['dynamic__days_max'] ) . " ); ";
81 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'dynamic__days_specific', [" . $days_selection_arr['dynamic__days_specific'] . "] ); ";
82 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'dynamic__week_days__start', [" . $days_selection_arr['dynamic__week_days__start'] . "] ); ";
83
84 // Informational help shown between the first and second clicks in range mode.
85 // Integrations can disable it per calendar/resource without changing the selection engine.
86 /**
87 * Filters whether the two-click range-selection guidance is shown for a calendar.
88 *
89 * @param bool $is_enabled Whether guidance is enabled.
90 * @param int $resource_id Booking resource ID.
91 * @param array $params Calendar shortcode/render parameters.
92 */
93 $range_selection_guidance_default = function_exists( 'wpbc_frontend_messages__is_enabled' )
94 ? wpbc_frontend_messages__is_enabled( 'message_range_selection_click_last_date' )
95 : true;
96 $range_selection_guidance_is_enabled = (bool) apply_filters(
97 'wpbc_calendar_range_selection_guidance_is_enabled',
98 $range_selection_guidance_default,
99 $resource_id,
100 $params
101 );
102 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'range_selection_guidance_is_enabled', " . ( $range_selection_guidance_is_enabled ? 'true' : 'false' ) . " ); ";
103
104 // Date / Time formats.
105 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'booking_date_format', " . wp_json_encode( (string) get_bk_option( 'booking_date_format' ) ) . " ); ";
106 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'booking_time_format', " . wp_json_encode( (string) get_bk_option( 'booking_time_format' ) ) . " ); ";
107
108 // Capacity | availability.
109 if ( class_exists( 'wpdev_bk_biz_l' ) ) {
110 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'is_parent_resource' , " . ( wpbc_get_child_resources_number( $resource_id ) ? 1 : 0 ) . " ); ";
111 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'booking_capacity_field' , " . wp_json_encode( (string) wpbc_get__booking_capacity_field__name() ) . " ); ";
112 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'booking_is_dissbale_booking_for_different_sub_resources' , " . wp_json_encode( (string) get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' ) ) . " ); ";
113 }
114 if ( class_exists( 'wpdev_bk_biz_s' ) ) {
115 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . " , 'booking_recurrent_time' , " . wp_json_encode( (string) get_bk_option( 'booking_recurrent_time' ) ) . " ); ";
116 }
117
118 // Save initial values of days selection for later use in conditional day logic.
119 $start_script_code .= " if ( 'function' === typeof ( wpbc__conditions__SAVE_INITIAL__days_selection_params__bm ) ) { wpbc__conditions__SAVE_INITIAL__days_selection_params__bm( " . $resource_id . " ); } ";
120
121 if (
122 ( ! empty( $params['shortcode_options'] ) )
123 && ( function_exists( 'wpbc_parse_shortcode_option__set_days_selection_conditions' ) )
124 ) {
125 $days_selection_conditions = wpbc_parse_shortcode_option__set_days_selection_conditions( $resource_id, $params['shortcode_options'] );
126
127 if ( ! empty( $days_selection_conditions ) ) {
128 $start_script_code .= " _wpbc.calendar__set_param_value( " . $resource_id . ", 'conditions', " . wp_json_encode( $days_selection_conditions['conditions'] ) . " ); ";
129 $start_script_code .= " _wpbc.seasons__set( " . $resource_id . ", " . wp_json_encode( $days_selection_conditions['seasons'] ) . " ); ";
130 }
131 }
132
133 return $start_script_code;
134 }
135
136
137 /**
138 * Get days selection parameters, which saved in database
139 *
140 * @return array
141 */
142 function wpbc__calendar__js_params__get_days_selection_arr(){
143
144 $specific_days_selection = ( function_exists( 'wpbc_get_specific_range_dates__as_comma_list' ) )
145 ? wpbc_get_specific_range_dates__as_comma_list( get_bk_option( 'booking_range_selection_days_specific_num_dynamic' ) )
146 : '';
147 $data_arr = array();
148 // Modes :: Selection of Days.
149 $day_selection_type = get_bk_option( 'booking_type_of_day_selections' );
150 if ( 'range' === $day_selection_type ) {
151 // Free/Personal use the unrestricted two-click range. Business Small+ can apply its saved range subtype/rules.
152 $data_arr['days_select_mode'] = class_exists( 'wpdev_bk_biz_s' )
153 ? get_bk_option( 'booking_range_selection_type' )
154 : 'dynamic';
155 } else {
156 $data_arr['days_select_mode'] = $day_selection_type;
157 }
158 $fixed_days_num = absint( get_bk_option( 'booking_range_selection_days_count' ) );
159 $data_arr['fixed__days_num'] = ( 0 < $fixed_days_num ) ? min( 180, $fixed_days_num ) : 3; /* Number of days selection with 1 mouse click */
160 $data_arr['fixed__week_days__start'] = get_bk_option( 'booking_range_start_day' ); /* { -1 - Any | 0 - Su, 1 - Mo, 2 - Tu, 3 - We, 4 - Th, 5 - Fr, 6 - Sat } */
161 $data_arr['dynamic__days_min'] = intval( get_bk_option( 'booking_range_selection_days_count_dynamic' ) ); /* Min. Number of days selection with 2 mouse clicks */
162 $data_arr['dynamic__days_max'] = intval( get_bk_option( 'booking_range_selection_days_max_count_dynamic' ) ); /* Max. Number of days selection with 2 mouse clicks */
163 $data_arr['dynamic__days_specific'] = $specific_days_selection; /* Example: '5,7' */
164 $data_arr['dynamic__week_days__start'] = get_bk_option( 'booking_range_start_day_dynamic' ); /* { -1 - Any | 0 - Su, 1 - Mo, 2 - Tu, 3 - We, 4 - Th, 5 - Fr, 6 - Sat } */
165
166 // A fixed one-click range remains Business Small+. On downgrade, preserve the Range choice but use Free's unrestricted two-click mode.
167 if ( ( ! class_exists( 'wpdev_bk_biz_s' ) ) && ( in_array( $data_arr['days_select_mode'], array( 'dynamic', 'fixed' ), true ) ) ) {
168 $data_arr['days_select_mode'] = 'dynamic';
169 $data_arr['dynamic__days_min'] = 1;
170 $data_arr['dynamic__days_specific'] = '';
171 $data_arr['dynamic__week_days__start'] = '-1';
172 }
173
174 return $data_arr;
175 }
176
177
178
179 /**
180 * Get JavaScript for Calendar Loading:
181 * 1) Show Calendar
182 * 2) Send Ajax to load bookings / availability / data for calendar
183 *
184 * @param array $params
185 *
186 * @return string Either '' (when collected to footer) OR legacy <script>...</script> fallback.
187 */
188 function wpbc__calendar__load( $params = array() ) {
189
190 $defaults = array(
191 'resource_id' => '1',
192 'aggregate_resource_id_arr' => array(),
193 'selected_dates_without_calendar' => '',
194 'calendar_number_of_months' => 1,
195 'start_month_calendar' => false,
196 'shortcode_options' => '',
197 'custom_form' => 'standard',
198 'calendar_dates_start' => '',
199 'calendar_dates_end' => '',
200 'classic_booking_context_token' => '',
201 'skip_general_availability' => 0,
202 'calendar_request_overrides' => array(),
203 );
204 $params = wp_parse_args( $params, $defaults );
205
206 // Resource ID.
207 $params['resource_id'] = absint( $params['resource_id'] );
208
209 // Build JS BODY (no <script>, no ready wrapper).
210 $js_body = '';
211
212 $js_body .= wpbc__calendar__set_js_params__before_show( $params );
213
214 // Show calendar.
215 $js_body .= " wpbc_calendar_show(" . wp_json_encode( (string) $params['resource_id'] ) . "); ";
216
217 // Security params.
218 $js_body .= " _wpbc.set_secure_param('nonce', " . wp_json_encode( wp_create_nonce( 'wpbc_calendar_load_ajx' . '_wpbcnonce' ) ) . "); ";
219 $js_body .= " _wpbc.set_secure_param('user_id', " . wp_json_encode( (string) wpbc_get_current_user_id() ) . "); ";
220 $js_body .= " _wpbc.set_secure_param('locale', " . wp_json_encode( (string) get_user_locale() ) . "); ";
221
222 // booking_hash (keep legacy GET behavior, sanitized).
223 $get_booking_hash = '';
224 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing
225 if ( isset( $_GET['booking_hash'] ) ) {
226 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
227 $raw_hash = wp_unslash( $_GET['booking_hash'] );
228 if ( ! is_array( $raw_hash ) ) {
229 $get_booking_hash = sanitize_text_field( (string) $raw_hash );
230 }
231 }
232
233 // Aggregate type.
234 $aggregate_type = 'all';
235 if (
236 ( ! empty( $params['shortcode_options'] ) )
237 && function_exists( 'wpbc_parse_calendar_options__aggregate_param' )
238 ) {
239 $aggregate_type_res = wpbc_parse_calendar_options__aggregate_param( $params['shortcode_options'] );
240 if ( ( ! empty( $aggregate_type_res ) ) && ( ! empty( $aggregate_type_res['type'] ) ) ) {
241 $aggregate_type = (string) $aggregate_type_res['type'];
242 }
243 }
244
245 // request_uri captured on front-end (legacy rationale).
246 $server_request_uri = '';
247 // phpcs:ignore WordPress.Security.NonceVerification.Missing
248 if ( isset( $_SERVER['REQUEST_URI'] ) ) {
249 // phpcs:ignore WordPress.Security.NonceVerification.Missing
250 $server_request_uri = sanitize_text_field( wp_unslash( (string) $_SERVER['REQUEST_URI'] ) );
251 }
252
253 $params_for_request = array(
254 'resource_id' => $params['resource_id'],
255 'booking_hash' => $get_booking_hash,
256 'request_uri' => $server_request_uri,
257 'custom_form' => (string) $params['custom_form'],
258 'aggregate_resource_id_str' => implode( ',', (array) $params['aggregate_resource_id_arr'] ),
259 'aggregate_type' => (string) $aggregate_type,
260 'skip_general_availability' => (int) $params['skip_general_availability'],
261 'classic_booking_context_token' => (string) $params['classic_booking_context_token'],
262 );
263
264 if ( ! empty( $params['calendar_request_overrides'] ) && is_array( $params['calendar_request_overrides'] ) ) {
265 foreach ( $params['calendar_request_overrides'] as $override_key => $override_value ) {
266 if ( 'allow_past' === $override_key ) {
267 $params_for_request['allow_past'] = ! empty( $override_value ) ? 1 : 0;
268 } elseif (
269 0 === strpos( (string) $override_key, 'wpbc_settings_calendar_preview' )
270 || in_array(
271 (string) $override_key,
272 array(
273 'wpbc_setup_wizard_date_selection_preview',
274 'wpbc_setup_wizard_date_selection_preview_nonce',
275 ),
276 true
277 )
278 ) {
279 $params_for_request[ $override_key ] = $override_value;
280 }
281 }
282 }
283
284 // Send Ajax request to load bookings.
285 $js_body .= " wpbc_calendar__load_data__ajx(" . wp_json_encode( $params_for_request ) . "); ";
286
287 // ---------------------------------------------------------------------
288 // Elementor-safe path: add inline via WP scripts (footer-safe, not in content). // FixIn: 10.14.13.2.
289 // ---------------------------------------------------------------------
290 $can_use_wp_inline = ( ! wp_doing_ajax() );
291
292 if ( $can_use_wp_inline && class_exists( 'WPBC_FE_Assets' ) ) {
293
294 $assets_key = 'wpbc:cal-load:' . intval( $params['resource_id'] ) . ':' . md5( wp_json_encode( $params_for_request ) );
295
296 // Try after main client script first (best), fallback to wpbc_all if needed.
297 $added = WPBC_FE_Assets::add_jq_ready_js_to_wp_script( 'wpbc-main-client', $js_body, $assets_key );
298 if ( ! $added ) {
299 $added = WPBC_FE_Assets::add_jq_ready_js_to_wp_script( 'wpbc_all', $js_body, $assets_key );
300 }
301
302 if ( $added ) {
303 return ''; // Important: nothing inline in the content.
304 }
305 }
306
307 // ---------------------------------------------------------------------
308 // Legacy fallback: return inline <script>...</script> (Works in rare contexts without wp_footer / without assets manager).
309 // ---------------------------------------------------------------------
310 $start_script_code = '<script type="text/javascript"> ' . wpbc_jq_ready_start() . $js_body . wpbc_jq_ready_end() . '</script>';
311
312 return $start_script_code;
313 }
314
315
316 /**
317 * Get Booking Calendar option names that belong to General Availability rules.
318 *
319 * @return array
320 */
321 function wpbc__calendar__get_general_availability_option_names() {
322
323 $option_names = array(
324 'booking_unavailable_days_num_from_today',
325 'booking_available_days_num_from_today',
326 'booking_unavailable_extra_in_out',
327 'booking_unavailable_extra_minutes_in',
328 'booking_unavailable_extra_minutes_out',
329 'booking_unavailable_extra_days_in',
330 'booking_unavailable_extra_days_out',
331 );
332
333 foreach ( range( 0, 6 ) as $week_day_num ) {
334 $option_names[] = 'booking_unavailable_day' . $week_day_num;
335 }
336
337 return $option_names;
338 }
339
340
341
342 /**
343 * Response to Ajax request, about loading calendar data
344 *
345 * @return void
346 */
347 function ajax_WPBC_AJX_CALENDAR_LOAD() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound
348
349 // Security ------------------------------------------------------------------------------------------------------ // in Ajax Post: 'nonce': _wpbc.get_secure_param( 'nonce' ),
350 $action_name = 'wpbc_calendar_load_ajx' . '_wpbcnonce';
351 $nonce_post_key = 'nonce';
352 if ( wpbc_is_use_nonce_at_front_end() ) { // FixIn: 10.1.1.2.
353 $result_check = check_ajax_referer( $action_name, $nonce_post_key );
354 }
355
356 $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
357
358 /**
359 * SQL ---------------------------------------------------------------------------
360 *
361 * in Ajax Post: 'search_params': _wpbc.search_get_all_params()
362 *
363 * Use prefix "search_params", if Ajax sent - $_REQUEST['search_params']['page_num'], $_REQUEST['search_params']['page_items_count'], ...
364 */
365
366 /**
367 * Using this class here only for escaping variables
368 */
369 $user_request = new WPBC_AJX__REQUEST( array(
370 'db_option_name' => 'booking__wpbc_calendar_load__request_params',
371 'user_id' => $user_id,
372 'request_rules_structure' => array(
373 'resource_id' => array( 'validate' => 'd', 'default' => 1 ), // 'digit_or_csd'
374 'booking_hash' => array( 'validate' => 's', 'default' => '' ),
375 'request_uri' => array( 'validate' => 's', 'default' => '' ),
376 'custom_form' => array( 'validate' => 's', 'default' => 'standard' ),
377 'allow_past' => array( 'validate' => 'd', 'default' => 0 ),
378 'classic_booking_context_token' => array( 'validate' => 'strong', 'default' => '' ),
379 'aggregate_resource_id_str' => array( 'validate' => 'digit_or_csd', 'default' => '' ), // Comma separated string of resource ID, which was used in 'aggregate' parameter.
380 'aggregate_type' => array( 'validate' => 's', 'default' => 'all' ), // 'all' | 'bookings_only' // FixIn: 9.8.15.10.
381 'dates_to_check' => array( 'validate' => 'array', 'default' => '' ), // 'all' | 'bookings_only' // FixIn: 9.8.15.10.
382 'skip_general_availability' => array( 'validate' => 'd', 'default' => 0 ),
383 'wpbc_settings_calendar_preview' => array( 'validate' => 'd', 'default' => 0 ),
384 'wpbc_settings_calendar_preview_changeover' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
385 'wpbc_settings_calendar_preview_triangles' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'On' ),
386 'wpbc_settings_calendar_preview_recurrent_time' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
387 'wpbc_settings_calendar_preview_last_checkout' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
388 'wpbc_settings_calendar_preview_excerpt_on_pages' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
389 'wpbc_settings_calendar_preview_excerpt_pages' => array( 'validate' => 's', 'default' => '' ),
390 'wpbc_settings_calendar_preview_excerpt_resources' => array( 'validate' => 's', 'default' => '' ),
391 'wpbc_settings_calendar_preview_show_legend' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
392 'wpbc_settings_calendar_preview_legend_show_numbers' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
393 'wpbc_settings_calendar_preview_legend_vertical' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
394 'wpbc_settings_calendar_preview_legend_show_available' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
395 'wpbc_settings_calendar_preview_legend_text_available' => array( 'validate' => 's', 'default' => '' ),
396 'wpbc_settings_calendar_preview_legend_show_pending' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
397 'wpbc_settings_calendar_preview_legend_text_pending' => array( 'validate' => 's', 'default' => '' ),
398 'wpbc_settings_calendar_preview_legend_show_approved' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
399 'wpbc_settings_calendar_preview_legend_text_approved' => array( 'validate' => 's', 'default' => '' ),
400 'wpbc_settings_calendar_preview_legend_show_partially' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
401 'wpbc_settings_calendar_preview_legend_text_partially' => array( 'validate' => 's', 'default' => '' ),
402 'wpbc_settings_calendar_preview_legend_show_unavailable' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
403 'wpbc_settings_calendar_preview_legend_text_unavailable' => array( 'validate' => 's', 'default' => '' ),
404 'wpbc_settings_calendar_preview_disable_timeslots_tooltip' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
405 'wpbc_settings_calendar_preview_timeslot_word' => array( 'validate' => 's', 'default' => '' ),
406 'wpbc_settings_calendar_preview_show_availability_tooltip' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
407 'wpbc_settings_calendar_preview_availability_word' => array( 'validate' => 's', 'default' => '' ),
408 'wpbc_settings_calendar_preview_show_availability_cell' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
409 'wpbc_settings_calendar_preview_availability_cell_word' => array( 'validate' => 's', 'default' => '' ),
410 'wpbc_settings_calendar_preview_show_cost_tooltip' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
411 'wpbc_settings_calendar_preview_cost_word' => array( 'validate' => 's', 'default' => '' ),
412 'wpbc_settings_calendar_preview_show_cost_cell' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
413 'wpbc_settings_calendar_preview_cost_currency' => array( 'validate' => 's', 'default' => '' ),
414 'wpbc_settings_calendar_preview_show_booked_details' => array( 'validate' => array( 'On', 'Off' ), 'default' => 'Off' ),
415 'wpbc_settings_calendar_preview_booked_details' => array( 'validate' => 's', 'default' => '' ),
416 'wpbc_setup_wizard_date_selection_preview' => array( 'validate' => 'd', 'default' => 0 ),
417 'wpbc_setup_wizard_date_selection_preview_nonce' => array( 'validate' => 'strong', 'default' => '' ),
418 )
419 )
420 );
421 $request_prefix = 'calendar_request_params';
422 $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['search_params']['resource_id']
423 if ( ! empty( $request_params['classic_booking_context_token'] ) && function_exists( 'wpbc_classic_booking_context_validate_submission' ) ) {
424 $classic_context = wpbc_classic_booking_context_validate_submission(
425 $request_params['classic_booking_context_token'],
426 $request_params['resource_id'],
427 $request_params['dates_to_check'],
428 $request_params['custom_form'],
429 $request_params['aggregate_resource_id_str']
430 );
431 if ( ! is_wp_error( $classic_context ) ) {
432 $request_params['allow_past'] = ! empty( $classic_context['allow_past'] ) ? 1 : 0;
433 // Use the signed canonical set so equivalent wire shapes cannot change availability input.
434 $request_params['aggregate_resource_id_str'] = implode( ',', $classic_context['aggregate_resource_ids'] );
435 $request_params['dates_to_check'] = array(
436 $classic_context['calendar_dates_start'],
437 $classic_context['calendar_dates_end'],
438 );
439 } else {
440 $request_params['allow_past'] = 0;
441 }
442 }
443 $can_allow_past = ! empty( $request_params['allow_past'] );
444 $request_params['allow_past'] = $can_allow_past ? 1 : 0;
445
446 if ( ! empty( $request_params['skip_general_availability'] ) ) {
447 $can_skip_general_availability = (
448 function_exists( 'wpbc_availability_general__get_manage_cap' )
449 && function_exists( 'wpbc_is_mu_user_can_be_here' )
450 && wpbc_is_mu_user_can_be_here( 'only_super_admin' )
451 && current_user_can( wpbc_availability_general__get_manage_cap() )
452 );
453
454 if ( ! $can_skip_general_availability ) {
455 $request_params['skip_general_availability'] = 0;
456 }
457 }
458
459
460 // Do Job here !! -------------------------------------------------------------------------------------------------
461
462 make_bk_action( 'check_pending_unpaid_bookings__do_auto_cancel', $request_params['resource_id'] );
463
464 $skip_booking_id = wpbc_get_booking_id__from_hash_in_url( $request_params['booking_hash'] ); // Booking ID ( like 1101 ) to skip in calendar
465 $max_days_count = wpbc_get_max_visible_days_in_calendar(); // 365 - number of visible days
466 // Resource ID from aggregate parameter in booking shortcode!
467 $aggregate_resource_id_arr = explode( ',', $request_params['aggregate_resource_id_str'] );
468 $aggregate_resource_id_arr = array_filter( $aggregate_resource_id_arr ); // All entries of array equal to FALSE (0, '', '0' ) will be removed.
469 $aggregate_resource_id_arr = array_unique( $aggregate_resource_id_arr ); // Remove duplicates // FixIn: 9.8.15.10.
470 if ( ( $resource_id_key = array_search( $request_params['resource_id'], $aggregate_resource_id_arr ) ) !== false ) {
471 unset( $aggregate_resource_id_arr[ $resource_id_key ] ); // Remove source booking resource from aggregate ARR. // FixIn: 9.8.15.10.
472 }
473 $aggregate_resource_id_arr = array_values($aggregate_resource_id_arr); // Reset keys
474
475 $availability_per_days__params = array(
476 'resource_id' => $request_params['resource_id'],
477 'max_days_count' => $max_days_count,
478 'skip_booking_id' => $skip_booking_id,
479 'request_uri' => $request_params['request_uri'], // It different in Ajax requests than $server_request_uri . It's used for change-over days to detect for exception at specific pages
480 'custom_form' => $request_params['custom_form'],
481 'allow_past' => ! empty( $request_params['allow_past'] )
482 , 'additional_bk_types' => $aggregate_resource_id_arr // It is array of booking resources from aggregate parameter() // arrays | CSD | int // OPTIONAL
483 , 'aggregate_type' => $request_params['aggregate_type'] // It is string: 'all' | 'bookings_only' // OPTIONAL
484 , 'skip_general_availability' => $request_params['skip_general_availability']
485 // , 'as_single_resource' => true // get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources'
486 // , 'max_days_count' => wpbc_get_max_visible_days_in_calendar() // 365
487 // , 'timeslots_to_check_intersect' => $timeslots_to_check_intersect //TODO 2023-10-25: delete it, because we get it in wpbc_get_availability_per_days_arr() //array( '12:20 - 12:55', '13:00 - 14:00' )
488 );
489
490 if ( ! empty( $request_params['dates_to_check'] ) ) {
491 $availability_per_days__params['dates_to_check'] = $request_params['dates_to_check']; // Usually if defined it is: [ '2025-01-01', '2025-12-31' ], because of shortcode [booking resource_id=4 calendar_dates_start='2025-01-01' calendar_dates_end='2025-12-31' ...]
492 }
493
494 // If in Booking > Add booking page we use in URL .../wp-admin/admin.php?page=wpbc&tab=add-booking&booking_type=2&as_single_resource=1 then get availability only for single resource.
495 if (
496 ( ! empty( $request_params['request_uri'] ) )
497 && ( strpos( $request_params['request_uri'], 'as_single_resource=1' ) !== false )
498 ){
499 $availability_per_days__params['as_single_resource'] = true;
500 }
501
502 /**
503 * Tricks:
504 * if ( wpbc_is_new_booking_page_url( $request_params['request_uri'] ) ) { $availability_per_days__params['is_days_always_available'] = true; }
505 *
506 * // Set dates in calendar always available only for specific resources with specific ID
507 * if ( in_array( $request_params['resource_id'], array( 12, 15, 17 ) ) ) { $availability_per_days__params['is_days_always_available'] = true; }
508 */
509
510 // Get unavailable dates for calendar
511 // $availability_per_days__params['dates_to_check'] = array( '2025-01-01', '2025-12-31' ); // FixIn: 10.13.1.4.
512 global $wpbc__skip_get_bk_options;
513
514 $previous_skip_get_bk_options = $wpbc__skip_get_bk_options;
515 $previous_settings_calendar_preview_options = null;
516 $is_settings_calendar_preview = (
517 function_exists( 'wpbc_settings_calendar__is_calendar_load_preview_request_allowed' )
518 && wpbc_settings_calendar__is_calendar_load_preview_request_allowed( $request_params )
519 );
520 if ( ! empty( $availability_per_days__params['skip_general_availability'] ) ) {
521 $wpbc__skip_get_bk_options = array_unique(
522 array_merge(
523 (array) $wpbc__skip_get_bk_options,
524 wpbc__calendar__get_general_availability_option_names()
525 )
526 );
527 }
528 if ( $is_settings_calendar_preview ) {
529 $previous_settings_calendar_preview_options = wpbc_settings_calendar__preview_options_push(
530 wpbc_settings_calendar__get_calendar_load_preview_options( $request_params )
531 );
532 }
533
534 $availability_per_days_arr = wpbc_get_availability_per_days_arr( $availability_per_days__params );
535 $wpbc__skip_get_bk_options = $previous_skip_get_bk_options;
536 if ( $is_settings_calendar_preview ) {
537 wpbc_settings_calendar__preview_options_pop( $previous_settings_calendar_preview_options );
538 }
539
540
541 $ajx_data_arr = array( /**
542 * Send JSON. It will make "wp_json_encode" - so pass only array, and This function call wp_die( '', '', array( 'response' => null, ) ) .
543 * Pass JS OBJ: response_data in "jQuery.post( " function on success.
544 */
545 'ajx_data' => $availability_per_days_arr,
546 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
547 'ajx_search_params' => $_REQUEST[ $request_prefix ],
548 'ajx_cleaned_params' => $request_params,
549 'resource_id' => $request_params['resource_id'],
550 );
551
552 // FixIn: 10.12.4.6.
553 if ( ! in_array( $request_params['resource_id'], $availability_per_days_arr['resources_id_arr__in_dates'] ) ) {
554 $ajx_data_arr['ajx_data']['ajx_after_action_message'] = 'Wrong ID of booking resource. Probably calendar (booking resource) with ID = ' . intval( $request_params['resource_id'] ) . ' not exists. Please check resource_id parameter in the Booking Calendar shortcode.';
555 }
556
557
558
559 // Show message under calendar, if needed.
560 //$availability_per_days_arr['ajx_after_action_message'] = 'Ta Da :))';
561 //$availability_per_days_arr['ajx_after_action_message_status'] = 'warning';
562
563 // Ajax ------------------------------------------------------------------------------------------------------------
564 wp_send_json( $ajx_data_arr );
565
566 }
567
568 if ( is_admin() && ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) {
569 add_action( 'wp_ajax_nopriv_' . 'WPBC_AJX_CALENDAR_LOAD', 'ajax_' . 'WPBC_AJX_CALENDAR_LOAD' ); // Client (not logged in)
570 add_action( 'wp_ajax_' . 'WPBC_AJX_CALENDAR_LOAD', 'ajax_' . 'WPBC_AJX_CALENDAR_LOAD' ); // Logged In users (or admin panel)
571 }
572
573