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
← All changes | includes/page-add-booking/add_booking__page.php +1188 -107 11.3 → 11.9 View file →
@@ -1,107 +1,1188 @@
1 -<?php /**
2 - * @version 1.0
3 - * @package Booking Calendar
4 - * @category Content of Add New Booking
5 - * @author wpdevelop
6 - *
7 - * @web-site https://wpbookingcalendar.com/
8 - * @email [email protected]
9 - *
10 - * @modified 2015-10-31
11 - */
12 -
13 -if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
14 -
15 -
16 -/**
17 - * Show Content
18 - * Update Content
19 - * Define Slug
20 - * Define where to show
21 - */
22 -class WPBC_Page_AddNewBooking extends WPBC_Page_Structure {
23 -
24 -
25 - public function in_page() {
26 - return 'wpbc';
27 - }
28 -
29 - public function tabs() {
30 -
31 - $is_can_add_booking = WPBC_Add_Booking_Component::current_user_can_add_booking();
32 -
33 - $tabs = array();
34 - $tabs['add-booking'] = array(
35 - 'is_show_top_path' => true, // true | false. By default value is: true.
36 - 'is_show_top_navigation' => true,
37 - 'left_navigation__default_view_mode' => 'min', // '' | 'min' | 'compact' | 'max' | 'none'. By default value is: ''.
38 - 'page_title' => __( 'Add New Booking', 'booking' ), // Header - Title. If false, than hidden.
39 - 'page_description' => __( 'Manually add new bookings from the Admin Panel.', 'booking' ), // Header - Title Description. If false, than hidden.
40 - 'title' => __( 'Add booking', 'booking' ), // Title of TAB.
41 - 'hint' => __( 'Add booking', 'booking' ), // Hint.
42 - 'link' => '', // Can be skiped, then generated link based on Page and Tab tags. Or can be extenral link.
43 - 'position' => '', // Can be: 'left' | 'right' | ''.
44 - 'css_classes' => '', // this is CSS class(es).
45 - 'icon' => '', // Icon - link to the real PNG img.
46 - 'font_icon' => 'wpbc-bi-plus', // CSS definition of forn Icon.
47 - 'default' => true, // Is this tab activated by default or not: true || false.
48 - 'disabled' => ! $is_can_add_booking, // Is this tab disbaled: true || false.
49 - 'hided' => ! $is_can_add_booking, // Is this tab hided: true || false.
50 - 'subtabs' => array(),
51 - );
52 -
53 - return $tabs;
54 - }
55 -
56 -
57 - public function content() {
58 -
59 - do_action( 'wpbc_hook_add_booking_page_header', 'add_booking' ); // Define Notices Section and show some static messages, if needed.
60 -
61 - if ( ! wpbc_is_mu_user_can_be_here( 'activated_user' ) ) {
62 - return false; // Check if MU user activated, otherwise show Warning message.
63 - }
64 -
65 - if ( ! WPBC_Add_Booking_Component::current_user_can_add_booking() ) {
66 - return false;
67 - }
68 -
69 - if ( ! wpbc_set_default_resource_to__get() ) {
70 - return false; // Define default booking resources for $_ GET and check if booking resource belong to user.
71 - }
72 -
73 - WPBC_Add_Booking_Component::render();
74 -
75 - do_action( 'wpbc_hook_add_booking_page_footer', 'add_booking' );
76 - }
77 -
78 -
79 - /**
80 - * Get Calendar Options of specific User
81 - *
82 - * @return array (number of months, options parameter
83 - */
84 - function get_saved_user_calendar_options() {
85 -
86 - return WPBC_Add_Booking_Component::get_saved_user_calendar_options();
87 - }
88 -
89 -}
90 -add_action('wpbc_menu_created', array( new WPBC_Page_AddNewBooking() , '__construct') ); // Executed after creation of Menu
91 -
92 -/**
93 - * Redirect legacy/main-menu Add Booking page to the Add Booking tab under Bookings.
94 - *
95 - * @param string $page_tag Current menu page tag.
96 - *
97 - * @return void
98 - */
99 -function wpbc_add_booking_menu_page__redirect_to_add_booking_tab( $page_tag ) {
100 -
101 - if ( 'wpbc-new' !== $page_tag ) {
102 - return;
103 - }
104 -
105 - wpbc_redirect( wpbc_get_new_booking_url() );
106 -}
107 -add_action( 'wpbc_page_structure_show', 'wpbc_add_booking_menu_page__redirect_to_add_booking_tab', 0 );
1 +<?php /**
2 + * @version 1.0
3 + * @package Booking Calendar
4 + * @category Content of Add New Booking
5 + * @author wpdevelop
6 + *
7 + * @web-site https://wpbookingcalendar.com/
8 + * @email [email protected]
9 + *
10 + * @modified 2015-10-31
11 + */
12 +
13 +if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
14 +
15 +
16 +/**
17 + * Check whether the Add Booking inspector interface is enabled.
18 + *
19 + * The interface is part of 11.4.4 and is enabled by default. The filter keeps
20 + * a narrow emergency fallback to the legacy toolbar for integrations that
21 + * need additional migration time; it is not connected to the 11.5 flag.
22 + *
23 + * @return bool True when the 11.4.4 inspector interface should be used.
24 + */
25 +function wpbc_add_booking_page_is_inspector_enabled() {
26 +
27 + return (bool) apply_filters( 'wpbc_add_booking_page_is_inspector_enabled', true );
28 +}
29 +
30 +
31 +/**
32 + * Check whether the current request is the Add Booking administrator tab.
33 + *
34 + * @return bool True on WP Booking Calendar > Add Booking with the inspector UI enabled.
35 + */
36 +function wpbc_add_booking_page_is_active() {
37 +
38 + if ( ! is_admin() || ! wpbc_add_booking_page_is_inspector_enabled() ) {
39 + return false;
40 + }
41 +
42 + $page = isset( $_GET['page'] ) && ! is_array( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
43 + $tab = isset( $_GET['tab'] ) && ! is_array( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
44 +
45 + return 'wpbc' === $page && 'add-booking' === $tab;
46 +}
47 +
48 +
49 +/**
50 + * Build a safe Add Booking URL while preserving the current resource, form,
51 + * editing context, and calendar layout parameters.
52 + *
53 + * @param bool $allow_past Whether the resulting page should allow past dates.
54 + *
55 + * @return string Add Booking administrator URL.
56 + */
57 +function wpbc_add_booking_page_get_allow_past_url( $allow_past ) {
58 +
59 + $url = wpbc_get_new_booking_url__base( array( 'allow_past' ) );
60 +
61 + // Match the legacy toolbar when returning from a payment result state.
62 + if ( ! empty( $_GET['is_show_payment_form'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
63 + $url = add_query_arg( 'is_show_payment_form', 'Off', $url );
64 + }
65 +
66 + if ( $allow_past ) {
67 + $url = add_query_arg( 'allow_past', '1', $url );
68 + }
69 +
70 + return $url;
71 +}
72 +
73 +
74 +/**
75 + * Resolve a generated page asset, with a source fallback for development.
76 + *
77 + * Release packages use `_out`. The fallback keeps the page usable immediately
78 + * after a source checkout and disappears automatically after asset compilation.
79 + *
80 + * @param string $file_name Asset file name without a directory.
81 + *
82 + * @return string Asset URL.
83 + */
84 +function wpbc_add_booking_page_get_asset_url( $file_name ) {
85 +
86 + $file_name = sanitize_file_name( $file_name );
87 + $folder = file_exists( __DIR__ . '/_out/' . $file_name ) ? '_out' : '_src';
88 +
89 + return trailingslashit( plugins_url( '', __FILE__ ) ) . $folder . '/' . $file_name;
90 +}
91 +
92 +
93 +/**
94 + * Enqueue source-built styles for the Add Booking inspector page only.
95 + *
96 + * @param string $where_to_load Booking Calendar asset context.
97 + *
98 + * @return void
99 + */
100 +function wpbc_add_booking_page_enqueue_css( $where_to_load ) {
101 +
102 + if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_add_booking_page_is_active() ) {
103 + return;
104 + }
105 +
106 + wp_enqueue_style(
107 + 'wpbc-add-booking-page',
108 + wpbc_add_booking_page_get_asset_url( 'add_booking_page.css' ),
109 + array( 'wpbc-all-admin' ),
110 + WP_BK_VERSION_NUM
111 + );
112 +}
113 +add_action( 'wpbc_enqueue_css_files', 'wpbc_add_booking_page_enqueue_css', 73 );
114 +
115 +
116 +/**
117 + * Enqueue the inspector tab controller for the Add Booking page only.
118 + *
119 + * @param string $where_to_load Booking Calendar asset context.
120 + *
121 + * @return void
122 + */
123 +function wpbc_add_booking_page_enqueue_js( $where_to_load ) {
124 +
125 + if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_add_booking_page_is_active() ) {
126 + return;
127 + }
128 +
129 + wp_enqueue_script(
130 + 'wpbc-add-booking-page',
131 + wpbc_add_booking_page_get_asset_url( 'add_booking_page.js' ),
132 + array( 'jquery', 'wpbc_all' ),
133 + WP_BK_VERSION_NUM,
134 + array( 'in_footer' => WPBC_JS_IN_FOOTER )
135 + );
136 +}
137 +add_action( 'wpbc_enqueue_js_files', 'wpbc_add_booking_page_enqueue_js', 73 );
138 +
139 +
140 +/**
141 + * Show Content
142 + * Update Content
143 + * Define Slug
144 + * Define where to show
145 + */
146 +class WPBC_Page_AddNewBooking extends WPBC_Page_Structure {
147 +
148 +
149 + /**
150 + * Published Booking Forms available to the current user.
151 + *
152 + * The value is loaded lazily because both the setup summary and selector use
153 + * the same list during one page render.
154 + *
155 + * @var array<string,array<string,mixed>>|null
156 + */
157 + private $available_booking_forms = null;
158 +
159 +
160 + /**
161 + * Place Add Booking under the shared Bookings page.
162 + *
163 + * @return string Parent page slug.
164 + */
165 + public function in_page() {
166 + return 'wpbc';
167 + }
168 +
169 + /**
170 + * Register the page tab and its 11.4.4 inspector shell.
171 + *
172 + * @return array<string,array<string,mixed>> Page tab definition.
173 + */
174 + public function tabs() {
175 +
176 + $is_can_add_booking = WPBC_Add_Booking_Component::current_user_can_add_booking();
177 + $is_inspector_ui = wpbc_add_booking_page_is_inspector_enabled();
178 +
179 + $tabs = array();
180 + $tabs['add-booking'] = array(
181 + 'is_show_top_path' => true, // true | false. By default value is: true.
182 + 'is_show_top_navigation' => true,
183 + 'left_navigation__default_view_mode' => '', // FixIn:11.7.1.1: 'min', // '' | 'min' | 'compact' | 'max' | 'none'. By default value is: ''.
184 + 'right_vertical_sidebar__is_show' => $is_inspector_ui,
185 + 'right_vertical_sidebar__default_view_mode' => '',
186 + 'right_vertical_sidebar_compact__is_show' => $is_inspector_ui,
187 + 'page_title' => __( 'Add New Booking', 'booking' ), // Header - Title. If false, than hidden.
188 + 'page_description' => __( 'Manually add new bookings from the Admin Panel.', 'booking' ), // Header - Title Description. If false, than hidden.
189 + 'title' => __( 'Add booking', 'booking' ), // Title of TAB.
190 + 'hint' => __( 'Add booking', 'booking' ), // Hint.
191 + 'link' => '', // Can be skiped, then generated link based on Page and Tab tags. Or can be extenral link.
192 + 'position' => '', // Can be: 'left' | 'right' | ''.
193 + 'css_classes' => '', // this is CSS class(es).
194 + 'icon' => '', // Icon - link to the real PNG img.
195 + 'font_icon' => 'wpbc-bi-plus', // CSS definition of forn Icon.
196 + 'default' => true, // Is this tab activated by default or not: true || false.
197 + 'disabled' => ! $is_can_add_booking, // Is this tab disbaled: true || false.
198 + 'hided' => ! $is_can_add_booking, // Is this tab hided: true || false.
199 + 'subtabs' => array(),
200 + );
201 +
202 + return $tabs;
203 + }
204 +
205 +
206 + /**
207 + * Render the compact Settings and Help controls for the right inspector.
208 + *
209 + * @return void
210 + */
211 + public function right_sidebar_compact_content() {
212 +
213 + if ( ! wpbc_add_booking_page_is_inspector_enabled() ) {
214 + return;
215 + }
216 +
217 + $tabs = array(
218 + array(
219 + 'id' => 'wpbc_tab_add_booking_settings',
220 + 'panel_id' => 'wpbc_add_booking__inspector_settings',
221 + 'title' => __( 'Settings', 'booking' ),
222 + 'icon' => 'wpbc_icn_tune',
223 + 'selected' => true,
224 + ),
225 + array(
226 + 'id' => 'wpbc_tab_add_booking_help',
227 + 'panel_id' => 'wpbc_add_booking__inspector_help',
228 + 'title' => __( 'Help', 'booking' ),
229 + 'icon' => 'wpbc-bi-info-circle',
230 + ),
231 + );
232 + $tabs = (array) apply_filters( 'wpbc_admin_add_booking_inspector_tabs', $tabs );
233 +
234 + WPBC_UI_Sidebar_Panels::render_rightbar_tabs(
235 + $tabs,
236 + array(
237 + 'aria_label' => __( 'Add Booking Panels', 'booking' ),
238 + 'context' => 'add_booking',
239 + 'class' => 'wpbc_add_booking__rightbar_tabs',
240 + )
241 + );
242 + }
243 +
244 +
245 + /**
246 + * Render the Builder-style right inspector panels.
247 + *
248 + * @return void
249 + */
250 + public function right_sidebar_content() {
251 +
252 + if ( ! wpbc_add_booking_page_is_inspector_enabled() ) {
253 + return;
254 + }
255 + ?>
256 + <div class="wpbc_bfb__panel--library wpbc_rightbar_palette wpbc_add_booking__rightbar">
257 + <?php
258 + WPBC_UI_Sidebar_Panels::render_panel(
259 + array(
260 + 'id' => 'wpbc_add_booking__inspector_settings',
261 + 'labelledby' => 'wpbc_tab_add_booking_settings',
262 + 'class' => 'wpbc_add_booking__inspector_settings',
263 + ),
264 + array( $this, 'render_settings_panel' )
265 + );
266 + WPBC_UI_Sidebar_Panels::render_panel(
267 + array(
268 + 'id' => 'wpbc_add_booking__inspector_help',
269 + 'labelledby' => 'wpbc_tab_add_booking_help',
270 + 'class' => 'wpbc_add_booking__inspector_help',
271 + 'hidden' => true,
272 + ),
273 + array( $this, 'render_help_panel' )
274 + );
275 + do_action( 'wpbc_admin_add_booking_inspector_panels', $this );
276 + ?>
277 + </div>
278 + <?php
279 + }
280 +
281 +
282 + /**
283 + * Render all controls formerly shown in the Add Booking top toolbar.
284 + *
285 + * Native inspector markup preserves the established IDs, reload URLs, AJAX
286 + * save endpoint, MultiUser visibility rules, and commercial behavior.
287 + *
288 + * @return void
289 + */
290 + public function render_settings_panel() {
291 +
292 + WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Booking Settings', 'booking' ), __( 'Configure this manual Booking and its calendar view.', 'booking' ) );
293 + ?>
294 + <div class="wpbc_bfb__inspector__body wpbc_add_booking__inspector_body">
295 + <div class="wpbc_add_booking__top_actions wpbc_bfb__inspector__section--content wpbc_ui_el__buttons_group">
296 + <?php if ( function_exists( 'wpbc_toolbar_btn__auto_fill' ) ) : ?>
297 + <?php wpbc_toolbar_btn__auto_fill( array( 'submit_after_fill' => false ) ); ?>
298 + <?php endif; ?>
299 + <?php wpbc_toolbar_btn__add_new_booking(); ?>
300 + <?php do_action( 'wpbc_admin_add_booking_primary_actions', $this ); ?>
301 + </div>
302 + <?php $this->render_booking_setup_summary(); ?>
303 + <?php
304 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
305 + array(
306 + 'id' => 'wpbc_add_booking_setup_group',
307 + 'group' => 'add-booking-setup',
308 + 'title' => __( 'Booking setup', 'booking' ),
309 + 'open' => false,
310 + ),
311 + array( $this, 'render_booking_setup_controls' )
312 + );
313 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
314 + array(
315 + 'id' => 'wpbc_add_booking_date_selection_group',
316 + 'group' => 'add-booking-date-selection',
317 + 'title' => __( 'Date selection', 'booking' ),
318 + 'open' => false,
319 + ),
320 + array( $this, 'render_date_selection_controls' )
321 + );
322 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
323 + array(
324 + 'id' => 'wpbc_add_booking_tools_group',
325 + 'group' => 'add-booking-tools',
326 + 'title' => __( 'Booking tools', 'booking' ),
327 + 'open' => false,
328 + ),
329 + array( $this, 'render_booking_tools_controls' )
330 + );
331 + WPBC_UI_Sidebar_Panels::render_collapsible_group(
332 + array(
333 + 'id' => 'wpbc_add_booking_calendar_view_group',
334 + 'group' => 'add-booking-calendar-view',
335 + 'title' => __( 'Calendar view', 'booking' ),
336 + 'open' => false,
337 + ),
338 + array( $this, 'render_calendar_view_controls' )
339 + );
340 + do_action( 'wpbc_admin_add_booking_settings_panel', $this );
341 + ?>
342 + </div>
343 + <?php
344 + }
345 +
346 +
347 + /**
348 + * Render resource and Booking Form selectors.
349 + *
350 + * @return void
351 + */
352 + public function render_booking_setup_controls() {
353 +
354 + $is_resource_rendered = $this->render_booking_resource_control();
355 + $is_form_rendered = $this->render_booking_form_control();
356 +
357 + if ( ! $is_resource_rendered && ! $is_form_rendered ) {
358 + ?><p class="wpbc_add_booking__empty_setup"><?php esc_html_e( 'This Booking uses the default Booking resource and Standard Booking Form.', 'booking' ); ?></p><?php
359 + }
360 + do_action( 'wpbc_admin_add_booking_setup_panel', $this );
361 + }
362 +
363 +
364 + /**
365 + * Render a compact summary of the current manual Booking configuration.
366 + *
367 + * Available settings are rendered as links that expand the correct inspector
368 + * group and move keyboard focus to the corresponding control.
369 + *
370 + * @return void
371 + */
372 + private function render_booking_setup_summary() {
373 +
374 + $resource_id = $this->get_selected_booking_resource_id();
375 + $resource_title = function_exists( 'wpbc_get_resource_title' ) ? wpbc_get_resource_title( $resource_id ) : '';
376 + $form_name = $this->get_selected_booking_form_name( $resource_id );
377 + $days_mode = $this->get_current_days_selection_mode();
378 + $send_emails = 'On' !== get_bk_option( 'booking_send_emails_off_addbooking' );
379 + $allow_past = isset( $_GET['allow_past'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
380 + $enabled_label = __( 'Enabled', 'booking' );
381 + $disabled_label = __( 'Disabled', 'booking' );
382 +
383 + if ( '' === trim( (string) $resource_title ) ) {
384 + $resource_title = sprintf( __( 'Resource #%d', 'booking' ), $resource_id );
385 + }
386 +
387 + $form_title = 'standard' === $form_name ? __( 'Standard', 'booking' ) : wpbc_lang( $form_name );
388 + ?>
389 + <div class="wpbc_add_booking__setup_summary wpbc_bfb__inspector__section--content"
390 + data-wpbc-add-booking-enabled-label="<?php echo esc_attr( $enabled_label ); ?>"
391 + data-wpbc-add-booking-disabled-label="<?php echo esc_attr( $disabled_label ); ?>"
392 + aria-label="<?php esc_attr_e( 'Current Booking setup', 'booking' ); ?>">
393 + <div class="wpbc_add_booking__setup_summary_item">
394 + <span class="wpbc_add_booking__setup_summary_label"><?php esc_html_e( 'Booking resource', 'booking' ); ?></span>
395 + <?php if ( $this->is_booking_resource_selector_available() || $this->is_booking_resource_upgrade_visible() ) : ?>
396 + <a href="#select_booking_resource" class="wpbc_add_booking__setup_summary_link" data-wpbc-add-booking-open-group="add-booking-setup" data-wpbc-add-booking-focus="#select_booking_resource"><?php echo esc_html( $resource_title ); ?></a>
397 + <?php else : ?>
398 + <strong><?php echo esc_html( $resource_title ); ?></strong>
399 + <?php endif; ?>
400 + </div>
401 + <div class="wpbc_add_booking__setup_summary_item">
402 + <span class="wpbc_add_booking__setup_summary_label"><?php esc_html_e( 'Booking Form', 'booking' ); ?></span>
403 + <?php if ( ! empty( $this->get_available_booking_forms() ) ) : ?>
404 + <a href="#select_booking_form" class="wpbc_add_booking__setup_summary_link" data-wpbc-add-booking-open-group="add-booking-setup" data-wpbc-add-booking-focus="#select_booking_form"><?php echo esc_html( $form_title ); ?></a>
405 + <?php else : ?>
406 + <strong><?php echo esc_html( $form_title ); ?></strong>
407 + <?php endif; ?>
408 + </div>
409 + <div class="wpbc_add_booking__setup_summary_item">
410 + <span class="wpbc_add_booking__setup_summary_label"><?php esc_html_e( 'Date selection', 'booking' ); ?></span>
411 + <a href="#wpbc_add_booking_date_selection_group"
412 + class="wpbc_add_booking__setup_summary_link"
413 + data-wpbc-add-booking-open-group="add-booking-date-selection"
414 + data-wpbc-add-booking-focus="input[name='wpbc_add_booking_days_selection_mode']:checked"
415 + data-wpbc-add-booking-summary="days-selection"
416 + aria-label="<?php esc_attr_e( 'Change date-selection behavior', 'booking' ); ?>"><?php echo esc_html( $this->get_days_selection_mode_label( $days_mode ) ); ?></a>
417 + </div>
418 + <div class="wpbc_add_booking__setup_summary_item">
419 + <span class="wpbc_add_booking__setup_summary_label"><?php esc_html_e( 'Email notifications', 'booking' ); ?></span>
420 + <a href="#wpbc_add_booking_tools_group"
421 + class="wpbc_add_booking__setup_summary_link"
422 + data-wpbc-add-booking-open-group="add-booking-tools"
423 + data-wpbc-add-booking-focus="#is_send_email_for_pending"
424 + data-wpbc-add-booking-summary="emails"
425 + aria-label="<?php esc_attr_e( 'Change email notification setting', 'booking' ); ?>"><?php echo esc_html( $send_emails ? $enabled_label : $disabled_label ); ?></a>
426 + </div>
427 + <div class="wpbc_add_booking__setup_summary_item">
428 + <span class="wpbc_add_booking__setup_summary_label"><?php esc_html_e( 'Allow booking in the past', 'booking' ); ?></span>
429 + <a href="#wpbc_add_booking_tools_group"
430 + class="wpbc_add_booking__setup_summary_link"
431 + data-wpbc-add-booking-open-group="add-booking-tools"
432 + data-wpbc-add-booking-focus="#is_allow_bookings_in_past"
433 + data-wpbc-add-booking-summary="allow-past"
434 + aria-label="<?php esc_attr_e( 'Change past-date Booking setting', 'booking' ); ?>"><?php echo esc_html( $allow_past ? $enabled_label : $disabled_label ); ?></a>
435 + </div>
436 + </div>
437 + <?php
438 + }
439 +
440 +
441 + /**
442 + * Render the page-local date-selection override as Calendar Settings radios.
443 + *
444 + * The radio values are not saved and do not reload the page. Add Booking
445 + * JavaScript applies them directly to the currently rendered calendar.
446 + *
447 + * @return void
448 + */
449 + public function render_date_selection_controls() {
450 +
451 + $resource_id = $this->get_selected_booking_resource_id();
452 + $days_mode = $this->get_current_days_selection_mode();
453 + $range_engine_mode = $this->get_current_range_engine_mode();
454 + $single_label = __( 'Single day', 'booking' );
455 + $multiple_label = __( 'Multiple days', 'booking' );
456 + $range_label = class_exists( 'wpdev_bk_biz_s' ) ? __( 'Range days', 'booking' ) : __( 'Range days - 2 mouse clicks', 'booking' );
457 + ?>
458 + <div class="wpbc_calendar_radio_stack wpbc_add_booking__days_radio_stack"
459 + data-wpbc-add-booking-days-selection="1"
460 + data-wpbc-resource-id="<?php echo absint( $resource_id ); ?>"
461 + data-wpbc-range-engine-mode="<?php echo esc_attr( $range_engine_mode ); ?>">
462 + <label>
463 + <input type="radio" name="wpbc_add_booking_days_selection_mode" value="single" data-wpbc-days-selection-label="<?php echo esc_attr( $single_label ); ?>" <?php checked( 'single', $days_mode ); ?> />
464 + <span><?php echo esc_html( $single_label ); ?></span>
465 + </label>
466 + <label>
467 + <input type="radio" name="wpbc_add_booking_days_selection_mode" value="multiple" data-wpbc-days-selection-label="<?php echo esc_attr( $multiple_label ); ?>" <?php checked( 'multiple', $days_mode ); ?> />
468 + <span><?php echo esc_html( $multiple_label ); ?></span>
469 + </label>
470 + <label>
471 + <input type="radio" name="wpbc_add_booking_days_selection_mode" value="range" data-wpbc-days-selection-label="<?php echo esc_attr( $range_label ); ?>" <?php checked( 'range', $days_mode ); ?> />
472 + <span><?php echo esc_html( $range_label ); ?></span>
473 + </label>
474 + </div>
475 + <p class="inspector__help"><?php esc_html_e( 'This temporary override applies immediately to this calendar. Changing the mode clears the currently selected dates but does not reload the page or modify saved Calendar and Booking Form settings.', 'booking' ); ?></p>
476 + <?php
477 + /**
478 + * Fires after the Add Booking date-selection radio controls.
479 + *
480 + * @param WPBC_Page_AddNewBooking $page_instance Current Add Booking page object.
481 + * @param string $days_mode Current normalized mode.
482 + * @param int $resource_id Current Booking resource ID.
483 + */
484 + do_action( 'wpbc_admin_add_booking_date_selection_panel', $this, $days_mode, $resource_id );
485 + }
486 +
487 +
488 + /**
489 + * Get the current global calendar mode normalized for the three radio choices.
490 + *
491 + * The selected Booking Form may apply its own mode after page initialization;
492 + * the page JavaScript synchronizes the radios with that effective mode.
493 + *
494 + * @return string One of `single`, `multiple`, or `range`.
495 + */
496 + private function get_current_days_selection_mode() {
497 +
498 + $days_selection_mode = function_exists( 'wpbc__calendar__js_params__get_days_selection_arr' )
499 + ? wpbc__calendar__js_params__get_days_selection_arr()
500 + : array( 'days_select_mode' => get_bk_option( 'booking_type_of_day_selections' ) );
501 + $days_selection_mode = isset( $days_selection_mode['days_select_mode'] ) ? sanitize_key( (string) $days_selection_mode['days_select_mode'] ) : 'multiple';
502 +
503 + if ( in_array( $days_selection_mode, array( 'fixed', 'dynamic', 'range' ), true ) ) {
504 + return 'range';
505 + }
506 +
507 + return in_array( $days_selection_mode, array( 'single', 'multiple' ), true ) ? $days_selection_mode : 'multiple';
508 + }
509 +
510 +
511 + /**
512 + * Get the range engine that the page should use when Range days is selected.
513 + *
514 + * Business Small and higher retain the configured fixed/dynamic subtype.
515 + * Free and Personal use their normal unrestricted dynamic range.
516 + *
517 + * @return string Either `fixed` or `dynamic`.
518 + */
519 + private function get_current_range_engine_mode() {
520 +
521 + if ( ! class_exists( 'wpdev_bk_biz_s' ) ) {
522 + return 'dynamic';
523 + }
524 +
525 + $range_engine_mode = sanitize_key( (string) get_bk_option( 'booking_range_selection_type' ) );
526 +
527 + return in_array( $range_engine_mode, array( 'fixed', 'dynamic' ), true ) ? $range_engine_mode : 'dynamic';
528 + }
529 +
530 +
531 + /**
532 + * Get the translated label for a normalized date-selection mode.
533 + *
534 + * @param string $days_mode Normalized date-selection mode.
535 + *
536 + * @return string Translated mode label.
537 + */
538 + private function get_days_selection_mode_label( $days_mode ) {
539 +
540 + if ( 'single' === $days_mode ) {
541 + return __( 'Single day', 'booking' );
542 + }
543 +
544 + if ( 'range' === $days_mode ) {
545 + return class_exists( 'wpdev_bk_biz_s' ) ? __( 'Range days', 'booking' ) : __( 'Range days - 2 mouse clicks', 'booking' );
546 + }
547 +
548 + return __( 'Multiple days', 'booking' );
549 + }
550 +
551 + /**
552 + * Get the Booking resource selected for the current Add Booking page.
553 + *
554 + * @return int Positive Booking resource ID.
555 + */
556 + private function get_selected_booking_resource_id() {
557 +
558 + if ( isset( $_GET['booking_type'] ) && ! is_array( $_GET['booking_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
559 + $resource_id = absint( $_GET['booking_type'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
560 + if ( $resource_id > 0 ) {
561 + return $resource_id;
562 + }
563 + }
564 +
565 + return 1;
566 + }
567 +
568 +
569 + /**
570 + * Get the Booking Form selected explicitly or inherited from the resource.
571 + *
572 + * @param int $resource_id Booking resource ID used to resolve its default form.
573 + *
574 + * @return string Sanitized Booking Form slug.
575 + */
576 + private function get_selected_booking_form_name( $resource_id ) {
577 +
578 + if ( isset( $_GET['booking_form'] ) && ! is_array( $_GET['booking_form'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
579 + $form_name = sanitize_text_field( wp_unslash( $_GET['booking_form'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
580 + if ( '' !== $form_name ) {
581 + return $form_name;
582 + }
583 + }
584 +
585 + return WPBC_Add_Booking_Component::get_default_booking_form_for_resource( $resource_id, 'standard' );
586 + }
587 +
588 +
589 + /**
590 + * Check whether the resource selector can be used in the current context.
591 + *
592 + * Resource changes remain disabled while editing an existing Booking.
593 + *
594 + * @return bool True when the commercial resource selector is available.
595 + */
596 + private function is_booking_resource_selector_available() {
597 +
598 + return empty( $_GET['booking_hash'] ) && class_exists( 'wpdev_bk_personal' ) && function_exists( 'wpbc_toolbar__get_resource_options_for_selection' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
599 + }
600 +
601 +
602 + /**
603 + * Check whether the Free-version resource upgrade notice should be shown.
604 + *
605 + * The notice replaces the unavailable resource selector only while creating
606 + * a new Booking. A user may dismiss it without affecting Booking behavior.
607 + *
608 + * @return bool True when the upgrade notice should be rendered.
609 + */
610 + private function is_booking_resource_upgrade_visible() {
611 +
612 + if ( class_exists( 'wpdev_bk_personal' ) || ! empty( $_GET['booking_hash'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
613 + return false;
614 + }
615 +
616 + if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
617 + return true;
618 + }
619 +
620 + return ! function_exists( 'wpbc_is_dismissed_panel_visible' ) || wpbc_is_dismissed_panel_visible( 'wpbc_add_booking_resource_upgrade' );
621 + }
622 +
623 +
624 + /**
625 + * Get published Booking Forms visible to the current user.
626 + *
627 + * @return array<string,array<string,mixed>> Available custom Booking Forms.
628 + */
629 + private function get_available_booking_forms() {
630 +
631 + if ( null !== $this->available_booking_forms ) {
632 + return $this->available_booking_forms;
633 + }
634 +
635 + $is_allowed = apply_bk_filter( 'multiuser_is_user_can_be_here', true, 'only_super_admin' );
636 + if ( ! $is_allowed && 'On' !== get_bk_option( 'booking_is_custom_forms_for_regular_users' ) ) {
637 + $this->available_booking_forms = array();
638 + return $this->available_booking_forms;
639 + }
640 +
641 + $this->available_booking_forms = WPBC_FE_Custom_Form_Helper::get_custom_booking_forms_list(
642 + array(
643 + 'include_standard' => false,
644 + 'owner_user_id' => WPBC_FE_Custom_Form_Helper::wpbc_mu__get_current__owner_user_id(),
645 + 'statuses' => array( 'published' ),
646 + 'list_mode' => 'auto',
647 + )
648 + );
649 +
650 + if ( ! is_array( $this->available_booking_forms ) ) {
651 + $this->available_booking_forms = array();
652 + }
653 +
654 + return $this->available_booking_forms;
655 + }
656 +
657 +
658 + /**
659 + * Render the commercial Booking resource selector as a native inspector row.
660 + *
661 + * The legacy resource option provider remains the source of truth for parent,
662 + * child, MultiUser, and edition-specific resource visibility. Only its toolbar
663 + * HTML renderer is bypassed on this page.
664 + *
665 + * @return bool True when a resource selector was rendered.
666 + */
667 + private function render_booking_resource_control() {
668 +
669 + if ( $this->is_booking_resource_upgrade_visible() ) {
670 + return $this->render_booking_resource_upgrade_control();
671 + }
672 +
673 + // A resource cannot be switched while editing an existing Booking.
674 + if ( ! $this->is_booking_resource_selector_available() ) {
675 + return false;
676 + }
677 +
678 + $resource_options = wpbc_toolbar__get_resource_options_for_selection();
679 + if ( empty( $resource_options ) || ! is_array( $resource_options ) ) {
680 + return false;
681 + }
682 +
683 + $link_base = wpbc_get_new_booking_url__base( array( 'booking_type', 'booking_form', 'parent_res' ) ) . '&booking_type=';
684 +
685 + $this->render_inspector_select_row(
686 + array(
687 + 'id' => 'select_booking_resource',
688 + 'name' => 'select_booking_resource',
689 + 'label' => __( 'Booking resource', 'booking' ),
690 + 'options' => $resource_options,
691 + 'on_change' => 'window.location.href = ' . wp_json_encode( $link_base ) . ' + this.value;',
692 + )
693 + );
694 +
695 + return true;
696 + }
697 +
698 +
699 + /**
700 + * Render the Free-version resource explanation in place of the selector.
701 + *
702 + * @return bool True after the inspector control has been rendered.
703 + */
704 + private function render_booking_resource_upgrade_control() {
705 +
706 + $dismiss_id = 'wpbc_add_booking_resource_upgrade';
707 + $is_demo = function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo();
708 + ?>
709 + <div class="inspector__row wpbc_add_booking__resource_upgrade_row" id="<?php echo esc_attr( $dismiss_id ); ?>">
710 + <span class="inspector__label"><?php esc_html_e( 'Booking resource', 'booking' ); ?></span>
711 + <div class="inspector__control" id="select_booking_resource" tabindex="-1">
712 + <div class="wpbc_add_booking__resource_upgrade_hint">
713 + <a class="wpbc_pro_label" href="<?php echo esc_url( 'https://wpbookingcalendar.com/features/' ); ?>" target="_blank" rel="noopener noreferrer">Pro</a>
714 + <?php if ( ! $is_demo && function_exists( 'wpbc_is_dismissed' ) ) : ?>
715 + <span class="wpbc_add_booking__premium_dismiss">
716 + <?php
717 + wpbc_is_dismissed(
718 + $dismiss_id,
719 + array(
720 + 'title' => '&times;',
721 + 'hint' => __( 'Hide this option', 'booking' ),
722 + 'css' => 'float:none;',
723 + )
724 + );
725 + ?>
726 + </span>
727 + <?php endif; ?>
728 + <span class="wpbc_add_booking__resource_upgrade_description"><?php esc_html_e( 'The Free version has one default booking resource. To have multiple resources, please upgrade to a premium version.', 'booking' ); ?></span>
729 + </div>
730 + </div>
731 + </div>
732 + <?php
733 +
734 + return true;
735 + }
736 +
737 +
738 + /**
739 + * Render the available Booking Forms as a native inspector row.
740 + *
741 + * This follows the same MultiUser access rule and resource-default selection
742 + * used by the legacy toolbar selector without mutating request data.
743 + *
744 + * @return bool True when a Booking Form selector was rendered.
745 + */
746 + private function render_booking_form_control() {
747 +
748 + $booking_forms = $this->get_available_booking_forms();
749 +
750 + if ( empty( $booking_forms ) || ! is_array( $booking_forms ) ) {
751 + return false;
752 + }
753 +
754 + $resource_id = $this->get_selected_booking_resource_id();
755 + $form_name = $this->get_selected_booking_form_name( $resource_id );
756 +
757 + $form_options = array(
758 + 'standard' => array(
759 + 'title' => __( 'Standard', 'booking' ),
760 + 'selected' => 'standard' === $form_name,
761 + ),
762 + 'custom_forms_open' => array(
763 + 'optgroup' => true,
764 + 'close' => false,
765 + 'title' => __( 'Custom Forms', 'booking' ),
766 + ),
767 + );
768 +
769 + foreach ( $booking_forms as $booking_form ) {
770 + $booking_form_name = isset( $booking_form['name'] ) ? sanitize_text_field( (string) $booking_form['name'] ) : '';
771 + if ( '' === $booking_form_name ) {
772 + continue;
773 + }
774 +
775 + $form_options[ $booking_form_name ] = array(
776 + 'title' => wpbc_lang( $booking_form_name ),
777 + 'selected' => $booking_form_name === $form_name,
778 + );
779 + }
780 +
781 + $form_options['custom_forms_close'] = array(
782 + 'optgroup' => true,
783 + 'close' => true,
784 + );
785 +
786 + $link_base = wpbc_get_new_booking_url__base( array( 'booking_form' ) ) . '&booking_form=';
787 +
788 + $this->render_inspector_select_row(
789 + array(
790 + 'id' => 'select_booking_form',
791 + 'name' => 'select_booking_form',
792 + 'label' => __( 'Booking Form', 'booking' ),
793 + 'options' => $form_options,
794 + 'on_change' => 'window.location.href = ' . wp_json_encode( $link_base ) . ' + this.value;',
795 + 'after' => function () use ( $form_name ) {
796 + $this->render_booking_form_edit_link( $form_name );
797 + },
798 + )
799 + );
800 +
801 + return true;
802 + }
803 +
804 +
805 + /**
806 + * Render a Forms Builder link for the currently selected Booking Form.
807 + *
808 + * @param string $form_name Sanitized Booking Form slug.
809 + *
810 + * @return void
811 + */
812 + private function render_booking_form_edit_link( $form_name ) {
813 +
814 + if ( ! function_exists( 'wpbc_get_settings_url' ) ) {
815 + return;
816 + }
817 +
818 + $base_url = add_query_arg( 'tab', 'builder_booking_form', wpbc_get_settings_url() );
819 + $edit_url = add_query_arg( 'form_name', $form_name, $base_url );
820 + ?>
821 + <a href="<?php echo esc_url( $edit_url ); ?>"
822 + class="wpbc_modal__add_booking__edit_form_link wpbc_add_booking__edit_form_link"
823 + data-wpbc-add-booking-form-builder-url="<?php echo esc_url( $base_url ); ?>"
824 + target="_blank"
825 + rel="noopener noreferrer"
826 + title="<?php esc_attr_e( 'Edit selected booking form', 'booking' ); ?>">
827 + <i class="menu_icon icon-1x wpbc_icn_draw" aria-hidden="true"></i>
828 + <span><?php esc_html_e( 'Edit form', 'booking' ); ?></span>
829 + </a>
830 + <?php
831 + }
832 +
833 +
834 + /**
835 + * Render one select field using the shared Builder-style inspector markup.
836 + *
837 + * Option arrays may contain the Booking Calendar toolbar option keys
838 + * `title`, `class`, `disabled`, `selected`, and `optgroup`.
839 + * An optional `after` callback renders related controls beneath the select.
840 + *
841 + * @param array $args Select field arguments.
842 + *
843 + * @return void
844 + */
845 + private function render_inspector_select_row( $args ) {
846 +
847 + $args = wp_parse_args(
848 + $args,
849 + array(
850 + 'id' => '',
851 + 'name' => '',
852 + 'label' => '',
853 + 'options' => array(),
854 + 'value' => null,
855 + 'on_change' => '',
856 + 'after' => null,
857 + )
858 + );
859 + ?>
860 + <div class="inspector__row">
861 + <label for="<?php echo esc_attr( $args['id'] ); ?>" class="inspector__label"><?php echo esc_html( $args['label'] ); ?></label>
862 + <div class="inspector__control">
863 + <select id="<?php echo esc_attr( $args['id'] ); ?>"
864 + name="<?php echo esc_attr( $args['name'] ); ?>"
865 + class="inspector__input"
866 + <?php echo '' !== $args['on_change'] ? 'onchange="' . esc_attr( $args['on_change'] ) . '"' : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
867 + autocomplete="off">
868 + <?php $this->render_inspector_select_options( $args['options'], $args['value'] ); ?>
869 + </select>
870 + <?php
871 + if ( is_callable( $args['after'] ) ) {
872 + call_user_func( $args['after'] );
873 + }
874 + ?>
875 + </div>
876 + </div>
877 + <?php
878 + }
879 +
880 +
881 + /**
882 + * Render select options and optgroups from Booking Calendar option arrays.
883 + *
884 + * @param array $options Option definitions keyed by submitted value.
885 + * @param string|int|null $selected_value Explicit selected value, or null to use each option's selected flag.
886 + *
887 + * @return void
888 + */
889 + private function render_inspector_select_options( $options, $selected_value = null ) {
890 +
891 + foreach ( (array) $options as $option_value => $option_definition ) {
892 + $option_definition = is_array( $option_definition ) ? $option_definition : array( 'title' => $option_definition );
893 +
894 + if ( ! empty( $option_definition['optgroup'] ) ) {
895 + if ( ! empty( $option_definition['close'] ) ) {
896 + ?></optgroup><?php
897 + } else {
898 + ?><optgroup label="<?php echo esc_attr( isset( $option_definition['title'] ) ? $option_definition['title'] : '' ); ?>"><?php
899 + }
900 + continue;
901 + }
902 +
903 + $option_title = isset( $option_definition['title'] ) ? wp_strip_all_tags( html_entity_decode( (string) $option_definition['title'], ENT_QUOTES, get_bloginfo( 'charset' ) ) ) : '';
904 + $is_selected = null === $selected_value ? ! empty( $option_definition['selected'] ) : (string) $selected_value === (string) $option_value;
905 + ?>
906 + <option value="<?php echo esc_attr( $option_value ); ?>"
907 + class="<?php echo esc_attr( isset( $option_definition['class'] ) ? $option_definition['class'] : '' ); ?>"
908 + <?php selected( $is_selected ); ?>
909 + <?php disabled( ! empty( $option_definition['disabled'] ) ); ?>><?php echo esc_html( $option_title ); ?></option>
910 + <?php
911 + }
912 + }
913 +
914 +
915 + /**
916 + * Render email and past-date controls.
917 + *
918 + * @return void
919 + */
920 + public function render_booking_tools_controls() {
921 +
922 + $allow_past = isset( $_GET['allow_past'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
923 + $allow_past_url = esc_url_raw( wpbc_add_booking_page_get_allow_past_url( true ) );
924 + $normal_url = esc_url_raw( wpbc_add_booking_page_get_allow_past_url( false ) );
925 + $send_emails = 'On' !== get_bk_option( 'booking_send_emails_off_addbooking' );
926 + ?>
927 + <?php
928 + wpbc_render_admin_cost_correction_control(
929 + array(
930 + 'input_id' => 'wpbc_add_booking_cost_correction',
931 + 'resource_id' => $this->get_selected_booking_resource_id(),
932 + 'wrapper_class' => 'inspector__row wpbc_add_booking__cost_correction',
933 + 'label_class' => 'inspector__label',
934 + 'control_class' => 'inspector__control',
935 + 'help_class' => 'description wpbc_bfb__help',
936 + 'help' => __( 'Leave the number empty to use the calculated Booking cost. Enter an exact total to replace the final cost for this Booking.', 'booking' ),
937 + )
938 + );
939 + ?>
940 + <div class="inspector__row inspector__row--toggle">
941 + <div class="inspector__control">
942 + <?php
943 + wpbc_flex_toggle(
944 + array(
945 + 'id' => 'is_send_email_for_pending',
946 + 'name' => 'is_send_email_for_pending',
947 + 'label' => array( 'title' => __( 'Emails sending', 'booking' ) ),
948 + 'value' => 'On',
949 + 'selected' => $send_emails,
950 + 'legend' => __( 'Send email notifications for this Booking', 'booking' ),
951 + )
952 + );
953 + ?>
954 + <p class="inspector__help"><?php esc_html_e( 'Send the normal Booking Calendar notifications when this Booking is created.', 'booking' ); ?></p>
955 + </div>
956 + </div>
957 + <div class="inspector__row inspector__row--toggle">
958 + <div class="inspector__control">
959 + <?php
960 + wpbc_flex_toggle(
961 + array(
962 + 'id' => 'is_allow_bookings_in_past',
963 + 'name' => 'is_allow_bookings_in_past',
964 + 'label' => array( 'title' => __( 'Allow booking in the past', 'booking' ) ),
965 + 'value' => 'On',
966 + 'selected' => $allow_past,
967 + 'legend' => __( 'Allow booking in the past', 'booking' ),
968 + 'onchange' => 'window.location.href = this.checked ? ' . wp_json_encode( $allow_past_url ) . ' : ' . wp_json_encode( $normal_url ) . ';',
969 + )
970 + );
971 + ?>
972 + <p class="inspector__help"><?php esc_html_e( 'Use this only for historical or manually entered Bookings.', 'booking' ); ?></p>
973 + </div>
974 + </div>
975 + <?php do_action( 'wpbc_admin_add_booking_tools_panel', $this ); ?>
976 + <?php
977 + }
978 +
979 + /**
980 + * Render per-user calendar sizing controls and their existing AJAX actions.
981 + *
982 + * @return void
983 + */
984 + public function render_calendar_view_controls() {
985 +
986 + $user_calendar_options = $this->get_saved_user_calendar_settings();
987 + $month_options = array_combine( range( 1, 12 ), range( 1, 12 ) );
988 + $months_per_row = array( 0 => __( 'All', 'booking' ) ) + $month_options;
989 +
990 + $this->render_inspector_select_row(
991 + array(
992 + 'id' => 'calendar_months_count',
993 + 'name' => 'calendar_months_count',
994 + 'label' => __( 'Visible months', 'booking' ),
995 + 'options' => $month_options,
996 + 'value' => isset( $user_calendar_options['calendar_months_count'] ) ? absint( $user_calendar_options['calendar_months_count'] ) : 1,
997 + )
998 + );
999 + $this->render_inspector_select_row(
1000 + array(
1001 + 'id' => 'calendar_months_num_in_1_row',
1002 + 'name' => 'calendar_months_num_in_1_row',
1003 + 'label' => __( 'Number of months in one row', 'booking' ),
1004 + 'options' => $months_per_row,
1005 + 'value' => isset( $user_calendar_options['calendar_months_num_in_1_row'] ) ? absint( $user_calendar_options['calendar_months_num_in_1_row'] ) : 0,
1006 + )
1007 + );
1008 +
1009 + $this->render_inspector_measurement_row(
1010 + 'calendar_width',
1011 + __( 'Maximum width of calendar', 'booking' ),
1012 + isset( $user_calendar_options['calendar_width'] ) ? $user_calendar_options['calendar_width'] : '',
1013 + isset( $user_calendar_options['calendar_widthunits'] ) ? $user_calendar_options['calendar_widthunits'] : 'px',
1014 + '100%'
1015 + );
1016 + $this->render_inspector_measurement_row(
1017 + 'calendar_cell_height',
1018 + __( 'Calendar cell height', 'booking' ),
1019 + isset( $user_calendar_options['calendar_cell_height'] ) ? $user_calendar_options['calendar_cell_height'] : '',
1020 + isset( $user_calendar_options['calendar_cell_heightunits'] ) ? $user_calendar_options['calendar_cell_heightunits'] : 'px',
1021 + '48px'
1022 + );
1023 + ?>
1024 + <div class="wpbc_add_booking__panel_actions">
1025 + <button type="button"
1026 + id="toolbar_btn__calendar_options_save"
1027 + class="button button-primary"
1028 + onclick="var wpbc_calendar_options = { calendar_months_count: jQuery( '#calendar_months_count' ).val(), calendar_months_num_in_1_row: jQuery( '#calendar_months_num_in_1_row' ).val(), calendar_width: jQuery( '#calendar_width' ).val(), calendar_widthunits: jQuery( '#calendar_widthunits' ).val(), calendar_cell_height: jQuery( '#calendar_cell_height' ).val(), calendar_cell_heightunits: jQuery( '#calendar_cell_heightunits' ).val() }; wpbc_save_custom_user_data( <?php echo absint( wpbc_get_current_user_id() ); ?>, 'add_booking_calendar_options', jQuery.param( wpbc_calendar_options ), 1 );"><?php esc_html_e( 'Save', 'booking' ); ?></button>
1029 + <button type="button"
1030 + class="button button-secondary"
1031 + onclick="jQuery( '#calendar_months_count' ).val( '1' ); jQuery( '#calendar_months_num_in_1_row' ).val( '0' ); jQuery( '#calendar_width' ).val( '0' ); jQuery( '#calendar_widthunits' ).val( 'px' ); jQuery( '#calendar_cell_height' ).val( '0' ); jQuery( '#calendar_cell_heightunits' ).val( 'px' ); jQuery( '#toolbar_btn__calendar_options_save' ).trigger( 'click' );"><?php esc_html_e( 'Reset', 'booking' ); ?></button>
1032 + </div>
1033 + <?php do_action( 'wpbc_admin_add_booking_calendar_view_panel', $this, $user_calendar_options ); ?>
1034 + <?php
1035 + }
1036 +
1037 +
1038 + /**
1039 + * Render a numeric calendar measurement and its unit selector.
1040 + *
1041 + * @param string $field_id Base HTML id and name.
1042 + * @param string $label Visible field label.
1043 + * @param string|int $value Saved numeric value.
1044 + * @param string $unit Saved unit: `px` or `percent`.
1045 + * @param string $placeholder Example value shown when empty.
1046 + *
1047 + * @return void
1048 + */
1049 + private function render_inspector_measurement_row( $field_id, $label, $value, $unit, $placeholder ) {
1050 +
1051 + $unit = in_array( $unit, array( 'px', 'percent' ), true ) ? $unit : 'px';
1052 + ?>
1053 + <div class="inspector__row">
1054 + <label for="<?php echo esc_attr( $field_id ); ?>" class="inspector__label"><?php echo esc_html( $label ); ?></label>
1055 + <div class="inspector__control inspector__control--inline">
1056 + <input id="<?php echo esc_attr( $field_id ); ?>" name="<?php echo esc_attr( $field_id ); ?>" type="number" min="0" step="1" value="<?php echo esc_attr( $value ); ?>" placeholder="<?php echo esc_attr( $placeholder ); ?>" class="inspector__input">
1057 + <select id="<?php echo esc_attr( $field_id . 'units' ); ?>" name="<?php echo esc_attr( $field_id . 'units' ); ?>" class="inspector__input inspector__input--unit" autocomplete="off">
1058 + <option value="px" <?php selected( 'px', $unit ); ?>>px</option>
1059 + <option value="percent" <?php selected( 'percent', $unit ); ?>>%</option>
1060 + </select>
1061 + </div>
1062 + </div>
1063 + <?php
1064 + }
1065 +
1066 +
1067 + /**
1068 + * Render contextual help in place of the legacy toolbar Help dropdown.
1069 + *
1070 + * @return void
1071 + */
1072 + public function render_help_panel() {
1073 +
1074 + WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Add Booking Help', 'booking' ), __( 'Create a Booking directly from the WordPress Admin Panel.', 'booking' ) );
1075 + ?>
1076 + <div class="wpbc_bfb__inspector__body wpbc_bfb__inspector__body--content wpbc_add_booking__help_body">
1077 + <div class="wpbc_add_booking__hint">
1078 + <span class="wpbc_icn_info_outline" aria-hidden="true"></span>
1079 + <p><?php esc_html_e( 'Choose the Booking resource and form first. Changing either selector reloads this page so every field and calendar keeps the correct resource ID.', 'booking' ); ?></p>
1080 + </div>
1081 + <div class="wpbc_add_booking__help_section">
1082 + <h4><?php esc_html_e( 'Recommended workflow', 'booking' ); ?></h4>
1083 + <ol>
1084 + <li><?php esc_html_e( 'Select the Booking resource and Booking Form.', 'booking' ); ?></li>
1085 + <li><?php esc_html_e( 'Choose an available date or time and complete the customer fields.', 'booking' ); ?></li>
1086 + <li><?php esc_html_e( 'Review email sending, then click Add booking.', 'booking' ); ?></li>
1087 + </ol>
1088 + </div>
1089 + <div class="wpbc_add_booking__help_actions">
1090 + <a class="button" href="https://wpbookingcalendar.com/faq/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'FAQ', 'booking' ); ?></a>
1091 + <a class="button" href="https://wpbookingcalendar.com/support/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Support', 'booking' ); ?></a>
1092 + </div>
1093 + <?php do_action( 'wpbc_admin_add_booking_help_panel', $this ); ?>
1094 + </div>
1095 + <?php
1096 + }
1097 +
1098 +
1099 + /**
1100 + * Render the legacy page or the released inspector presentation.
1101 + *
1102 + * Both presentations delegate the actual Booking Form to the same reusable
1103 + * component, so calendar and submission behavior cannot diverge.
1104 + *
1105 + * @return void|false False when the current user cannot use the page.
1106 + */
1107 + public function content() {
1108 +
1109 + do_action( 'wpbc_hook_add_booking_page_header', 'add_booking' ); // Define Notices Section and show some static messages, if needed.
1110 +
1111 + if ( ! wpbc_is_mu_user_can_be_here( 'activated_user' ) ) {
1112 + return false; // Check if MU user activated, otherwise show Warning message.
1113 + }
1114 +
1115 + if ( ! WPBC_Add_Booking_Component::current_user_can_add_booking() ) {
1116 + return false;
1117 + }
1118 +
1119 + if ( ! wpbc_set_default_resource_to__get() ) {
1120 + return false; // Define default booking resources for $_ GET and check if booking resource belong to user.
1121 + }
1122 +
1123 + if ( wpbc_add_booking_page_is_inspector_enabled() ) {
1124 + ?>
1125 + <div class="wpbc_add_booking_page" data-wpbc-add-booking-page="1">
1126 + <div class="wpbc_add_booking__canvas">
1127 + <?php
1128 + WPBC_Add_Booking_Component::render(
1129 + array(
1130 + 'is_toolbar_visible' => false,
1131 + 'is_show_before_content_spacer' => false,
1132 + 'is_show_footer_email_toggle' => false,
1133 + 'content_css_class' => 'add_booking_page_content wpbc_add_booking__form_content',
1134 + 'content_style' => '',
1135 + )
1136 + );
1137 + ?>
1138 + </div>
1139 + </div>
1140 + <?php
1141 + } else {
1142 + WPBC_Add_Booking_Component::render();
1143 + }
1144 +
1145 + do_action( 'wpbc_hook_add_booking_page_footer', 'add_booking' );
1146 + }
1147 +
1148 +
1149 + /**
1150 + * Get Calendar Options of specific User
1151 + *
1152 + * @return array<string,mixed> Normalized calendar display options.
1153 + */
1154 + public function get_saved_user_calendar_options() {
1155 +
1156 + return WPBC_Add_Booking_Component::get_saved_user_calendar_options();
1157 + }
1158 +
1159 +
1160 + /**
1161 + * Get raw saved calendar fields used by the Add Booking inspector.
1162 + *
1163 + * @return array<string,mixed> Raw per-user calendar settings.
1164 + */
1165 + private function get_saved_user_calendar_settings() {
1166 +
1167 + return WPBC_Add_Booking_Component::get_saved_user_calendar_settings();
1168 + }
1169 +
1170 +}
1171 +add_action('wpbc_menu_created', array( new WPBC_Page_AddNewBooking() , '__construct') ); // Executed after creation of Menu
1172 +
1173 +/**
1174 + * Redirect legacy/main-menu Add Booking page to the Add Booking tab under Bookings.
1175 + *
1176 + * @param string $page_tag Current menu page tag.
1177 + *
1178 + * @return void
1179 + */
1180 +function wpbc_add_booking_menu_page__redirect_to_add_booking_tab( $page_tag ) {
1181 +
1182 + if ( 'wpbc-new' !== $page_tag ) {
1183 + return;
1184 + }
1185 +
1186 + wpbc_redirect( wpbc_get_new_booking_url() );
1187 +}
1188 +add_action( 'wpbc_page_structure_show', 'wpbc_add_booking_menu_page__redirect_to_add_booking_tab', 0 );