PluginProbe
Booking Calendar / 10.15.6
Booking Calendar v10.15.6
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 / page-form-builder / field-packs / calendar / calendar.php

calendar.php in Booking Calendar 10.15.6, at includes/page-form-builder/field-packs/calendar/calendar.php

567 lines 23.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPBC BFB Pack: Calendar Field (WP-template–driven with Inspector)
4 *
5 * Purpose
6 * - Registers a "calendar" field pack with schema defaults and usage guard (once per form).
7 * - Prints wp.template() blocks for Preview and Inspector.
8 * - Enqueues the companion JS renderer and passes boot-time options via wp_localize_script().
9 *
10 * Contracts Used
11 * - Filter: wpbc_bfb_register_field_packs
12 * - Action: wpbc_enqueue_js_field_pack
13 * - Action: wpbc_bfb_palette_register_items
14 *
15 * Implementation Notes
16 * - Label "for" now always targets the computed textarea ID (accessibility fix).
17 * - Enqueue is limited to the Builder page slug.
18 * - Calendar boot params keep legacy option keys (historic spellings) for compatibility.
19 * - Minimal inline guard normalizes min_width to px/%/rem/em in the preview template.
20 *
21 * @package Booking Calendar
22 * @author wpdevelop, oplugins
23 * @since 11.0.0
24 * @modified 2025-10-10
25 * @version 1.2.3
26 * @file ../includes/page-form-builder/field-packs/calendar/calendar.php
27 */
28
29 if ( ! defined( 'ABSPATH' ) ) {
30 exit;
31 }
32
33 /**
34 * Class-like wrapper with static methods for better modularity/extendability.
35 * All names use snake_case per project convention.
36 */
37 class WPBC_BFB_Field_Calendar_WPTPL_Pack {
38
39 /**
40 * Bootstrap all hooks.
41 *
42 * @return void
43 */
44 public static function init() {
45 add_filter( 'wpbc_bfb_register_field_packs', array( __CLASS__, 'register_field_packs' ) );
46 add_action( 'wpbc_enqueue_js_field_pack', array( __CLASS__, 'enqueue_js' ), 10, 1 );
47 add_action( 'wpbc_bfb_palette_register_items', array( __CLASS__, 'palette_register_item' ), 10, 2 );
48 }
49
50 /**
51 * Register the "calendar" field pack and its schema.
52 *
53 * @param array $packs Accumulated packs from other modules.
54 *
55 * @return array Modified packs with "calendar".
56 */
57 public static function register_field_packs( $packs ) {
58 $packs['calendar'] = array(
59 'kind' => 'field',
60 'type' => 'calendar',
61 'label' => __( 'Calendar', 'booking' ),
62 'icon' => 'wpbc-bi-calendar3',
63 'usage_key' => 'calendar', // once-per-form guard lives on the client via data-usagenumber="1".
64 'schema' => array(
65 'props' => array(
66 'label' => array( 'type' => 'string', 'default' => __( 'Date', 'booking' ) ),
67 'name' => array( 'type' => 'string', 'default' => '' ),
68 'html_id' => array( 'type' => 'string', 'default' => '' ),
69 'cssclass' => array( 'type' => 'string', 'default' => '' ),
70 'help' => array( 'type' => 'string', 'default' => '' ),
71 'resource_id' => array( 'type' => 'number', 'default' => self::get_preview_resource_id(), 'min' => 1 ),
72 'months' => array( 'type' => 'number', 'default' => 1, 'min' => 1, 'max' => 12 ),
73 'min_width' => array( 'type' => 'string', 'default' => '250px' ),
74 ),
75 ),
76 'templates_printer' => array( __CLASS__, 'print_templates' ),
77 );
78
79 return $packs;
80 }
81
82 /**
83 * Build a minimal, sanitized resources list for client templates.
84 *
85 * Each item: array( 'booking_type_id' => int, 'title' => string, 'parent' => string '0'|id )
86 * Titles are stripped of HTML. Only essential keys are exported to reduce payload size.
87 *
88 * @return array
89 */
90 protected static function get_booking_resources_boot_data() {
91 $resources_boot = array();
92
93 // Booking plugin must be active. Keep hard guard, same as other toolbars.
94 if ( ! class_exists( 'wpdev_bk_personal' ) ) {
95 return $resources_boot;
96 }
97
98 if ( ! function_exists( 'wpbc_ajx_get_all_booking_resources_arr' ) || ! function_exists( 'wpbc_ajx_get_sorted_booking_resources_arr' ) ) {
99 return $resources_boot;
100 }
101
102 $resources_arr = wpbc_ajx_get_all_booking_resources_arr();
103 $resources_arr_sorted = wpbc_ajx_get_sorted_booking_resources_arr( $resources_arr );
104
105 foreach ( $resources_arr_sorted as $resource_item ) {
106 $id = isset( $resource_item['booking_type_id'] ) ? intval( $resource_item['booking_type_id'] ) : 0;
107 if ( $id <= 0 ) {
108 continue;
109 }
110
111 $title_raw = isset( $resource_item['title'] ) ? $resource_item['title'] : '';
112 $title = wp_strip_all_tags( $title_raw, true );
113
114 $parent = isset( $resource_item['parent'] ) ? (string) intval( $resource_item['parent'] ) : '0';
115
116 $resources_boot[] = array(
117 'booking_type_id' => $id,
118 'title' => $title,
119 'parent' => $parent,
120 );
121 }
122
123 /**
124 * Filter the resources list provided to the Calendar Inspector select.
125 *
126 * @param array $resources_boot List of arrays: booking_type_id, title, parent.
127 */
128 return apply_filters( 'wpbc_bfb_calendar_resources_boot', $resources_boot );
129 }
130
131 /**
132 * Enqueue the field JS (calendar renderer) and pass calendar boot options.
133 *
134 * @param string $page Current admin page slug used by the Builder.
135 *
136 * @return void
137 */
138 public static function enqueue_js( $page ) {
139
140 // Ensure we load only on the Builder UI.
141 if ( defined( 'WPBC_BFB_BUILDER_PAGE_SLUG' ) && WPBC_BFB_BUILDER_PAGE_SLUG !== $page ) {
142 return;
143 }
144
145 wp_enqueue_script(
146 'wpbc-bfb_field_calendar_wptpl',
147 wpbc_plugin_url( '/includes/page-form-builder/field-packs/calendar/_out/calendar.js' ),
148 array( 'wp-util', 'wpbc-bfb' ),
149 WP_BK_VERSION_NUM,
150 true
151 );
152
153 // Current request URI (used for AJAX routing/diagnostics).
154 $request_uri = '';
155 if ( isset( $_SERVER['REQUEST_URI'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
156 $request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
157 }
158
159 // Parallelism for calendar loader (defaults to 1).
160 $balancer_max_threads = intval( get_bk_option( 'booking_load_balancer_max_threads' ) );
161 $balancer_max_threads = ( empty( $balancer_max_threads ) ) ? 1 : $balancer_max_threads;
162
163 // Day-selection defaults (allow override from core helper if present).
164 $days_selection = array(
165 'days_select_mode' => 'multiple',
166 'fixed__days_num' => 0,
167 'fixed__week_days__start' => '-1',
168 'dynamic__days_min' => 0,
169 'dynamic__days_max' => 0,
170 'dynamic__days_specific' => '',
171 'dynamic__week_days__start' => '-1',
172 );
173 if ( function_exists( 'wpbc__calendar__js_params__get_days_selection_arr' ) ) {
174 $days_selection = wpbc__calendar__js_params__get_days_selection_arr();
175 }
176
177 /**
178 * Enterprise/business options — kept for compatibility.
179 * Note: We intentionally keep legacy option names (typos included) because related
180 * client code expects them. Do not rename without updating JS consumer logic.
181 */
182 $is_parent_resource = 0; // Placeholder: 0 means "single resource mode" preview.
183 $booking_capacity_field = '';
184 $booking_is_different_subres_disabled = '';
185 if ( class_exists( 'wpdev_bk_biz_l' ) ) {
186 $booking_capacity_field = function_exists( 'wpbc_get__booking_capacity_field__name' ) ? wpbc_get__booking_capacity_field__name() : '';
187 $booking_is_different_subres_disabled = get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' );
188 }
189
190 $booking_recurrent_time = '';
191 if ( class_exists( 'wpdev_bk_biz_s' ) ) {
192 // No esc_js() — wp_localize_script() JSON-encodes safely.
193 $booking_recurrent_time = (string) get_bk_option( 'booking_recurrent_time' );
194 }
195
196 // Boot payload for real booking resources (used by the Inspector select).
197 $booking_resources = self::get_booking_resources_boot_data();
198 $is_free_version = ( class_exists( 'wpdev_bk_personal' ) ) ? 0 : 1;
199
200 wp_localize_script(
201 'wpbc-bfb_field_calendar_wptpl',
202 'WPBC_BFB_CalendarBoot',
203 array(
204 'nonce' => wp_create_nonce( 'wpbc_calendar_load_ajx' . '_wpbcnonce' ),
205 'user_id' => (int) wpbc_get_current_user_id(),
206 'locale' => (string) get_user_locale(),
207 'request_uri' => (string) $request_uri,
208
209 // Core calendar env.
210 'balancer_max_threads' => (int) $balancer_max_threads,
211 'booking_max_monthes_in_calendar' => (string) get_bk_option( 'booking_max_monthes_in_calendar' ),
212 'booking_start_day_weeek' => (string) get_bk_option( 'booking_start_day_weeek' ),
213 'booking_date_format' => (string) get_bk_option( 'booking_date_format' ),
214 'booking_time_format' => (string) get_bk_option( 'booking_time_format' ),
215
216 'default_preview_resource_id' => (int) self::get_preview_resource_id(),
217
218 // Day-selection policy.
219 'days_selection' => array(
220 'days_select_mode' => (string) $days_selection['days_select_mode'],
221 'fixed__days_num' => (int) $days_selection['fixed__days_num'],
222 'fixed__week_days__start' => (string) $days_selection['fixed__week_days__start'],
223 'dynamic__days_min' => (int) $days_selection['dynamic__days_min'],
224 'dynamic__days_max' => (int) $days_selection['dynamic__days_max'],
225 'dynamic__days_specific' => (string) $days_selection['dynamic__days_specific'],
226 'dynamic__week_days__start' => (string) $days_selection['dynamic__week_days__start'],
227 ),
228
229 // Enterprise/business flags.
230 'is_parent_resource' => (int) $is_parent_resource,
231 'booking_capacity_field' => (string) $booking_capacity_field,
232 'booking_is_dissbale_booking_for_different_sub_resources' => (string) $booking_is_different_subres_disabled,
233 'booking_recurrent_time' => (string) $booking_recurrent_time,
234
235 // Real booking resources for Inspector select.
236 'booking_resources' => $booking_resources,
237 'is_free_version' => (int) $is_free_version,
238 )
239 );
240 }
241
242 /**
243 * Print Preview & Inspector templates (Underscore.js) on the Builder page.
244 *
245 * @param string $page Current admin page slug used by the Builder.
246 *
247 * @return void
248 */
249 public static function print_templates( $page ) {
250
251 if ( defined( 'WPBC_BFB_BUILDER_PAGE_SLUG' ) && WPBC_BFB_BUILDER_PAGE_SLUG !== $page ) {
252 return;
253 }
254
255 // == CANVAS PREVIEW ==
256 ?>
257 <script type="text/html" id="tmpl-wpbc-bfb-field-calendar">
258 <#
259 // Normalize booleans and numbers defensively.
260 var __boot_resources = ( window.WPBC_BFB_CalendarBoot && Array.isArray( window.WPBC_BFB_CalendarBoot.booking_resources ) ) ? window.WPBC_BFB_CalendarBoot.booking_resources : [];
261 var __configured_rid = Number( ( window.WPBC_BFB_CalendarBoot && window.WPBC_BFB_CalendarBoot.default_preview_resource_id ) || 1 );
262 var __data_rid = Number( data.resource_id || 1 );
263
264 function __resource_exists( id ) {
265 id = Number( id || 1 );
266 if ( ! isFinite( id ) || id <= 0 ) {
267 return false;
268 }
269 for ( var i = 0; i < __boot_resources.length; i++ ) {
270 if ( Number( __boot_resources[i].booking_type_id ) === id ) {
271 return true;
272 }
273 }
274 return false;
275 }
276 function __first_existing_resource_id() {
277 for ( var i = 0; i < __boot_resources.length; i++ ) {
278 var id = Number( __boot_resources[i].booking_type_id );
279 if ( isFinite( id ) && id > 0 ) {
280 return id;
281 }
282 }
283 return 1;
284 }
285 var rid = __resource_exists( __configured_rid ) ? __configured_rid : ( __resource_exists( __data_rid ) ? __data_rid : __first_existing_resource_id() );
286
287
288 var months = Math.max(1, Math.min(12, Number(data.months || 1)));
289
290 // Compute textarea ID/name: calendar requires 'date_booking{rid}' by convention.
291 var input_id = (data.html_id && String(data.html_id).trim()) ? data.html_id : ('date_booking' + rid);
292 var input_name = (data.name && String(data.name).trim()) ? data.name : ('date_booking' + rid);
293
294 // Guard CSS length: allow px, %, rem, em; fallback to 250px.
295 var minw = (function(v){
296 var s = String(v || '').trim();
297 var m = s.match(/^(-?\d+(?:\.\d+)?)(px|%|rem|em)$/i);
298 return m ? m[0] : '250px';
299 })( data.min_width );
300 #>
301
302 <span class="wpbc_bfb__noaction wpbc_bfb__no-drag-zone {{ data.cssclass || '' }}" inert=""
303 style="display:block; min-width: {{ minw }};">
304 <# if ( data.label && data.label !== '' ) { #>
305 <label class="wpbc_bfb__field-label" for="{{ input_id }}">
306 {{ data.label }}
307 </label>
308 <# } #>
309
310 <div class="wpbc_calendar_wraper wpbc_change_over_triangle">
311 <div class="wpbc_cal_container bk_calendar_frame wpbc_no_custom_width months_num_in_row_ cal_month_num_{{ months }}">
312 <div id="calendar_booking{{ rid }}"><?php esc_html_e( 'Calendar is loading', 'booking' ); ?>...</div>
313 </div>
314 </div>
315
316 <textarea rows="3" cols="50" id="{{ input_id }}" name="{{ input_name }}" autocomplete="off" style="display:none;" tabindex="-1" aria-hidden="true"></textarea>
317 <?php
318 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
319 echo wpbc_get_calendar_legend();
320 ?>
321 <# if ( data.help ) { #>
322 <div class="wpbc_bfb__help">{{ data.help }}</div>
323 <# } #>
324 </span>
325 </script>
326 <?php
327
328 // == INSPECTOR ==
329 ?>
330 <script type="text/html" id="tmpl-wpbc-bfb-inspector-calendar">
331 <div class="wpbc_bfb__inspector__head">
332 <div class="header_container">
333 <div class="header_title_content">
334 <h3 class="title"><?php echo esc_html__( 'Calendar', 'booking' ); ?></h3>
335 <div class="desc"><?php echo esc_html__( 'Configure the date picker calendar. Some options affect only the preview.', 'booking' ); ?></div>
336 </div>
337 <div class="actions wpbc_ajx_toolbar wpbc_no_borders">
338 <div class="ui_container ui_container_small">
339 <div class="ui_group">
340 <div class="ui_element">
341 <button type="button" class="button button-secondary wpbc_ui_control wpbc_ui_button"
342 data-action="deselect" aria-label="<?php echo esc_attr__( 'Deselect', 'booking' ); ?>">
343 <i class="menu_icon icon-1x wpbc_icn_remove_done"></i>
344 </button>
345 <button type="button" class="button button-secondary wpbc_ui_control wpbc_ui_button"
346 data-action="scrollto" aria-label="<?php echo esc_attr__( 'Scroll to field', 'booking' ); ?>">
347 <i class="menu_icon icon-1x wpbc_icn_ads_click filter_center_focus"></i>
348 </button>
349 </div>
350 <div class="ui_element">
351 <button type="button" class="button button-secondary wpbc_ui_control wpbc_ui_button"
352 data-action="move-up" aria-label="<?php echo esc_attr__( 'Move up', 'booking' ); ?>">
353 <i class="menu_icon icon-1x wpbc_icn_arrow_upward"></i>
354 </button>
355 <button type="button" class="button button-secondary wpbc_ui_control wpbc_ui_button"
356 data-action="move-down" aria-label="<?php echo esc_attr__( 'Move down', 'booking' ); ?>">
357 <i class="menu_icon icon-1x wpbc_icn_arrow_downward"></i>
358 </button>
359 </div>
360 <div class="ui_element">
361 <button data-action="delete" aria-label="<?php echo esc_attr__( 'Delete', 'booking' ); ?>" type="button"
362 class="button button-secondary wpbc_ui_control wpbc_ui_button wpbc_ui_button_danger button-link-delete">
363 <span class="in-button-text"><?php echo esc_html__( 'Delete', 'booking' ); ?></span>&nbsp;&nbsp;<i class="menu_icon icon-1x wpbc_icn_delete_outline"></i>
364 </button>
365 </div>
366 </div>
367 </div>
368 </div><!-- .actions -->
369 </div>
370 </div>
371
372 <div class="wpbc_bfb__inspector__body">
373
374 <section class="wpbc_bfb__inspector__group wpbc_ui__collapsible_group is-open" data-group="basic">
375 <button type="button" class="group__header">
376 <h3><?php echo esc_html__( 'Basic', 'booking' ); ?></h3>
377 <i class="wpbc_ui_el__vert_menu_root_section_icon menu_icon icon-1x wpbc-bi-chevron-right"></i>
378 </button>
379 <div class="group__fields">
380 <div class="inspector__row">
381 <label class="inspector__label"><?php echo esc_html__( 'Label', 'booking' ); ?></label>
382 <div class="inspector__control">
383 <input type="text" class="inspector__input" data-inspector-key="label"
384 value="{{ data.label || '<?php echo esc_js( __( 'Date', 'booking' ) ); ?>' }}">
385 </div>
386 </div>
387
388 <div class="inspector__row">
389 <label class="inspector__label"><?php echo esc_html__( 'Help text', 'booking' ); ?></label>
390 <div class="inspector__control">
391 <textarea class="inspector__textarea" rows="3" data-inspector-key="help">{{ data.help || '' }}</textarea>
392 </div>
393 </div>
394 </div>
395 </section>
396 <?php /* ?>
397 <section class="wpbc_bfb__inspector__group wpbc_ui__collapsible_group is-open" data-group="calendar">
398 <button type="button" class="group__header">
399 <h3><?php echo esc_html__( 'Calendar', 'booking' ); ?></h3>
400 <i class="wpbc_ui_el__vert_menu_root_section_icon menu_icon icon-1x wpbc-bi-chevron-right"></i>
401 </button>
402 <div class="group__fields">
403
404 <!-- Resource selector with Free-version guard -->
405 <div class="inspector__row">
406 <label class="inspector__label"><?php echo esc_html__( 'Booking resource', 'booking' ); ?></label>
407 <div class="inspector__control">
408 <#
409 var __boot = window.WPBC_BFB_CalendarBoot || {};
410 var __free = (__boot.is_free_version==='1');
411 var __res = (__boot.booking_resources) ? __boot.booking_resources : [];
412 var __has_list = (__res && __res.length > 0);
413 var __rid = String( data.resource_id || '1' );
414
415 // If Free -> enforce ID=1 and persist to data.
416 if ( __free ) {
417 __rid = '1';
418 data.resource_id = __rid;
419 }
420 #>
421 <# if ( __free ) { #>
422 <input type="hidden" class="inspector__input" data-inspector-key="resource_id" value="1">
423 <p class="wpbc_bfb__help">
424 <?php
425 echo esc_html__(
426 'The Free version has one default booking resource. To have multiple resources, please upgrade to a premium version.',
427 'booking'
428 );
429 ?>
430 </p>
431 <# } else if ( __has_list ) { #>
432 <select class="inspector__input" data-inspector-key="resource_id">
433 <#
434 // Coerce current value to an existing one if missing.
435 var __exists = _.some( __res, function(r){ return String(r.booking_type_id) === __rid; } );
436 if ( ! __exists ) { __rid = String( __res[0].booking_type_id ); data.resource_id = __rid; }
437
438 _.each( __res, function( r ){
439 var is_parent = ( String(r.parent||'0') === '0' );
440 var selected = ( __rid === String(r.booking_type_id) );
441 #>
442 <option value="{{ r.booking_type_id }}"
443 <# if ( selected ) { #>selected="selected"<# } #>
444 style="<# if ( is_parent ) { #>font-weight:600;<# } else { #>font-size:0.95em;padding-left:20px;<# } #>">
445 <# if ( ! is_parent ) { #>&nbsp;&nbsp;&nbsp;<# } #>{{ r.title }}
446 </option>
447 <# }); #>
448 </select>
449 <p class="wpbc_bfb__help"><?php echo esc_html__( 'Choose booking resource for calendar preview ', 'booking' ); ?></p>
450 <# } else { #>
451 <input type="hidden" class="inspector__input inspector__w_30" data-inspector-key="resource_id" value="{{ data.resource_id || 1 }}">
452 <p class="wpbc_bfb__help"><?php echo esc_html__( 'No resources available. Create booking resources on WP Booking Calendar > Resources > Resources page.', 'booking' ); ?></p>
453 <# } #>
454 </div>
455 </div>
456
457 <div class="inspector__row">
458 <label class="inspector__label"><?php echo esc_html__( 'Months in row', 'booking' ); ?></label>
459 <div class="inspector__control">
460 <div class="wpbc_len_group wpbc_inline_inputs" data-len-group="calendar-months">
461 <input data-len-range type="range" class="inspector__input" min="1" max="12"
462 step="1" data-inspector-key="months" value="{{ data.months || 1 }}">
463 <input data-len-value type="number" class="inspector__input inspector__w_30"
464 data-inspector-key="months" min="1" max="12" value="{{ data.months || 1 }}">
465 </div>
466 <p class="wpbc_bfb__help"><?php echo esc_html__( 'Preview hint: multiple months may require full calendar init in preview.', 'booking' ); ?></p>
467 </div>
468 </div>
469
470 </div>
471 </section>
472
473 <section class="wpbc_bfb__inspector__group wpbc_ui__collapsible_group" data-group="advanced">
474 <button type="button" class="group__header">
475 <h3><?php echo esc_html__( 'Advanced', 'booking' ); ?></h3>
476 <i class="wpbc_ui_el__vert_menu_root_section_icon menu_icon icon-1x wpbc-bi-chevron-right"></i>
477 </button>
478 <div class="group__fields">
479
480 <div class="inspector__row">
481 <label class="inspector__label"><?php echo esc_html__( 'CSS class', 'booking' ); ?></label>
482 <div class="inspector__control">
483 <input type="text" class="inspector__input" data-inspector-key="cssclass"
484 value="{{ data.cssclass || '' }}">
485 </div>
486 </div>
487
488 <?php
489 // HTML ID must be 'date_booking{rid}' (rid = resource id) to bind with the textarea under the calendar.
490 // This field is intentionally non-editable in Inspector to avoid breaking loader contracts.
491 // If you must expose it, ensure builder-side enforcement keeps the 'date_booking{rid}' pattern.
492 ?>
493
494 </div>
495 </section>
496 <?php */ ?>
497 </div>
498 </script>
499 <?php
500 }
501
502 /**
503 * Register the palette item (draggable field entry).
504 *
505 * @param string $group Palette group slug.
506 * @param string $position Position within group.
507 *
508 * @return void
509 */
510 public static function palette_register_item( $group, $position ) {
511 if ( 'essentials' !== $group || 'top' !== $position ) {
512 return;
513 }
514 ?>
515 <li class="wpbc_bfb__field"
516 data-id="calendar"
517 data-type="calendar"
518 data-usage_key="calendar"
519 data-usagenumber="1"
520 data-resource_id="<?php echo intval( self::get_preview_resource_id() ); ?>"
521 data-months="1"
522 data-label="<?php echo esc_attr( __( 'Calendar', 'booking' ) ); ?>"
523 data-min_width="250px">
524 <i class="menu_icon icon-1x wpbc-bi-calendar3"></i>
525 <span class="wpbc_bfb__field-label"><?php echo esc_html( __( 'Calendar', 'booking' ) ); ?></span>
526 <span class="wpbc_bfb__field-type">calendar</span>
527 </li>
528 <?php
529 }
530
531 /**
532 * Get preview resource ID for Builder calendar field.
533 *
534 * Uses the configured default resource if it exists in available resources.
535 * Falls back to the first existing resource ID.
536 *
537 * @return int
538 */
539 protected static function get_preview_resource_id() {
540 $resources_boot = self::get_booking_resources_boot_data();
541 $existing_ids = array();
542
543 foreach ( $resources_boot as $resource_item ) {
544 if ( ! empty( $resource_item['booking_type_id'] ) ) {
545 $existing_ids[] = intval( $resource_item['booking_type_id'] );
546 }
547 }
548
549 $existing_ids = array_values( array_filter( array_unique( $existing_ids ) ) );
550
551 $default_rid = intval( wpbc_get_default_resource() );
552
553 if ( in_array( $default_rid, $existing_ids, true ) ) {
554 return $default_rid;
555 }
556
557 if ( ! empty( $existing_ids ) ) {
558 return intval( $existing_ids[0] );
559 }
560
561 return 1;
562 }
563 }
564
565 // Bootstrap.
566 WPBC_BFB_Field_Calendar_WPTPL_Pack::init();
567