| 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">×</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">×</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 |
|