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-appointment-services / appointment_services__page.php

appointment_services__page.php in Booking Calendar 11.9, at includes/page-appointment-services/appointment_services__page.php

608 lines 28.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /** AJAX Appointment Services admin page. @package Booking Calendar */
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * Enqueue Appointment Services management-page styles.
9 *
10 * @param string $where_to_load Booking Calendar asset context.
11 *
12 * @return void
13 */
14 function wpbc_appointment_services_enqueue_css( $where_to_load ) {
15 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) ) {
16 return;
17 }
18
19 $is_resources_page_with_mode_guide = function_exists( 'wpbc_is_resources_page' )
20 && wpbc_is_resources_page();
21
22 if ( $is_resources_page_with_mode_guide ) {
23 wp_enqueue_style(
24 'wpbc-appointment-provider-tools',
25 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_provider_tools.css',
26 array(),
27 WP_BK_VERSION_NUM
28 );
29 }
30
31 if ( ! wpbc_appointment_services__is_page() ) {
32 return;
33 }
34
35 WPBC_UI_Catalog::enqueue_styles();
36 wp_enqueue_style( 'wpbc-appointment-services-page', trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_services_page.css', array( 'wpbc-ui-catalog' ), WP_BK_VERSION_NUM );
37 }
38 add_action( 'wpbc_enqueue_css_files', 'wpbc_appointment_services_enqueue_css', 66 );
39
40
41 /**
42 * Resolve the Service that should be selected when the catalog first opens.
43 *
44 * An explicit `service_id` request opens that Service. Ordinary catalog visits
45 * do not infer a selection from Setup Wizard state.
46 *
47 * @return int Initial Service ID, or zero when the inspector should stay closed.
48 */
49 function wpbc_appointment_services_get_initial_service_id() {
50
51 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only selection state.
52 $requested_service_id = isset( $_GET['service_id'] ) && is_scalar( $_GET['service_id'] )
53 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only selection state.
54 ? absint( wp_unslash( $_GET['service_id'] ) )
55 : 0;
56
57 return $requested_service_id;
58 }
59
60 /**
61 * Migrate released Service listing preferences into the shared catalog namespace.
62 *
63 * The migration runs only when the new site-local preference payload has never
64 * been created. Existing shared preferences, including an explicit reset, are
65 * never replaced by older listing values.
66 *
67 * @param array<string,mixed> $configuration Registered Service catalog configuration.
68 *
69 * @return void
70 */
71 function wpbc_appointment_services_migrate_catalog_preferences( $configuration ) {
72 $user_id = get_current_user_id();
73 $namespace = WPBC_UI_Catalog_Preferences::get_namespace( 'appointment_services_catalog' );
74 if ( ! $user_id || '' === $namespace || false !== get_user_option( $namespace, $user_id ) ) {
75 return;
76 }
77
78 $legacy_preferences = get_user_option( 'wpbc_admin_listing_preferences', $user_id );
79 $legacy_preferences = is_array( $legacy_preferences ) && isset( $legacy_preferences['appointment_services_catalog'] ) && is_array( $legacy_preferences['appointment_services_catalog'] )
80 ? $legacy_preferences['appointment_services_catalog']
81 : array();
82 if ( empty( $legacy_preferences ) ) {
83 return;
84 }
85
86 $request_values = array_intersect_key(
87 $legacy_preferences,
88 array(
89 'items_per_page' => true,
90 'sort_by' => true,
91 'sort_order' => true,
92 'column_order' => true,
93 )
94 );
95 if ( isset( $legacy_preferences['visible_fields'] ) && is_array( $legacy_preferences['visible_fields'] ) ) {
96 $request_values['visible_columns'] = $legacy_preferences['visible_fields'];
97 }
98 $legacy_filters = isset( $legacy_preferences['filters'] ) && is_array( $legacy_preferences['filters'] )
99 ? $legacy_preferences['filters']
100 : array();
101 $service_request = WPBC_Appointment_Services_Catalog_Request::create(
102 array(
103 'status' => isset( $legacy_filters['status'] ) ? $legacy_filters['status'] : 'all',
104 'resource_id' => isset( $legacy_filters['resource_id'] ) ? $legacy_filters['resource_id'] : 0,
105 )
106 );
107
108 $request = WPBC_UI_Catalog_Request::create( $configuration, $request_values );
109 if ( ! is_wp_error( $request ) && ! is_wp_error( $service_request ) ) {
110 WPBC_UI_Catalog_Preferences::save(
111 'appointment_services_catalog',
112 $request,
113 array(
114 'status' => $service_request->get( 'status', 'all' ),
115 'resource_id' => $service_request->get( 'resource_id', 0 ),
116 ),
117 $user_id,
118 1
119 );
120 }
121 }
122
123 /**
124 * Return validated initial URL overrides for the Services catalog.
125 *
126 * @return array<string,mixed> Shared catalog URL overrides.
127 */
128 function wpbc_appointment_services_get_initial_catalog_overrides() {
129 $parameter_map = array(
130 'page_number' => 'catalog_page',
131 'items_per_page' => 'catalog_per_page',
132 'sort_by' => 'catalog_sort',
133 'sort_order' => 'catalog_order',
134 'search' => 'catalog_search',
135 'visible_columns' => 'catalog_columns',
136 'column_order' => 'catalog_column_order',
137 'template_pack' => 'template_pack',
138 );
139 $url_overrides = array();
140 foreach ( $parameter_map as $request_key => $parameter_name ) {
141 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only initial catalog state.
142 if ( ! isset( $_GET[ $parameter_name ] ) ) {
143 continue;
144 }
145 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Validated by WPBC_UI_Catalog_Request.
146 $request_value = wp_unslash( $_GET[ $parameter_name ] );
147 if ( in_array( $request_key, array( 'visible_columns', 'column_order' ), true ) && is_scalar( $request_value ) ) {
148 $request_value = '' === trim( (string) $request_value ) ? array() : explode( ',', (string) $request_value );
149 }
150 $url_overrides[ $request_key ] = $request_value;
151 }
152
153 return $url_overrides;
154 }
155
156 /**
157 * Build the shared Services catalog browser configuration.
158 *
159 * @return array<string,mixed> Browser-safe Service and catalog configuration.
160 */
161 function wpbc_appointment_services_get_catalog_client_config() {
162 $catalog_id = 'appointment_services_catalog';
163 $domain_configuration = wpbc_appointment_services_get_catalog_config();
164 $configuration = WPBC_UI_Catalog_Registry::get_instance()->get_configuration( $catalog_id );
165 if ( empty( $configuration ) ) {
166 return array();
167 }
168
169 wpbc_appointment_services_migrate_catalog_preferences( $configuration );
170 $stored_preferences = WPBC_UI_Catalog_Preferences::load( $catalog_id );
171 $shared_request = WPBC_UI_Catalog_Request::create(
172 $configuration,
173 array( 'request_id' => 1 ),
174 $stored_preferences,
175 wpbc_appointment_services_get_initial_catalog_overrides(),
176 true
177 );
178 $service_values = array(
179 'status' => isset( $stored_preferences['status'] ) ? $stored_preferences['status'] : 'all',
180 'resource_id' => isset( $stored_preferences['resource_id'] ) ? $stored_preferences['resource_id'] : 0,
181 );
182 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only initial Provider filter.
183 if ( isset( $_GET['provider_id'] ) ) {
184 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Validated by the Service request object.
185 $service_values['resource_id'] = wp_unslash( $_GET['provider_id'] );
186 }
187 $service_request = WPBC_Appointment_Services_Catalog_Request::create( $service_values );
188 $auto_load = ! is_wp_error( $shared_request ) && ! is_wp_error( $service_request );
189 if ( ! $auto_load ) {
190 $shared_request = WPBC_UI_Catalog_Request::create( $configuration, array( 'request_id' => 1 ) );
191 if ( is_wp_error( $shared_request ) ) {
192 return array();
193 }
194 $initial_error = is_wp_error( $service_request ) ? $service_request : new WP_Error( 'wpbc_appointment_services_invalid_request', __( 'The Services request is invalid.', 'booking' ) );
195 $initial_response = WPBC_UI_Catalog_Response::from_wp_error( $catalog_id, 1, $initial_error );
196 $service_request = WPBC_Appointment_Services_Catalog_Request::create();
197 } else {
198 $empty_response = WPBC_UI_Catalog_Response::create_empty( $catalog_id, $shared_request );
199 $initial_response = is_wp_error( $empty_response )
200 ? WPBC_UI_Catalog_Response::from_wp_error( $catalog_id, 1, $empty_response )
201 : $empty_response->to_array();
202 }
203
204 $initial_request = $shared_request->to_array();
205 $initial_request['status'] = $service_request->get( 'status', 'all' );
206 $initial_request['resource_id'] = $service_request->get( 'resource_id', 0 );
207 $client_configuration = WPBC_UI_Catalog::get_client_configuration( $catalog_id, 'wpbc_appointment_services_catalog', $initial_request, $initial_response );
208 $client_configuration['ajax_url'] = admin_url( 'admin-ajax.php' );
209 $client_configuration['auto_load'] = $auto_load;
210 $client_configuration['nonce'] = wp_create_nonce( $configuration['nonce_name'] );
211 $default_request = WPBC_UI_Catalog_Request::create( $configuration );
212 if ( is_wp_error( $default_request ) ) {
213 return array();
214 }
215 $client_configuration['default_request'] = $default_request->to_array();
216 $client_configuration['default_request']['status'] = 'all';
217 $client_configuration['default_request']['resource_id'] = 0;
218 $client_configuration['url_parameters'] = array(
219 'page_number' => 'catalog_page',
220 'items_per_page' => 'catalog_per_page',
221 'sort_by' => 'catalog_sort',
222 'sort_order' => 'catalog_order',
223 'search' => 'catalog_search',
224 'visible_columns' => 'catalog_columns',
225 'column_order' => 'catalog_column_order',
226 'template_pack' => 'template_pack',
227 'resource_id' => 'provider_id',
228 );
229 $client_configuration['filters'] = $domain_configuration['filters'];
230 $client_configuration['actions'] = $domain_configuration['actions'];
231
232 return $client_configuration;
233 }
234
235
236 /**
237 * Enqueue and configure the AJAX Appointment Services management client.
238 *
239 * @param string $where_to_load Booking Calendar asset context.
240 *
241 * @return void
242 */
243 function wpbc_appointment_services_enqueue_js( $where_to_load ) {
244 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_appointment_services__is_page() ) {
245 return;
246 }
247 WPBC_UI_Catalog::enqueue_scripts();
248 wpbc_load_js__required_for_media_upload();
249 $selected_service_id = wpbc_appointment_services_get_initial_service_id();
250 $default_provider_ids = array();
251
252 if ( ! class_exists( 'wpdev_bk_personal' ) ) {
253 $default_provider_id = function_exists( 'wpbc_get_default_resource' ) ? absint( wpbc_get_default_resource() ) : 1;
254 if ( ! $default_provider_id ) {
255 $default_provider_id = 1;
256 }
257
258 $default_provider_ids[] = $default_provider_id;
259 }
260
261 wp_enqueue_script( 'wpbc-appointment-services-page', trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_services_page.js', array(
262 'jquery',
263 'wpbc_all',
264 'wpbc-ui-catalog',
265 'wpbc-ui-catalog-actions',
266 ), WP_BK_VERSION_NUM, array( 'in_footer' => WPBC_JS_IN_FOOTER ) );
267
268 wp_localize_script( 'wpbc-appointment-services-page', 'wpbc_appointment_services_config', array(
269 'ajax_url' => admin_url( 'admin-ajax.php' ),
270 'nonce' => wp_create_nonce( 'wpbc_appointment_services_ajax_nonce' ),
271 'actions' => array(
272 'list' => 'WPBC_AJX_APPOINTMENT_SERVICES_LIST',
273 'load' => 'WPBC_AJX_APPOINTMENT_SERVICE_LOAD',
274 'save' => 'WPBC_AJX_APPOINTMENT_SERVICE_SAVE',
275 'duplicate' => 'WPBC_AJX_APPOINTMENT_SERVICE_DUPLICATE',
276 'archive' => 'WPBC_AJX_APPOINTMENT_SERVICE_ARCHIVE',
277 'inline_schema' => 'WPBC_AJX_APPOINTMENT_SERVICES_INLINE_SCHEMA',
278 'bulk_contract' => 'WPBC_AJX_APPOINTMENT_SERVICES_BULK_CONTRACT',
279 'preview' => 'WPBC_AJX_APPOINTMENT_SERVICES_CATALOG_PREVIEW',
280 'apply' => 'WPBC_AJX_APPOINTMENT_SERVICES_CATALOG_APPLY',
281 'delete_preview' => 'WPBC_AJX_APPOINTMENT_SERVICES_DELETE_PREVIEW',
282 'delete_apply' => 'WPBC_AJX_APPOINTMENT_SERVICES_DELETE_APPLY',
283 ),
284 'selected_id' => $selected_service_id,
285 'focus_section' => wpbc_appointment_services_get_requested_focus_section(),
286 'default_provider_ids' => $default_provider_ids,
287 'pricing_available' => wpbc_appointment_services_is_pricing_available(),
288 'catalog' => wpbc_appointment_services_get_catalog_client_config(),
289 'i18n' => array(
290 'loading' => __( 'Loading Services', 'booking' ) . '...',
291 'empty' => __( 'No Services yet', 'booking' ),
292 'empty_help' => __( 'Create the first Service to define what customers can book.', 'booking' ),
293 'no_providers' => __( 'No Providers available', 'booking' ),
294 'no_providers_help'=> __( 'Create a Provider before assigning Services and accepting Appointments.', 'booking' ),
295 'not_connected' => __( 'The Services database is not ready. Reload this page as an administrator or reactivate Booking Calendar to finish the database upgrade.', 'booking' ),
296 'load_failed' => __( 'Services could not be loaded.', 'booking' ),
297 'operation_failed' => __( 'The Service editor could not be opened. Please reload the page and try again.', 'booking' ),
298 'save_failed' => __( 'The Service could not be saved.', 'booking' ),
299 'duplicate_failed' => __( 'The Service could not be duplicated.', 'booking' ),
300 'archive_failed' => __( 'The Service could not be archived.', 'booking' ),
301 'preview_failed' => __( 'The Service changes could not be reviewed.', 'booking' ),
302 'apply_failed' => __( 'The Service changes could not be applied.', 'booking' ),
303 'delete_preview_failed' => __( 'The Service deletion could not be reviewed.', 'booking' ),
304 'delete_apply_failed' => __( 'The selected Services could not be permanently deleted.', 'booking' ),
305 'confirm_archive' => __( 'Archive this Service?', 'booking' ),
306 'confirm_discard' => __( 'Discard unsaved Service changes?', 'booking' ),
307 'untitled' => __( 'Untitled Service', 'booking' ),
308 'new_service' => __( 'New Service', 'booking' ),
309 'create_service_title' => __( 'Add Service', 'booking' ),
310 'create_service_description' => __( 'Create a Service without leaving the catalog.', 'booking' ),
311 'edit_service_title' => __( 'Edit Service', 'booking' ),
312 'edit_service_description' => __( 'Update this Service without leaving the catalog.', 'booking' ),
313 'inspector_context_new' => __( 'New', 'booking' ),
314 /* translators: %d: Service ID. */
315 'inspector_context_id' => __( 'ID: %d', 'booking' ),
316 'active' => __( 'Active', 'booking' ),
317 'draft' => __( 'Draft', 'booking' ),
318 'archived' => __( 'Archived', 'booking' ),
319 'no_provider' => __( 'No Providers assigned', 'booking' ),
320 'no_availability' => __( 'No weekly availability', 'booking' ),
321 'more_providers' => __( 'more Providers', 'booking' ),
322 'available' => __( 'Available', 'booking' ),
323 'unavailable' => __( 'Unavailable', 'booking' ),
324 'available_providers' => __( 'Available Providers: %s', 'booking' ),
325 'no_available_providers' => __( 'No assigned Providers are available', 'booking' ),
326 'edit_availability' => __( 'Edit availability for %s', 'booking' ),
327 'edit_provider_availability' => __( 'Edit Provider availability', 'booking' ),
328 'column_id' => __( 'ID', 'booking' ),
329 'column_service' => __( 'Service', 'booking' ),
330 'column_duration' => __( 'Duration', 'booking' ),
331 'column_price' => __( 'Price', 'booking' ),
332 'column_providers' => __( 'Providers', 'booking' ),
333 'column_weekly_availability' => __( 'Weekly Availability', 'booking' ),
334 'column_status' => __( 'Status', 'booking' ),
335 'column_actions' => __( 'Actions', 'booking' ),
336 /* translators: %s: Duration in minutes. */
337 'duration_minutes' => __( '%s min', 'booking' ),
338 /* translators: 1: Buffer before in minutes, 2: Buffer after in minutes. */
339 'buffers_summary' => __( 'Buffers: %1$s / %2$s min', 'booking' ),
340 /* translators: 1: Buffer before in minutes, 2: Buffer after in minutes. */
341 'buffers_tooltip' => __( 'Buffer before: %1$s min; Buffer after: %2$s min', 'booking' ),
342 /* translators: 1: Service title, 2: Service description. */
343 'service_thumbnail_tooltip' => __( 'Title: %1$s — Description: %2$s', 'booking' ),
344 'no_description' => __( 'No description', 'booking' ),
345 'edit' => __( 'Edit Service', 'booking' ),
346 'archive' => __( 'Archive Service', 'booking' ),
347 /* translators: %s: Service title. */
348 'select_service' => __( 'Select %s', 'booking' ),
349 'select_all' => __( 'Select all visible Services', 'booking' ),
350 'editing_rows' => __( 'Editing rows', 'booking' ),
351 /* translators: %s: Number of changed Service rows. */
352 'changed_rows' => __( '%s changed rows', 'booking' ),
353 'changed' => __( 'Changed', 'booking' ),
354 'inline_help' => __( 'Edit safe fields on this page, then review all changes before applying them.', 'booking' ),
355 'inline_schema_failed' => __( 'Safe inline Service fields could not be loaded.', 'booking' ),
356 'bulk_contract_failed' => __( 'Safe fields could not be determined for this Service selection.', 'booking' ),
357 'cancel' => __( 'Cancel', 'booking' ),
358 'review_changes' => __( 'Review changes', 'booking' ),
359 'apply_changes' => __( 'Apply changes', 'booking' ),
360 'bulk_edit_title' => __( 'Edit selected Services', 'booking' ),
361 'bulk_edit_description' => __( 'Enable only the fields that should change for every selected Service.', 'booking' ),
362 'inline_review_title' => __( 'Review inline changes', 'booking' ),
363 'bulk_review_title' => __( 'Review bulk changes', 'booking' ),
364 'review_confirmation' => __( 'Confirm the row-specific changes before applying them.', 'booking' ),
365 'review_description' => __( 'No Service will change until you choose Apply changes.', 'booking' ),
366 'showing' => __( 'Showing %1$s–%2$s of %3$s Services', 'booking' ),
367 ),
368 'currency_symbol' => wpbc_appointment_services_is_pricing_available() && function_exists( 'wpbc_get_currency_symbol' ) ? wpbc_get_currency_symbol() : '',
369 'weekdays' => array(
370 __( 'Monday', 'booking' ),
371 __( 'Tuesday', 'booking' ),
372 __( 'Wednesday', 'booking' ),
373 __( 'Thursday', 'booking' ),
374 __( 'Friday', 'booking' ),
375 __( 'Saturday', 'booking' ),
376 __( 'Sunday', 'booking' ),
377 ),
378 ) );
379 }
380 add_action( 'wpbc_enqueue_js_files', 'wpbc_appointment_services_enqueue_js', 66 );
381
382 /**
383 * Print Service-owned catalog templates on the Services page only.
384 *
385 * @return void
386 */
387 function wpbc_appointment_services_print_catalog_templates() {
388 static $templates_printed = false;
389 if ( $templates_printed || ! wpbc_appointment_services__is_page() ) {
390 return;
391 }
392 $templates_printed = true;
393 WPBC_UI_Catalog::print_templates( 'appointment_services_catalog' );
394 }
395 add_action( 'admin_footer', 'wpbc_appointment_services_print_catalog_templates', 40 );
396
397 /**
398 * Render the global Add Service action in the Booking Calendar top toolbar.
399 *
400 * Creating a Service is a page-level action, while saving remains contextual
401 * to the Service editor in the right inspector.
402 *
403 * @param string $page_tag Current Booking Calendar page tag.
404 * @param string $active_page_tab Current active page tab.
405 * @param string $active_page_subtab Current active page subtab.
406 *
407 * @return void
408 */
409 function wpbc_appointment_services_render_top_toolbar( $page_tag, $active_page_tab, $active_page_subtab ) {
410 if (
411 ! wpbc_appointment_services__is_page()
412 || ! current_user_can( wpbc_appointment_services_get_manage_capability() )
413 ) {
414 return;
415 }
416 ?>
417 <div class="wpbc_ui_el__buttons_group wpbc_appointment_services__top_toolbar">
418 <button type="button" class="button button-primary wpbc_appointment_services__add" disabled>
419 <span class="wpbc-bi-plus-lg" aria-hidden="true"></span>
420 <?php esc_html_e( 'Add Service', 'booking' ); ?>
421 </button>
422 </div>
423 <?php
424 }
425 add_action( 'wpbc_ui_el__top_nav__content_end', 'wpbc_appointment_services_render_top_toolbar', 20, 3 );
426
427
428 /** Appointment Services page definition for the shared Booking Calendar shell. */
429 class WPBC_Page_Appointment_Services extends WPBC_Page_Structure {
430
431 /**
432 * Return the WordPress admin page slug when the user may access Services.
433 *
434 * @return string Page slug, or a non-matching random value when unauthorized.
435 */
436 public function in_page() {
437 if ( ! current_user_can( wpbc_appointment_services_get_manage_capability() ) ) {
438 return (string) wp_rand( 100000, 1000000 );
439 }
440
441 return 'wpbc-services';
442 }
443
444 /**
445 * Define the Appointment Services tab and shared page-shell behavior.
446 *
447 * @return array<string,array<string,mixed>> Page tab definition.
448 */
449 public function tabs() {
450 $service_hint = wpbc_appointment_services_is_pricing_available()
451 ? __( 'Create Services and configure duration, buffers, price, Providers, and Booking Form.', 'booking' )
452 : __( 'Create Services and configure duration, buffers, Providers, and Booking Form.', 'booking' );
453
454 return array(
455 'appointment_services' => array(
456 'is_show_top_path' => true,
457 'is_show_top_navigation' => false,
458 'left_navigation__default_view_mode' => '', // FixIn:11.7.1.1: 'compact',
459 'right_vertical_sidebar__is_show' => true,
460 'right_vertical_sidebar__default_view_mode' => 'none',
461 'right_vertical_sidebar__content_click_collapse_mode' => 'none',
462 'right_vertical_sidebar_compact__is_show' => true,
463 'top_path' => array( 'root_title' => false ),
464 'top_path_title' => __( 'Services', 'booking' ),
465 'title' => __( 'Services', 'booking' ),
466 'page_title' => __( 'Services', 'booking' ),
467 'hint' => $service_hint,
468 'font_icon' => 'wpbc-bi-grid',
469 'default' => true,
470 'disabled' => false,
471 'hided' => false,
472 'subtabs' => array(),
473 ),
474 );
475 }
476
477 /**
478 * Render the compact Settings and Help controls for the right sidebar.
479 *
480 * @return void
481 */
482 public function right_sidebar_compact_content() {
483 WPBC_UI_Sidebar_Panels::render_rightbar_tabs( array(
484 array(
485 'id' => 'wpbc_tab_service_settings',
486 'panel_id' => 'wpbc_service__inspector_settings',
487 'title' => __( 'Settings', 'booking' ),
488 'icon' => 'wpbc_icn_tune',
489 'selected' => true,
490 ),
491 array(
492 'id' => 'wpbc_tab_service_help',
493 'panel_id' => 'wpbc_service__inspector_help',
494 'title' => __( 'Help', 'booking' ),
495 'icon' => 'wpbc-bi-info-circle',
496 ),
497 ), array(
498 'aria_label' => __( 'Service Panels', 'booking' ),
499 'context' => 'appointment_services',
500 'class' => 'wpbc_appointment_services__rightbar_tabs',
501 ) );
502 }
503
504 /**
505 * Render the right-sidebar Settings and Help inspector panels.
506 *
507 * @return void
508 */
509 public function right_sidebar_content() {
510 ?>
511 <div class="wpbc_bfb__panel--library wpbc_rightbar_palette wpbc_appointment_services__rightbar"><?php
512 WPBC_UI_Sidebar_Panels::render_panel( array(
513 'id' => 'wpbc_service__inspector_settings',
514 'labelledby' => 'wpbc_tab_service_settings',
515 'class' => 'wpbc_appointment_services__inspector_settings',
516 ), 'wpbc_appointment_services_render_settings_panel' );
517 WPBC_UI_Sidebar_Panels::render_panel( array(
518 'id' => 'wpbc_service__inspector_help',
519 'labelledby' => 'wpbc_tab_service_help',
520 'class' => 'wpbc_appointment_services__inspector_help',
521 'hidden' => true,
522 ), 'wpbc_appointment_services_render_help_panel' );
523 ?></div><?php
524 }
525
526 /**
527 * Render the AJAX-driven Service filters and management table shell.
528 *
529 * @return void|false False when the current user is unauthorized.
530 */
531 public function content() {
532 if ( ! current_user_can( wpbc_appointment_services_get_manage_capability() ) ) {
533 return false;
534 }
535 $providers = wpbc_appointment_services_get_provider_options();
536 $resource_capability = class_exists( 'wpdev_bk_personal' )
537 ? get_bk_option( 'booking_user_role_resources' )
538 : get_bk_option( 'booking_user_role_settings' );
539 $can_manage_providers = $resource_capability && current_user_can( $resource_capability );
540 $selected_provider_id = isset( $_GET['provider_id'] ) ? absint( $_GET['provider_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
541 if ( ! isset( $providers[ $selected_provider_id ] ) ) {
542 $selected_provider_id = 0;
543 }
544 ?>
545 <div class="wpbc_appointment_services_page" data-wpbc-appointment-services-page="1">
546 <div class="wpbc_appointment_services__filters">
547 <div class="wpbc_appointment_services__status_filters" role="group" aria-label="<?php
548 esc_attr_e( 'Filter Services by status', 'booking' ); ?>">
549 <button type="button" class="wpbc_appointment_services__status_filter is-active"
550 data-service-status="all" aria-pressed="true"><span><?php
551 esc_html_e( 'All', 'booking' ); ?></span><strong data-service-count="all">0</strong>
552 </button>
553 <button type="button" class="wpbc_appointment_services__status_filter" data-service-status="active"
554 aria-pressed="false"><span><?php
555 esc_html_e( 'Active', 'booking' ); ?></span><strong data-service-count="active">0</strong>
556 </button>
557 <button type="button" class="wpbc_appointment_services__status_filter"
558 data-service-status="inactive" aria-pressed="false"><span><?php
559 esc_html_e( 'Draft', 'booking' ); ?></span><strong data-service-count="inactive">0</strong>
560 </button>
561 <button type="button" class="wpbc_appointment_services__status_filter"
562 data-service-status="archived" aria-pressed="false"><span><?php
563 esc_html_e( 'Archived', 'booking' ); ?></span><strong
564 data-service-count="archived">0</strong></button>
565 <span class="wpbc_appointment_services__filter_divider" aria-hidden="true"></span>
566 <span class="wpbc_appointment_services__provider_total"><span><?php
567 esc_html_e( 'Providers', 'booking' ); ?></span><strong data-provider-count>0</strong></span>
568 </div>
569 <div class="wpbc_appointment_services__filter_controls">
570 <div class="wpbc_appointment_services__search">
571 <span class="wpbc-bi-search" aria-hidden="true"></span>
572 <label class="screen-reader-text" for="wpbc_service_search"><?php esc_html_e( 'Search Services', 'booking' ); ?></label>
573 <input type="search" id="wpbc_service_search" placeholder="<?php esc_attr_e( 'Search Services', 'booking' ); ?>" autocomplete="off" data-wpbc-ui-catalog-search />
574 <button type="button" class="wpbc_appointment_services__search_clear" aria-label="<?php esc_attr_e( 'Clear Service search', 'booking' ); ?>" data-wpbc-ui-catalog-search-clear data-wpbc-appointment-services-search-clear hidden>
575 <span class="wpbc-bi-x-lg" aria-hidden="true"></span>
576 </button>
577 </div>
578 <label class="screen-reader-text" for="wpbc_service_provider_filter"><?php
579 esc_html_e( 'Filter by Provider', 'booking' ); ?></label>
580 <select id="wpbc_service_provider_filter">
581 <option value="0"><?php
582 esc_html_e( 'All Providers', 'booking' ); ?></option><?php
583 foreach ( $providers as $provider_id => $provider_title ) : ?>
584 <option value="<?php
585 echo esc_attr( $provider_id ); ?>" <?php selected( $selected_provider_id, absint( $provider_id ) ); ?>><?php
586 echo esc_html( $provider_title ); ?></option><?php
587 endforeach; ?></select>
588 </div>
589 </div>
590 <div class="wpbc_appointment_services__content" aria-live="polite" aria-busy="true">
591 <div class="wpbc_appointment_services__provider_notice" hidden>
592 <span class="wpbc-bi-exclamation-circle" aria-hidden="true"></span>
593 <div><strong><?php esc_html_e( 'No Providers available', 'booking' ); ?></strong>
594 <span><?php esc_html_e( 'Create a Provider before assigning Services and accepting Appointments.', 'booking' ); ?></span></div>
595 <?php if ( $can_manage_providers ) : ?>
596 <a class="button button-secondary" href="<?php echo esc_url( admin_url( 'admin.php?page=wpbc-resources' ) ); ?>"><?php esc_html_e( 'Manage Providers', 'booking' ); ?></a>
597 <?php endif; ?>
598 </div>
599 <div id="wpbc_appointment_services_catalog"></div>
600 <noscript><p><?php esc_html_e( 'JavaScript is required to manage Services.', 'booking' ); ?></p></noscript>
601 </div>
602 </div>
603 <?php
604 }
605 }
606
607 add_action( 'wpbc_menu_created', array( new WPBC_Page_Appointment_Services(), '__construct' ) );
608