PluginProbe
Booking Calendar / 11.5
Booking Calendar v11.5
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.5, at includes/page-appointment-services/appointment_services__page.php

411 lines 18.3 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 && function_exists( 'wpbc_is_11_5_features_enabled' )
22 && wpbc_is_11_5_features_enabled();
23
24 if ( $is_resources_page_with_mode_guide ) {
25 wp_enqueue_style(
26 'wpbc-appointment-provider-tools',
27 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_provider_tools.css',
28 array(),
29 WP_BK_VERSION_NUM
30 );
31 }
32
33 if ( ! wpbc_appointment_services__is_page() ) {
34 return;
35 }
36
37 WPBC_UI_Listing::enqueue_styles();
38 wp_enqueue_style( 'wpbc-appointment-services-page', trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_services_page.css', array( 'wpbc-ui-listing' ), WP_BK_VERSION_NUM );
39 }
40 add_action( 'wpbc_enqueue_css_files', 'wpbc_appointment_services_enqueue_css', 66 );
41
42
43 /**
44 * Resolve the Service that should be selected when the catalog first opens.
45 *
46 * An explicit `service_id` request always wins. The first owner-visible active
47 * Service is selected automatically only on the Service and Provider Setup
48 * Wizard step, leaving ordinary Services-page visits unchanged.
49 *
50 * @return int Initial Service ID, or zero when the inspector should stay closed.
51 */
52 function wpbc_appointment_services_get_initial_service_id() {
53
54 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only selection state.
55 $requested_service_id = isset( $_GET['service_id'] ) && is_scalar( $_GET['service_id'] )
56 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only selection state.
57 ? absint( wp_unslash( $_GET['service_id'] ) )
58 : 0;
59
60 if ( $requested_service_id ) {
61 return $requested_service_id;
62 }
63
64 if (
65 ! function_exists( 'wpbc_setup_wizard_page__is_active_step' )
66 || ! wpbc_setup_wizard_page__is_active_step( 'service_provider' )
67 || ! function_exists( 'wpbc_appointment_services_repository' )
68 ) {
69 return 0;
70 }
71
72 $services = wpbc_appointment_services_repository()->list_items(
73 array(
74 'status' => 'active',
75 'sort_by' => 'service_id',
76 'sort_order' => 'asc',
77 'limit' => 1,
78 )
79 );
80
81 if ( is_wp_error( $services ) || empty( $services[0]['service_id'] ) ) {
82 return 0;
83 }
84
85 return absint( $services[0]['service_id'] );
86 }
87
88
89 /**
90 * Enqueue and configure the AJAX Appointment Services management client.
91 *
92 * @param string $where_to_load Booking Calendar asset context.
93 *
94 * @return void
95 */
96 function wpbc_appointment_services_enqueue_js( $where_to_load ) {
97 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_appointment_services__is_page() ) {
98 return;
99 }
100 wpbc_load_js__required_for_media_upload();
101 $service_listing = wpbc_appointment_services_get_catalog_listing();
102 $selected_service_id = wpbc_appointment_services_get_initial_service_id();
103 wp_enqueue_script( 'wpbc-appointment-services-page', trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/appointment_services_page.js', array(
104 'jquery',
105 'wpbc_all',
106 ), WP_BK_VERSION_NUM, array( 'in_footer' => WPBC_JS_IN_FOOTER ) );
107
108 wp_localize_script( 'wpbc-appointment-services-page', 'wpbc_appointment_services_config', array(
109 'ajax_url' => admin_url( 'admin-ajax.php' ),
110 'nonce' => wp_create_nonce( 'wpbc_appointment_services_ajax_nonce' ),
111 'actions' => array(
112 'list' => 'WPBC_AJX_APPOINTMENT_SERVICES_LIST',
113 'load' => 'WPBC_AJX_APPOINTMENT_SERVICE_LOAD',
114 'save' => 'WPBC_AJX_APPOINTMENT_SERVICE_SAVE',
115 'duplicate' => 'WPBC_AJX_APPOINTMENT_SERVICE_DUPLICATE',
116 'archive' => 'WPBC_AJX_APPOINTMENT_SERVICE_ARCHIVE',
117 ),
118 'selected_id' => $selected_service_id,
119 'focus_section' => wpbc_appointment_services_get_requested_focus_section(),
120 'pricing_available' => wpbc_appointment_services_is_pricing_available(),
121 'listing' => $service_listing->get_client_settings(),
122 'i18n' => array(
123 'loading' => __( 'Loading Services', 'booking' ) . '...',
124 'empty' => __( 'No Services yet', 'booking' ),
125 'empty_help' => __( 'Create the first Service to define what customers can book.', 'booking' ),
126 'no_providers' => __( 'No Providers available', 'booking' ),
127 'no_providers_help'=> __( 'Create a Provider before assigning Services and accepting Appointments.', 'booking' ),
128 'not_connected' => __( 'The Services database is not ready. Reload this page as an administrator or reactivate Booking Calendar to finish the database upgrade.', 'booking' ),
129 'load_failed' => __( 'Services could not be loaded.', 'booking' ),
130 'save_failed' => __( 'The Service could not be saved.', 'booking' ),
131 'duplicate_failed' => __( 'The Service could not be duplicated.', 'booking' ),
132 'archive_failed' => __( 'The Service could not be archived.', 'booking' ),
133 'confirm_archive' => __( 'Archive this Service?', 'booking' ),
134 'confirm_discard' => __( 'Discard unsaved Service changes?', 'booking' ),
135 'untitled' => __( 'Untitled Service', 'booking' ),
136 'new_service' => __( 'New Service', 'booking' ),
137 'active' => __( 'Active', 'booking' ),
138 'draft' => __( 'Draft', 'booking' ),
139 'archived' => __( 'Archived', 'booking' ),
140 'no_provider' => __( 'No Providers assigned', 'booking' ),
141 'no_availability' => __( 'No weekly availability', 'booking' ),
142 'more_providers' => __( 'more Providers', 'booking' ),
143 'available' => __( 'Available', 'booking' ),
144 'unavailable' => __( 'Unavailable', 'booking' ),
145 'available_providers' => __( 'Available Providers: %s', 'booking' ),
146 'no_available_providers' => __( 'No assigned Providers are available', 'booking' ),
147 'edit_availability' => __( 'Edit availability for %s', 'booking' ),
148 'edit_provider_availability' => __( 'Edit Provider availability', 'booking' ),
149 'column_id' => __( 'ID', 'booking' ),
150 'column_service' => __( 'Service', 'booking' ),
151 'column_duration' => __( 'Duration', 'booking' ),
152 'column_price' => __( 'Price', 'booking' ),
153 'column_providers' => __( 'Providers', 'booking' ),
154 'column_weekly_availability' => __( 'Weekly Availability', 'booking' ),
155 'column_status' => __( 'Status', 'booking' ),
156 'column_actions' => __( 'Actions', 'booking' ),
157 /* translators: %s: Duration in minutes. */
158 'duration_minutes' => __( '%s min', 'booking' ),
159 /* translators: 1: Buffer before in minutes, 2: Buffer after in minutes. */
160 'buffers_summary' => __( 'Buffers: %1$s / %2$s min', 'booking' ),
161 /* translators: 1: Buffer before in minutes, 2: Buffer after in minutes. */
162 'buffers_tooltip' => __( 'Buffer before: %1$s min; Buffer after: %2$s min', 'booking' ),
163 /* translators: 1: Service title, 2: Service description. */
164 'service_thumbnail_tooltip' => __( 'Title: %1$s — Description: %2$s', 'booking' ),
165 'no_description' => __( 'No description', 'booking' ),
166 'edit' => __( 'Edit Service', 'booking' ),
167 'archive' => __( 'Archive Service', 'booking' ),
168 'showing' => __( 'Showing %1$s–%2$s of %3$s Services', 'booking' ),
169 ),
170 'currency_symbol' => wpbc_appointment_services_is_pricing_available() && function_exists( 'wpbc_get_currency_symbol' ) ? wpbc_get_currency_symbol() : '',
171 'weekdays' => array(
172 __( 'Monday', 'booking' ),
173 __( 'Tuesday', 'booking' ),
174 __( 'Wednesday', 'booking' ),
175 __( 'Thursday', 'booking' ),
176 __( 'Friday', 'booking' ),
177 __( 'Saturday', 'booking' ),
178 __( 'Sunday', 'booking' ),
179 ),
180 ) );
181 }
182 add_action( 'wpbc_enqueue_js_files', 'wpbc_appointment_services_enqueue_js', 66 );
183
184 /**
185 * Render the global Add Service action in the Booking Calendar top toolbar.
186 *
187 * Creating a Service is a page-level action, while saving remains contextual
188 * to the Service editor in the right inspector.
189 *
190 * @param string $page_tag Current Booking Calendar page tag.
191 * @param string $active_page_tab Current active page tab.
192 * @param string $active_page_subtab Current active page subtab.
193 *
194 * @return void
195 */
196 function wpbc_appointment_services_render_top_toolbar( $page_tag, $active_page_tab, $active_page_subtab ) {
197 if (
198 ! wpbc_appointment_services__is_page()
199 || ! current_user_can( wpbc_appointment_services_get_manage_capability() )
200 ) {
201 return;
202 }
203 ?>
204 <div class="wpbc_ui_el__buttons_group wpbc_appointment_services__top_toolbar">
205 <button type="button" class="button button-secondary wpbc_appointment_services__add" disabled>
206 <span class="wpbc-bi-plus-lg" aria-hidden="true"></span>
207 <?php esc_html_e( 'Add Service', 'booking' ); ?>
208 </button>
209 </div>
210 <?php
211 }
212 add_action( 'wpbc_ui_el__top_nav__content_end', 'wpbc_appointment_services_render_top_toolbar', 20, 3 );
213
214
215 /** Appointment Services page definition for the shared Booking Calendar shell. */
216 class WPBC_Page_Appointment_Services extends WPBC_Page_Structure {
217
218 /**
219 * Return the WordPress admin page slug when the user may access Services.
220 *
221 * @return string Page slug, or a non-matching random value when unauthorized.
222 */
223 public function in_page() {
224 if ( ! current_user_can( wpbc_appointment_services_get_manage_capability() ) ) {
225 return (string) wp_rand( 100000, 1000000 );
226 }
227
228 return 'wpbc-services';
229 }
230
231 /**
232 * Define the Appointment Services tab and shared page-shell behavior.
233 *
234 * @return array<string,array<string,mixed>> Page tab definition.
235 */
236 public function tabs() {
237 $service_hint = wpbc_appointment_services_is_pricing_available()
238 ? __( 'Create Services and configure duration, buffers, price, Providers, and Booking Form.', 'booking' )
239 : __( 'Create Services and configure duration, buffers, Providers, and Booking Form.', 'booking' );
240
241 return array(
242 'appointment_services' => array(
243 'is_show_top_path' => true,
244 'is_show_top_navigation' => false,
245 'left_navigation__default_view_mode' => 'compact',
246 'right_vertical_sidebar__is_show' => true,
247 'right_vertical_sidebar__default_view_mode' => 'none',
248 'right_vertical_sidebar_compact__is_show' => true,
249 'top_path' => array( 'root_title' => false ),
250 'top_path_title' => __( 'Services', 'booking' ),
251 'title' => __( 'Services', 'booking' ),
252 'page_title' => __( 'Services', 'booking' ),
253 'hint' => $service_hint,
254 'font_icon' => 'wpbc-bi-grid',
255 'default' => true,
256 'disabled' => false,
257 'hided' => false,
258 'subtabs' => array(),
259 ),
260 );
261 }
262
263 /**
264 * Render the compact Settings and Help controls for the right sidebar.
265 *
266 * @return void
267 */
268 public function right_sidebar_compact_content() {
269 WPBC_UI_Sidebar_Panels::render_rightbar_tabs( array(
270 array(
271 'id' => 'wpbc_tab_service_settings',
272 'panel_id' => 'wpbc_service__inspector_settings',
273 'title' => __( 'Settings', 'booking' ),
274 'icon' => 'wpbc_icn_tune',
275 'selected' => true,
276 ),
277 array(
278 'id' => 'wpbc_tab_service_help',
279 'panel_id' => 'wpbc_service__inspector_help',
280 'title' => __( 'Help', 'booking' ),
281 'icon' => 'wpbc-bi-info-circle',
282 ),
283 ), array(
284 'aria_label' => __( 'Service Panels', 'booking' ),
285 'context' => 'appointment_services',
286 'class' => 'wpbc_appointment_services__rightbar_tabs',
287 ) );
288 }
289
290 /**
291 * Render the right-sidebar Settings and Help inspector panels.
292 *
293 * @return void
294 */
295 public function right_sidebar_content() {
296 ?>
297 <div class="wpbc_bfb__panel--library wpbc_rightbar_palette wpbc_appointment_services__rightbar"><?php
298 WPBC_UI_Sidebar_Panels::render_panel( array(
299 'id' => 'wpbc_service__inspector_settings',
300 'labelledby' => 'wpbc_tab_service_settings',
301 'class' => 'wpbc_appointment_services__inspector_settings',
302 ), 'wpbc_appointment_services_render_settings_panel' );
303 WPBC_UI_Sidebar_Panels::render_panel( array(
304 'id' => 'wpbc_service__inspector_help',
305 'labelledby' => 'wpbc_tab_service_help',
306 'class' => 'wpbc_appointment_services__inspector_help',
307 'hidden' => true,
308 ), 'wpbc_appointment_services_render_help_panel' );
309 ?></div><?php
310 }
311
312 /**
313 * Render the AJAX-driven Service filters and management table shell.
314 *
315 * @return void|false False when the current user is unauthorized.
316 */
317 public function content() {
318 if ( ! current_user_can( wpbc_appointment_services_get_manage_capability() ) ) {
319 return false;
320 }
321 $providers = wpbc_appointment_services_get_provider_options();
322 $resource_capability = class_exists( 'wpdev_bk_personal' )
323 ? get_bk_option( 'booking_user_role_resources' )
324 : get_bk_option( 'booking_user_role_settings' );
325 $can_manage_providers = $resource_capability && current_user_can( $resource_capability );
326 $selected_provider_id = isset( $_GET['provider_id'] ) ? absint( $_GET['provider_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
327 if ( ! isset( $providers[ $selected_provider_id ] ) ) {
328 $selected_provider_id = 0;
329 }
330 ?>
331 <div class="wpbc_appointment_services_page" data-wpbc-appointment-services-page="1">
332 <div class="wpbc_appointment_services__filters">
333 <div class="wpbc_appointment_services__status_filters" role="group" aria-label="<?php
334 esc_attr_e( 'Filter Services by status', 'booking' ); ?>">
335 <button type="button" class="wpbc_appointment_services__status_filter is-active"
336 data-service-status="all" aria-pressed="true"><span><?php
337 esc_html_e( 'All', 'booking' ); ?></span><strong data-service-count="all">0</strong>
338 </button>
339 <button type="button" class="wpbc_appointment_services__status_filter" data-service-status="active"
340 aria-pressed="false"><span><?php
341 esc_html_e( 'Active', 'booking' ); ?></span><strong data-service-count="active">0</strong>
342 </button>
343 <button type="button" class="wpbc_appointment_services__status_filter"
344 data-service-status="inactive" aria-pressed="false"><span><?php
345 esc_html_e( 'Draft', 'booking' ); ?></span><strong data-service-count="inactive">0</strong>
346 </button>
347 <button type="button" class="wpbc_appointment_services__status_filter"
348 data-service-status="archived" aria-pressed="false"><span><?php
349 esc_html_e( 'Archived', 'booking' ); ?></span><strong
350 data-service-count="archived">0</strong></button>
351 <span class="wpbc_appointment_services__filter_divider" aria-hidden="true"></span>
352 <span class="wpbc_appointment_services__provider_total"><span><?php
353 esc_html_e( 'Providers', 'booking' ); ?></span><strong data-provider-count>0</strong></span>
354 </div>
355 <div class="wpbc_appointment_services__filter_controls">
356 <div class="wpbc_appointment_services__search"><span class="wpbc-bi-search"
357 aria-hidden="true"></span><label
358 class="screen-reader-text" for="wpbc_service_search"><?php
359 esc_html_e( 'Search Services', 'booking' ); ?></label><input type="search"
360 id="wpbc_service_search"
361 placeholder="<?php
362 esc_attr_e( 'Search Services', 'booking' ); ?>"/>
363 </div>
364 <label class="screen-reader-text" for="wpbc_service_provider_filter"><?php
365 esc_html_e( 'Filter by Provider', 'booking' ); ?></label>
366 <select id="wpbc_service_provider_filter">
367 <option value="0"><?php
368 esc_html_e( 'All Providers', 'booking' ); ?></option><?php
369 foreach ( $providers as $provider_id => $provider_title ) : ?>
370 <option value="<?php
371 echo esc_attr( $provider_id ); ?>" <?php selected( $selected_provider_id, absint( $provider_id ) ); ?>><?php
372 echo esc_html( $provider_title ); ?></option><?php
373 endforeach; ?></select>
374 </div>
375 </div>
376 <div class="wpbc_appointment_services__content" aria-live="polite" aria-busy="true">
377 <div class="wpbc_appointment_services__provider_notice" hidden>
378 <span class="wpbc-bi-exclamation-circle" aria-hidden="true"></span>
379 <div><strong><?php esc_html_e( 'No Providers available', 'booking' ); ?></strong>
380 <span><?php esc_html_e( 'Create a Provider before assigning Services and accepting Appointments.', 'booking' ); ?></span></div>
381 <?php if ( $can_manage_providers ) : ?>
382 <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>
383 <?php endif; ?>
384 </div>
385 <div class="wpbc_appointment_services__loading is-visible" role="status" aria-hidden="false"
386 data-wpbc-appointment-services-loader="1">
387 <div class="wpbc_spins_loading_container">
388 <div class="wpbc_booking_form_spin_loader">
389 <div class="wpbc_spins_loader_wrapper">
390 <div class="wpbc_spin_loader_one_new"></div>
391 </div>
392 </div>
393 <span><?php
394 esc_html_e( 'Loading Services', 'booking' ); ?> ...</span></div>
395 </div>
396 <?php wpbc_appointment_services_get_catalog_listing()->render(); ?>
397 <div class="wpbc_appointment_services__empty" hidden><span class="wpbc-bi-grid"
398 aria-hidden="true"></span>
399 <h2><?php
400 esc_html_e( 'No Services yet', 'booking' ); ?></h2>
401 <p><?php
402 esc_html_e( 'Create the first Service to define what customers can book.', 'booking' ); ?></p>
403 </div>
404 </div>
405 </div>
406 <?php
407 }
408 }
409
410 add_action( 'wpbc_menu_created', array( new WPBC_Page_Appointment_Services(), '__construct' ) );
411