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 / page-catalog-booking-resources / booking-resources-catalog-page.php

booking-resources-catalog-page.php in Booking Calendar 11.9, at includes/page-catalog-booking-resources/booking-resources-catalog-page.php

655 lines 27.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Template-driven Booking Resources catalog page shell.
4 *
5 * @package Booking Calendar
6 * @since 11.6.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Determine whether the current request targets the independent catalog page.
15 *
16 * @return bool True only for the gated catalog administration route.
17 */
18 function wpbc_catalog_booking_resources_is_page() {
19 return 'new' === wpbc_booking_resources_catalog_get_request_renderer();
20 }
21
22 /**
23 * Return allow-listed shared request overrides from the initial page URL.
24 *
25 * These values remain untrusted and are normalized by WPBC_UI_Catalog_Request.
26 * Subsequent browser requests do not reuse this URL layer.
27 *
28 * @return array<string,mixed> Untrusted initial shared request overrides.
29 */
30 function wpbc_catalog_booking_resources_get_initial_url_overrides() {
31 $url_overrides = array();
32 $url_parameters = array(
33 'page_number' => array( 'page_number', 'catalog_page' ),
34 'items_per_page' => array( 'items_per_page', 'catalog_per_page' ),
35 'sort_by' => array( 'sort_by', 'catalog_sort' ),
36 'sort_order' => array( 'sort_order', 'catalog_order' ),
37 'search' => array( 'search', 'catalog_search' ),
38 'visible_columns' => array( 'visible_columns', 'catalog_columns' ),
39 'column_order' => array( 'column_order', 'catalog_column_order' ),
40 'template_pack' => array( 'template_pack' ),
41 );
42
43 foreach ( $url_parameters as $request_key => $parameter_names ) {
44 foreach ( $parameter_names as $parameter_name ) {
45 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only initial catalog display overrides.
46 if ( ! array_key_exists( $parameter_name, $_GET ) ) {
47 continue;
48 }
49 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Validated by the shared request normalizer.
50 $request_value = wp_unslash( $_GET[ $parameter_name ] );
51 if ( in_array( $request_key, array( 'visible_columns', 'column_order' ), true ) && is_scalar( $request_value ) ) {
52 $request_value = '' === trim( (string) $request_value ) ? array() : explode( ',', (string) $request_value );
53 }
54 $url_overrides[ $request_key ] = $request_value;
55 break;
56 }
57 }
58
59 return $url_overrides;
60 }
61
62 /**
63 * Build the canonical per-Resource publishing URL used by external admin pages.
64 *
65 * The visible-column and filter arguments are read-only initial display
66 * overrides. They do not include the shared preference action, so opening this
67 * URL cannot replace the user's saved catalog view. The Resource catalog owns
68 * the launch intent and resolves the first publishable Resource from its
69 * authorized list DTO before opening its Actions menu and focusing Publish.
70 *
71 * @return string Canonical Resources URL with a request-local publishing view.
72 */
73 function wpbc_catalog_booking_resources_get_publish_url() {
74 $catalog_url = function_exists( 'wpbc_booking_modes_get_canonical_page_url' )
75 ? wpbc_booking_modes_get_canonical_page_url( 'wpbc-resources__resources' )
76 : '';
77 $catalog_url = is_string( $catalog_url ) && '' !== $catalog_url
78 ? $catalog_url
79 : admin_url( 'admin.php?page=wpbc-resources&tab=resources' );
80 $configuration = wpbc_catalog_booking_resources_get_config();
81 $allowed_columns = isset( $configuration['columns']['allowed'] ) && is_array( $configuration['columns']['allowed'] )
82 ? $configuration['columns']['allowed']
83 : array();
84 $publishing_columns = isset( $configuration['views']['definitions']['publishing']['fields'] ) && is_array( $configuration['views']['definitions']['publishing']['fields'] )
85 ? array_values( array_intersect( $configuration['views']['definitions']['publishing']['fields'], $allowed_columns ) )
86 : array();
87
88 $catalog_url = add_query_arg(
89 array(
90 'catalog_page' => 1,
91 'catalog_search' => '',
92 'catalog_columns' => implode( ',', $publishing_columns ),
93 'resource_type' => 'all',
94 'wpbc_catalog_resource_action' => 'publish_resource',
95 ),
96 $catalog_url
97 );
98
99 return $catalog_url . '#wpbc_catalog_booking_resources';
100 }
101
102 /**
103 * Return one allow-listed read-only catalog launch intent from the page URL.
104 *
105 * The intent grants no authority and carries no Resource ID. Browser code must
106 * resolve a Resource only from the authorized list response. Activating the
107 * focused action follows the normal domain handler and authorization checks.
108 *
109 * @return array<string,string> Browser-safe launch intent, or an empty array.
110 */
111 function wpbc_catalog_booking_resources_get_launch_intent() {
112 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only presentation intent; inspector requests remain nonce protected.
113 $requested_action = isset( $_GET['wpbc_catalog_resource_action'] ) && is_scalar( $_GET['wpbc_catalog_resource_action'] )
114 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Sanitized and allow-listed immediately below.
115 ? sanitize_key( wp_unslash( $_GET['wpbc_catalog_resource_action'] ) )
116 : '';
117
118 if ( 'publish_resource' !== $requested_action ) {
119 return array();
120 }
121
122 return array(
123 'action' => 'publish_resource',
124 'query_parameter' => 'wpbc_catalog_resource_action',
125 );
126 }
127
128 /**
129 * Build the normalized browser configuration for the initial catalog mount.
130 *
131 * Phase 4 localizes the read endpoint and enables shared automatic loading.
132 * The browser renders the returned DTOs through registered Resource templates.
133 *
134 * @return array<string,mixed> Browser-safe catalog configuration.
135 */
136 function wpbc_catalog_booking_resources_get_client_config() {
137 $catalog_id = 'catalog_booking_resources';
138 $configuration = WPBC_UI_Catalog_Registry::get_instance()->get_configuration( $catalog_id );
139
140 if ( empty( $configuration ) ) {
141 return array();
142 }
143
144 $stored_preferences = WPBC_UI_Catalog_Preferences::load( $catalog_id );
145 $request = WPBC_UI_Catalog_Request::create(
146 $configuration,
147 array( 'request_id' => 1 ),
148 $stored_preferences,
149 wpbc_catalog_booking_resources_get_initial_url_overrides(),
150 true
151 );
152 $stored_resource_request = WPBC_Catalog_Booking_Resources_Request::create(
153 array_intersect_key( $stored_preferences, array( 'resource_type' => true, 'hierarchy_state' => true ) )
154 );
155 if ( is_wp_error( $stored_resource_request ) ) {
156 $stored_resource_request = WPBC_Catalog_Booking_Resources_Request::create();
157 }
158 $resource_request = $stored_resource_request;
159 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only initial filter override validated below.
160 if ( ! empty( $configuration['features']['resource_type_filter'] ) && isset( $_GET['resource_type'] ) ) {
161 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Validated by the Resource request object.
162 $resource_request = WPBC_Catalog_Booking_Resources_Request::create(
163 array(
164 'resource_type' => wp_unslash( $_GET['resource_type'] ),
165 'hierarchy_state' => $stored_resource_request->get( 'hierarchy_state', array() ),
166 )
167 );
168 }
169 $auto_load = ! is_wp_error( $request ) && ! is_wp_error( $resource_request );
170
171 if ( is_wp_error( $request ) || is_wp_error( $resource_request ) ) {
172 $fallback_request = WPBC_UI_Catalog_Request::create( $configuration, array( 'request_id' => 1 ) );
173 if ( is_wp_error( $fallback_request ) ) {
174 return array();
175 }
176
177 $initial_request = $fallback_request->to_array();
178 $initial_request['resource_type'] = 'all';
179 $initial_request['hierarchy_state'] = WPBC_Catalog_Booking_Resources_Request::create()->get_hierarchy_state_json();
180 $initial_error = is_wp_error( $request ) ? $request : $resource_request;
181 $initial_response = WPBC_UI_Catalog_Response::from_wp_error( $catalog_id, 1, $initial_error );
182 } else {
183 $initial_request = $request->to_array();
184 $initial_request['resource_type'] = $resource_request->get( 'resource_type', 'all' );
185 $initial_request['hierarchy_state'] = $resource_request->get_hierarchy_state_json();
186 $empty_response = WPBC_UI_Catalog_Response::create_empty( $catalog_id, $request );
187 if ( is_wp_error( $empty_response ) ) {
188 $initial_response = WPBC_UI_Catalog_Response::from_wp_error( $catalog_id, 1, $empty_response );
189 } else {
190 $initial_response = $empty_response->to_array();
191 }
192 }
193
194 $client_configuration = WPBC_UI_Catalog::get_client_configuration(
195 $catalog_id,
196 'wpbc_catalog_booking_resources',
197 $initial_request,
198 $initial_response
199 );
200 $client_configuration['ajax_url'] = admin_url( 'admin-ajax.php' );
201 $client_configuration['auto_load'] = $auto_load;
202 $client_configuration['is_demo'] = function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo();
203 $client_configuration['launch_intent'] = wpbc_catalog_booking_resources_get_launch_intent();
204 $client_configuration['nonce'] = wp_create_nonce( $configuration['nonce_name'] );
205 $client_configuration['details_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_DETAILS';
206 $client_configuration['inspector_create_schema_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_CREATE_SCHEMA';
207 $client_configuration['inspector_edit_schema_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_EDIT_SCHEMA';
208 $client_configuration['inspector_create_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_CREATE';
209 $client_configuration['inspector_update_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_UPDATE';
210 $client_configuration['bulk_schema_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_BULK_SCHEMA';
211 $client_configuration['bulk_preview_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_BULK_PREVIEW';
212 $client_configuration['bulk_apply_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_BULK_APPLY';
213 $client_configuration['inline_schema_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_INLINE_SCHEMA';
214 $client_configuration['inline_preview_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_INLINE_PREVIEW';
215 $client_configuration['inline_apply_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_INLINE_APPLY';
216 $client_configuration['delete_preview_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_DELETE_PREVIEW';
217 $client_configuration['delete_apply_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCES_DELETE_APPLY';
218 $client_configuration['capacity_context_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_CAPACITY_CONTEXT';
219 $client_configuration['capacity_preview_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_CAPACITY_PREVIEW';
220 $client_configuration['capacity_apply_action'] = 'WPBC_AJX_CATALOG_BOOKING_RESOURCE_CAPACITY_APPLY';
221 $default_request = WPBC_UI_Catalog_Request::create( $configuration );
222 $client_configuration['default_request'] = is_wp_error( $default_request ) ? array() : $default_request->to_array();
223 $client_configuration['default_request']['resource_type'] = 'all';
224 $client_configuration['default_request']['hierarchy_state'] = WPBC_Catalog_Booking_Resources_Request::create()->get_hierarchy_state_json();
225 $client_configuration['url_parameters'] = array(
226 'page_number' => 'catalog_page',
227 'items_per_page' => 'catalog_per_page',
228 'sort_by' => 'catalog_sort',
229 'sort_order' => 'catalog_order',
230 'search' => 'catalog_search',
231 'visible_columns' => 'catalog_columns',
232 'column_order' => 'catalog_column_order',
233 'template_pack' => 'template_pack',
234 );
235 if ( ! empty( $configuration['features']['resource_type_filter'] ) ) {
236 $client_configuration['url_parameters']['resource_type'] = 'resource_type';
237 }
238 $client_configuration['i18n']['publishing_launch_unavailable'] = __( 'No Booking Resource is available for publishing.', 'booking' );
239
240 return $client_configuration;
241 }
242
243 /**
244 * Enqueue catalog styles only on the independent Resources page.
245 *
246 * @param string $where_to_load Booking Calendar asset context.
247 *
248 * @return void
249 */
250 function wpbc_catalog_booking_resources_enqueue_css( $where_to_load ) {
251 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_catalog_booking_resources_is_page() ) {
252 return;
253 }
254
255 $asset_path = __DIR__ . '/_out/booking_resources_catalog.css';
256 $asset_modified = is_readable( $asset_path ) ? filemtime( $asset_path ) : false;
257 $asset_version = false !== $asset_modified ? WP_BK_VERSION_NUM . '.' . $asset_modified : WP_BK_VERSION_NUM;
258
259 WPBC_UI_Catalog::enqueue_styles();
260 wp_enqueue_style(
261 'wpbc-catalog-booking-resources',
262 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/booking_resources_catalog.css',
263 array( 'wpbc-ui-catalog' ),
264 $asset_version
265 );
266 }
267 add_action( 'wpbc_enqueue_css_files', 'wpbc_catalog_booking_resources_enqueue_css', 68 );
268
269 /**
270 * Enqueue and configure catalog scripts only on the independent Resources page.
271 *
272 * @param string $where_to_load Booking Calendar asset context.
273 *
274 * @return void
275 */
276 function wpbc_catalog_booking_resources_enqueue_js( $where_to_load ) {
277 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_catalog_booking_resources_is_page() ) {
278 return;
279 }
280
281 $asset_path = __DIR__ . '/_out/booking_resources_catalog.js';
282 $asset_modified = is_readable( $asset_path ) ? filemtime( $asset_path ) : false;
283 $asset_version = false !== $asset_modified ? WP_BK_VERSION_NUM . '.' . $asset_modified : WP_BK_VERSION_NUM;
284
285 WPBC_UI_Catalog::enqueue_scripts();
286 if ( function_exists( 'wpbc_load_js__required_for_media_upload' ) ) {
287 wpbc_load_js__required_for_media_upload();
288 }
289 if ( function_exists( 'wpbc_load_js__required_for_modals' ) ) {
290 wpbc_load_js__required_for_modals();
291 }
292 wp_enqueue_script(
293 'wpbc-catalog-booking-resources',
294 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/booking_resources_catalog.js',
295 array( 'wpbc-ui-catalog', 'wpbc-ui-catalog-hierarchy', 'wpbc-ui-catalog-selection', 'wpbc-ui-catalog-actions', 'wpbc-admin-support' ),
296 $asset_version,
297 array( 'in_footer' => WPBC_JS_IN_FOOTER )
298 );
299 wp_localize_script(
300 'wpbc-catalog-booking-resources',
301 'wpbc_catalog_booking_resources_config',
302 wpbc_catalog_booking_resources_get_client_config()
303 );
304 }
305 add_action( 'wpbc_enqueue_js_files', 'wpbc_catalog_booking_resources_enqueue_js', 68 );
306
307 /**
308 * Print registered WP templates only for the independent catalog page.
309 *
310 * Templates are static presentation files. They perform no authorization,
311 * queries, mutations, or domain decisions.
312 *
313 * @return void
314 */
315 function wpbc_catalog_booking_resources_print_templates() {
316 static $templates_printed = false;
317
318 if ( $templates_printed || ! wpbc_catalog_booking_resources_is_page() ) {
319 return;
320 }
321
322 $templates_printed = true;
323 WPBC_UI_Catalog::print_templates( 'catalog_booking_resources' );
324 }
325 add_action( 'admin_footer', 'wpbc_catalog_booking_resources_print_templates', 40 );
326
327 /**
328 * Render the shared Free-edition Booking Resource upgrade button.
329 *
330 * Both the top toolbar and catalog footer open the same informational modal.
331 * The toolbar keeps its compact label on one line, while the footer preserves
332 * the established superscript Pro marker used by in-page upgrade controls.
333 *
334 * @param bool $use_superscript Whether to render the Pro marker in a sup element.
335 *
336 * @return void
337 */
338 function wpbc_catalog_booking_resources_render_upgrade_button( $use_superscript = false ) {
339 ?>
340 <button
341 type="button"
342 class="button button-primary tooltip_bottom wpbc_catalog_booking_resources__upgrade_button"
343 data-wpbc-catalog-booking-resource-upgrade
344 data-wpbc-catalog-booking-resource-upgrade-url="<?php echo esc_url( 'https://wpbookingcalendar.com/features/' ); ?>"
345 aria-haspopup="dialog"
346 aria-controls="wpbc_catalog_booking_resources__upgrade_modal"
347 data-original-title="<?php esc_attr_e( 'Adding more Booking Resources is available in paid versions.', 'booking' ); ?>"
348 title="<?php esc_attr_e( 'Adding more Booking Resources is available in paid versions.', 'booking' ); ?>"
349 >
350 <span class="wpbc-bi-plus-lg" aria-hidden="true"></span>
351 <?php if ( $use_superscript ) : ?>
352 <?php esc_html_e( 'Add Booking Resource', 'booking' ); ?>
353 <sup><?php esc_html_e( '(Pro)', 'booking' ); ?></sup>
354 <?php else : ?>
355 <?php esc_html_e( 'Add Booking Resource (Pro)', 'booking' ); ?>
356 <?php endif; ?>
357 </button>
358 <?php
359 }
360
361 /**
362 * Render the old-compatible Add Resource control in the native top toolbar.
363 *
364 * @param string $page_tag Current Booking Calendar page tag.
365 * @param string $active_page_tab Current active tab.
366 * @param string $active_page_subtab Current active subtab.
367 *
368 * @return void
369 */
370 function wpbc_catalog_booking_resources_render_top_toolbar( $page_tag, $active_page_tab, $active_page_subtab ) {
371 unset( $page_tag, $active_page_tab, $active_page_subtab );
372
373 if ( ! wpbc_catalog_booking_resources_is_page() || ! current_user_can( wpbc_catalog_booking_resources_get_manage_capability() ) ) {
374 return;
375 }
376 $maximum_quantity = ( new WPBC_Catalog_Booking_Resource_Inspector_Schema() )->get_maximum_quantity();
377 ?>
378 <div class="wpbc_ui_el__buttons_group wpbc_catalog_booking_resources__top_toolbar">
379 <?php if ( class_exists( 'wpdev_bk_personal' ) && 0 < $maximum_quantity ) : ?>
380 <button type="button" class="button button-primary" data-wpbc-catalog-booking-resource-create data-wpbc-right-sidebar-keep-open="1">
381 <span class="wpbc-bi-plus-lg" aria-hidden="true"></span>
382 <?php esc_html_e( 'Add Booking Resource', 'booking' ); ?>
383 </button>
384 <?php elseif ( class_exists( 'wpdev_bk_personal' ) ) : ?>
385 <span class="wpbc_catalog_booking_resources__free_limit_top"><span class="wpbc-bi-info-circle" aria-hidden="true"></span><?php esc_html_e( 'The Booking Resource limit for this account has been reached.', 'booking' ); ?></span>
386 <?php else : ?>
387 <?php wpbc_catalog_booking_resources_render_upgrade_button(); ?>
388 <?php endif; ?>
389 </div>
390 <?php
391 }
392 add_action( 'wpbc_ui_el__top_nav__content_end', 'wpbc_catalog_booking_resources_render_top_toolbar', 20, 3 );
393
394 /**
395 * Render the Free-edition explanation shown by the Pro-only add control.
396 *
397 * The modal is informational only. It never calls a mutation endpoint in the
398 * Free edition and keeps the existing single default Resource available.
399 *
400 * @return void
401 */
402 function wpbc_catalog_booking_resources_render_free_upgrade_modal() {
403 if ( class_exists( 'wpdev_bk_personal' ) ) {
404 return;
405 }
406 ?>
407 <div class="wpdevelop">
408 <div
409 id="wpbc_catalog_booking_resources__upgrade_modal"
410 class="modal wpbc_popup_modal wpbc_catalog_booking_resources__upgrade_modal"
411 tabindex="-1"
412 role="dialog"
413 aria-hidden="true"
414 aria-labelledby="wpbc_catalog_booking_resources__upgrade_modal_title"
415 aria-describedby="wpbc_catalog_booking_resources__upgrade_modal_description"
416 >
417 <div class="modal-dialog">
418 <div class="modal-content">
419 <div class="modal-header">
420 <button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e( 'Close', 'booking' ); ?>"><span aria-hidden="true">&times;</span></button>
421 <h4 id="wpbc_catalog_booking_resources__upgrade_modal_title" class="modal-title"><?php esc_html_e( 'Add more Booking Resources with Pro', 'booking' ); ?></h4>
422 </div>
423 <div class="modal-body">
424 <p id="wpbc_catalog_booking_resources__upgrade_modal_description"><?php esc_html_e( 'Creating additional Booking Resources is available in paid versions of Booking Calendar. Each Resource can have its own calendar and availability.', 'booking' ); ?></p>
425 <p><?php esc_html_e( 'Your default Resource remains ready to customize in the Free version.', 'booking' ); ?></p>
426 </div>
427 <div class="modal-footer">
428 <button type="button" class="button button-secondary" data-dismiss="modal"><?php esc_html_e( 'Close', 'booking' ); ?></button>
429 <a class="button button-primary" href="<?php echo esc_url( 'https://wpbookingcalendar.com/features/' ); ?>" target="_blank" rel="noopener noreferrer">
430 <?php esc_html_e( 'Compare Pro versions', 'booking' ); ?>
431 <span class="screen-reader-text"> <?php esc_html_e( '(opens in a new tab)', 'booking' ); ?></span>
432 </a>
433 </div>
434 </div>
435 </div>
436 </div>
437 </div>
438 <?php
439 }
440
441 /**
442 * Render the reusable informational dialog for catalog warnings and messages.
443 *
444 * The browser runtime supplies escaped text through textContent before opening
445 * the established Booking Calendar modal. Keeping one dialog in the page avoids
446 * browser alerts while preserving a safe fallback when modal assets fail.
447 *
448 * @return void
449 */
450 function wpbc_catalog_booking_resources_render_message_modal() {
451 ?>
452 <div class="wpdevelop">
453 <div
454 id="wpbc_catalog_booking_resources__message_modal"
455 class="modal wpbc_popup_modal wpbc_catalog_booking_resources__upgrade_modal wpbc_catalog_booking_resources__message_modal"
456 tabindex="-1"
457 role="dialog"
458 aria-hidden="true"
459 aria-labelledby="wpbc_catalog_booking_resources__message_modal_title"
460 aria-describedby="wpbc_catalog_booking_resources__message_modal_description"
461 >
462 <div class="modal-dialog">
463 <div class="modal-content">
464 <div class="modal-header">
465 <button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e( 'Close', 'booking' ); ?>"><span aria-hidden="true">&times;</span></button>
466 <h4 id="wpbc_catalog_booking_resources__message_modal_title" class="modal-title" data-wpbc-default-title="<?php esc_attr_e( 'Booking Resources', 'booking' ); ?>"><?php esc_html_e( 'Booking Resources', 'booking' ); ?></h4>
467 </div>
468 <div class="modal-body">
469 <p id="wpbc_catalog_booking_resources__message_modal_description"></p>
470 </div>
471 <div class="modal-footer">
472 <button type="button" class="button button-primary" data-dismiss="modal"><?php esc_html_e( 'Close', 'booking' ); ?></button>
473 </div>
474 </div>
475 </div>
476 </div>
477 </div>
478 <?php
479 }
480
481 /**
482 * Render sticky inspector actions in the native right-sidebar footer.
483 *
484 * @param array $active_page Current page identifiers.
485 *
486 * @return void
487 */
488 function wpbc_catalog_booking_resources_render_right_sidebar_footer( $active_page ) {
489 if (
490 empty( $active_page['active_page'] )
491 || 'wpbc-resources' !== $active_page['active_page']
492 || ! wpbc_catalog_booking_resources_is_page()
493 || ! current_user_can( wpbc_catalog_booking_resources_get_manage_capability() )
494 ) {
495 return;
496 }
497 ?>
498 <div class="wpbc_ui_el__vert_right_bar__footer_section wpbc_catalog_booking_resources__right_sidebar_footer" data-wpbc-ui-catalog-inspector-footer hidden>
499 <div class="wpbc_catalog_booking_resources__footer_actions wpbc_ui_el__buttons_group">
500 <button type="button" class="button" data-wpbc-ui-catalog-inspector-cancel><?php esc_html_e( 'Cancel', 'booking' ); ?></button>
501 <button type="submit" class="button button-primary" form="wpbc_catalog_booking_resource_inspector_form" data-wpbc-ui-catalog-inspector-save disabled><?php esc_html_e( 'Save changes', 'booking' ); ?></button>
502 </div>
503 </div>
504 <?php
505 }
506 add_action( 'wpbc_ui__right_vertical_sidebar_footer', 'wpbc_catalog_booking_resources_render_right_sidebar_footer', 10, 1 );
507
508 /**
509 * Register the independent catalog shell with the shared admin page controller.
510 */
511 class WPBC_Page_Catalog_Booking_Resources extends WPBC_Page_Structure {
512
513 /**
514 * Return the catalog page slug for authorized users.
515 *
516 * @return string Page slug or a non-matching value when unauthorized.
517 */
518 public function in_page() {
519 if ( ! wpbc_booking_resources_catalog_should_use_new_renderer() || ! current_user_can( wpbc_catalog_booking_resources_get_manage_capability() ) ) {
520 return 'wpbc-catalog-booking-resources-unauthorized';
521 }
522
523 return 'wpbc-resources';
524 }
525
526 /**
527 * Define the initial template-driven catalog page tab.
528 *
529 * @return array<string,array<string,mixed>> Page tab definition.
530 */
531 public function tabs() {
532 return array(
533 'resources' => array(
534 'is_show_top_path' => true,
535 'is_show_top_navigation' => false,
536 'left_navigation__default_view_mode' => '', // FixIn:11.7.1.1: 'compact',
537 'right_vertical_sidebar__is_show' => true,
538 'right_vertical_sidebar__default_view_mode' => 'none',
539 'right_vertical_sidebar__content_click_collapse_mode' => 'none',
540 'right_vertical_sidebar_compact__is_show' => true,
541 'top_path' => array( 'root_title' => false ),
542 'top_path_title' => __( 'Booking Resources', 'booking' ),
543 'title' => __( 'Booking Resources', 'booking' ),
544 'page_title' => __( 'Booking Resources', 'booking' ),
545 'hint' => __( 'A new, unified listing for the properties, people, places, and assets customers can book.', 'booking' ),
546 'font_icon' => 'wpbc-bi-collection',
547 'default' => true,
548 'disabled' => false,
549 'hided' => false,
550 'subtabs' => array(),
551 ),
552 );
553 }
554
555 /**
556 * Render the compact Settings tab for the native right sidebar.
557 *
558 * @return void
559 */
560 public function right_sidebar_compact_content() {
561 WPBC_UI_Sidebar_Panels::render_rightbar_tabs(
562 array(
563 array(
564 'id' => 'wpbc_tab_catalog_booking_resources_selection',
565 'panel_id' => 'wpbc_catalog_booking_resources__inspector_selection',
566 'title' => __( 'Settings', 'booking' ),
567 'icon' => 'wpbc_icn_tune',
568 'selected' => true,
569 ),
570 ),
571 array(
572 'aria_label' => __( 'Booking resource panels', 'booking' ),
573 'context' => 'catalog_booking_resources',
574 'class' => 'wpbc_catalog_booking_resources__rightbar_tabs',
575 )
576 );
577 }
578
579 /**
580 * Render the template target inside the native old-compatible palette.
581 *
582 * @return void
583 */
584 public function right_sidebar_content() {
585 ?>
586 <div class="wpbc_bfb__panel--library wpbc_rightbar_palette wpbc_catalog_booking_resources__rightbar">
587 <?php
588 WPBC_UI_Sidebar_Panels::render_panel(
589 array(
590 'id' => 'wpbc_catalog_booking_resources__inspector_selection',
591 'labelledby' => 'wpbc_tab_catalog_booking_resources_selection',
592 'class' => 'wpbc_catalog_booking_resources__inspector_panel',
593 ),
594 static function () {
595 ?><div data-wpbc-catalog-booking-resources-inspector-host></div><?php
596 }
597 );
598 ?>
599 </div>
600 <?php
601 }
602
603 /**
604 * Render the Phase 4 catalog mount without querying Booking Resource data in PHP.
605 *
606 * @return void|false False when the current user is unauthorized.
607 */
608 public function content() {
609 if ( 'new' !== wpbc_booking_resources_catalog_get_request_renderer() || ! current_user_can( wpbc_catalog_booking_resources_get_manage_capability() ) ) {
610 return false;
611 }
612 ?>
613 <div class="wpbc_catalog_booking_resources_page">
614 <div id="wpbc_catalog_booking_resources" class="wpbc_catalog_booking_resources__mount" data-wpbc-catalog-mount="catalog_booking_resources" aria-live="polite">
615 <div class="wpbc_spins_loading_container wpbc_ui_catalog__loading" role="status">
616 <div class="wpbc_booking_form_spin_loader" aria-hidden="true">
617 <div class="wpbc_spins_loader_wrapper">
618 <div class="wpbc_one_spin_loader_mini2"></div>
619 </div>
620 </div>
621 <span><?php esc_html_e( 'Loading', 'booking' ); ?>...</span>
622 </div>
623 </div>
624 <noscript>
625 <div class="notice notice-error inline">
626 <p><?php esc_html_e( 'JavaScript is required to display the Booking Resources catalog.', 'booking' ); ?></p>
627 </div>
628 </noscript>
629 </div>
630 <?php if ( ! class_exists( 'wpdev_bk_personal' ) ) : ?>
631 <div class="wpbc_catalog_booking_resources__footer_upgrade">
632 <?php wpbc_catalog_booking_resources_render_upgrade_button( true ); ?>
633 </div>
634 <?php endif; ?>
635 <div class="wpbc_page_publish_notice_section wpbc_catalog_booking_resources__footer_notice">
636 <?php
637 // Render only the provider guidance formerly attached to the broad legacy Resources hook.
638 if ( function_exists( 'wpbc_appointment_services_render_provider_tools' ) ) {
639 wpbc_appointment_services_render_provider_tools( 'resources' );
640 }
641 ?>
642 </div>
643 <?php
644 wpbc_catalog_booking_resources_render_free_upgrade_modal();
645 wpbc_catalog_booking_resources_render_message_modal();
646 if ( class_exists( 'WPBC_Booking_Form_Publish_Modal' ) ) {
647 WPBC_Booking_Form_Publish_Modal::render();
648 }
649 }
650 }
651
652 if ( wpbc_booking_resources_catalog_should_use_new_renderer() ) {
653 add_action( 'wpbc_menu_created', array( new WPBC_Page_Catalog_Booking_Resources(), '__construct' ) );
654 }
655