PluginProbe
Booking Calendar / 11.7
Booking Calendar v11.7
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-add-appointment / add_appointment__page.php

add_appointment__page.php in Booking Calendar 11.7, at includes/page-add-appointment/add_appointment__page.php

510 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Dedicated Add Appointment administrator page.
4 *
5 * @package Booking Calendar
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Check whether the Add Appointment tab is the current Booking Calendar page.
14 *
15 * @return bool True on the Add Appointment tab.
16 */
17 function wpbc_add_appointment_page_is_active() {
18 if ( ! is_admin() ) {
19 return false;
20 }
21
22 $page = isset( $_GET['page'] ) && ! is_array( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
23 $tab = isset( $_GET['tab'] ) && ! is_array( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
24
25 return 'wpbc' === $page && 'add-appointment' === $tab;
26 }
27
28 /**
29 * Return the canonical administrator URL for the Add Appointment workflow.
30 *
31 * @return string Absolute WordPress administrator URL.
32 */
33 function wpbc_add_appointment_page_get_url() {
34 return add_query_arg(
35 array(
36 'page' => 'wpbc',
37 'tab' => 'add-appointment',
38 ),
39 admin_url( 'admin.php' )
40 );
41 }
42
43 /**
44 * Read the page-level Appointment options from the current administrator URL.
45 *
46 * These values configure only the dedicated Add Appointment screen. Service
47 * duration, buffers, price, and Booking Form remain owned by the Service.
48 *
49 * @return array{calendar_months:int,auto_select_provider:bool,allow_past:bool} Normalized page options.
50 */
51 function wpbc_add_appointment_page_get_settings() {
52 $months = isset( $_GET['appointment_months'] ) && ! is_array( $_GET['appointment_months'] )
53 ? absint( wp_unslash( $_GET['appointment_months'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
54 : 1;
55 $months = min( 12, max( 1, $months ) );
56
57 return array(
58 'calendar_months' => $months,
59 'auto_select_provider' => isset( $_GET['appointment_auto_select_provider'] ) && ! is_array( $_GET['appointment_auto_select_provider'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
60 'allow_past' => isset( $_GET['allow_past'] ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
61 );
62 }
63
64 /**
65 * Print the legacy booking context required by calendar and submit scripts.
66 *
67 * Reusing the Add Booking minimum-date calculation keeps past-date behavior
68 * identical in both administrator workflows.
69 *
70 * @param bool $allow_past Whether past dates may be selected and submitted.
71 *
72 * @return void
73 */
74 function wpbc_add_appointment_page_print_booking_context( $allow_past ) {
75 $allow_past_date_arr = WPBC_Add_Booking_Component::get_allow_past_min_date_arr(
76 array(
77 'allow_past' => $allow_past ? 1 : 0,
78 'booking_hash' => '',
79 )
80 );
81 ?>
82 <script type="text/javascript">
83 if ( 'undefined' !== typeof _wpbc ) {
84 _wpbc.set_other_param( 'this_page_booking_hash', '' );
85 _wpbc.set_other_param( 'this_page_allow_past', <?php echo wp_json_encode( $allow_past ? 1 : 0 ); ?> );
86 _wpbc.set_other_param( 'this_page_allow_past_arr', <?php echo wp_json_encode( $allow_past_date_arr ); ?> );
87 }
88 </script>
89 <?php
90 }
91
92 /**
93 * Enqueue the existing Appointment controller and the page-only admin styles.
94 *
95 * @param string $where_to_load Booking Calendar asset context.
96 *
97 * @return void
98 */
99 function wpbc_add_appointment_page_enqueue_css( $where_to_load ) {
100 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_add_appointment_page_is_active() ) {
101 return;
102 }
103
104 wp_enqueue_style(
105 'wpbc-add-appointment-page',
106 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/add_appointment_page.css',
107 array( 'wpbc-all-admin', 'wpbc-booking-appointment' ),
108 WP_BK_VERSION_NUM
109 );
110 }
111 add_action( 'wpbc_enqueue_css_files', 'wpbc_add_appointment_page_enqueue_css', 73 );
112
113 /**
114 * Enqueue the existing AJAX Appointment controller and page inspector client.
115 *
116 * @param string $where_to_load Booking Calendar asset context.
117 *
118 * @return void
119 */
120 function wpbc_add_appointment_page_enqueue_js( $where_to_load ) {
121 if ( ! in_array( $where_to_load, array( 'admin', 'both' ), true ) || ! wpbc_add_appointment_page_is_active() ) {
122 return;
123 }
124
125 wp_enqueue_script(
126 'wpbc-add-appointment-page',
127 trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/add_appointment_page.js',
128 array( 'jquery', 'wpbc-booking-appointment', 'wpbc_all' ),
129 WP_BK_VERSION_NUM,
130 array( 'in_footer' => WPBC_JS_IN_FOOTER )
131 );
132 $settings = wpbc_add_appointment_page_get_settings();
133 $allow_past_date_arr = WPBC_Add_Booking_Component::get_allow_past_min_date_arr(
134 array(
135 'allow_past' => $settings['allow_past'] ? 1 : 0,
136 'booking_hash' => '',
137 )
138 );
139
140 wp_localize_script(
141 'wpbc-add-appointment-page',
142 'wpbc_add_appointment_page_config',
143 array(
144 'emptyLabel' => __( 'Not selected', 'booking' ),
145 'enabledLabel' => __( 'Enabled', 'booking' ),
146 'disabledLabel' => __( 'Disabled', 'booking' ),
147 'minutesLabel' => __( 'min', 'booking' ),
148 'allowPast' => $settings['allow_past'] ? 1 : 0,
149 'allowPastDateArr' => $allow_past_date_arr,
150 )
151 );
152 }
153 add_action( 'wpbc_enqueue_js_files', 'wpbc_add_appointment_page_enqueue_js', 73 );
154
155 /**
156 * Add Appointment page integrated into the shared Booking Calendar page shell.
157 */
158 class WPBC_Page_Add_Appointment extends WPBC_Page_Structure {
159
160 /**
161 * Place the page beside Booking Listing, Timeline, and Add Booking.
162 *
163 * @return string Parent page slug.
164 */
165 public function in_page() {
166 if ( ! WPBC_Add_Booking_Component::current_user_can_add_booking() ) {
167 return (string) wp_rand( 100000, 1000000 );
168 }
169
170 return 'wpbc';
171 }
172
173 /**
174 * Register the released Appointment tab and shared inspector layout.
175 *
176 * @return array<string,array<string,mixed>> Tab definition.
177 */
178 public function tabs() {
179 return array(
180 'add-appointment' => array(
181 'is_show_top_path' => true,
182 'is_show_top_navigation' => true,
183 'left_navigation__default_view_mode' => 'min',
184 'right_vertical_sidebar__is_show' => true,
185 'right_vertical_sidebar__default_view_mode' => '',
186 'right_vertical_sidebar_compact__is_show' => true,
187 'page_title' => __( 'Add New Appointment', 'booking' ),
188 'page_description' => __( 'Select a Service and Provider, then create the Appointment with the existing Booking Calendar form.', 'booking' ),
189 'title' => __( 'Add Appointment', 'booking' ),
190 'hint' => __( 'Add Appointment', 'booking' ),
191 'font_icon' => 'wpbc-bi-plus-circle-dotted wpbc_icn_rotate_180',
192 'default' => false,
193 'disabled' => false,
194 'hided' => false,
195 'subtabs' => array(),
196 ),
197 );
198 }
199
200 /**
201 * Render compact controls for the Appointment inspector.
202 *
203 * @return void
204 */
205 public function right_sidebar_compact_content() {
206 WPBC_UI_Sidebar_Panels::render_rightbar_tabs(
207 array(
208 array(
209 'id' => 'wpbc_tab_add_appointment_overview',
210 'panel_id' => 'wpbc_add_appointment__inspector_overview',
211 'title' => __( 'Settings', 'booking' ),
212 'icon' => 'wpbc_icn_tune',
213 'selected' => true,
214 ),
215 array(
216 'id' => 'wpbc_tab_add_appointment_help',
217 'panel_id' => 'wpbc_add_appointment__inspector_help',
218 'title' => __( 'Help', 'booking' ),
219 'icon' => 'wpbc-bi-info-circle',
220 ),
221 ),
222 array(
223 'aria_label' => __( 'Add Appointment Panels', 'booking' ),
224 'context' => 'add_appointment',
225 'class' => 'wpbc_add_appointment__rightbar_tabs',
226 )
227 );
228 }
229
230 /**
231 * Render the standard Builder-style Appointment inspector panels.
232 *
233 * @return void
234 */
235 public function right_sidebar_content() {
236 ?>
237 <div class="wpbc_bfb__panel--library wpbc_rightbar_palette wpbc_add_appointment__rightbar">
238 <?php
239 WPBC_UI_Sidebar_Panels::render_panel(
240 array(
241 'id' => 'wpbc_add_appointment__inspector_overview',
242 'labelledby' => 'wpbc_tab_add_appointment_overview',
243 'class' => 'wpbc_add_appointment__inspector_overview',
244 ),
245 array( $this, 'render_overview_panel' )
246 );
247 WPBC_UI_Sidebar_Panels::render_panel(
248 array(
249 'id' => 'wpbc_add_appointment__inspector_help',
250 'labelledby' => 'wpbc_tab_add_appointment_help',
251 'class' => 'wpbc_add_appointment__inspector_help',
252 'hidden' => true,
253 ),
254 array( $this, 'render_help_panel' )
255 );
256 ?>
257 </div>
258 <?php
259 }
260
261 /**
262 * Render the contextual workflow panel.
263 *
264 * @return void
265 */
266 public function render_overview_panel() {
267 $settings = wpbc_add_appointment_page_get_settings();
268 WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Appointment Settings', 'booking' ), __( 'Page-level options for this administrator booking.', 'booking' ) );
269 ?>
270 <div class="wpbc_bfb__inspector__body wpbc_add_appointment__inspector_body">
271 <?php
272 WPBC_UI_Sidebar_Panels::render_collapsible_group(
273 array(
274 'id' => 'wpbc_add_appointment_summary_group',
275 'group' => 'add-appointment-summary',
276 'title' => __( 'Appointment Summary', 'booking' ),
277 'open' => true,
278 ),
279 array( $this, 'render_summary_content' )
280 );
281 WPBC_UI_Sidebar_Panels::render_collapsible_group(
282 array(
283 'id' => 'wpbc_add_appointment_booking_tools_group',
284 'group' => 'add-appointment-booking-tools',
285 'title' => __( 'Booking tools', 'booking' ),
286 'open' => false,
287 ),
288 function () {
289 $send_emails = get_bk_option( 'booking_send_emails_off_addbooking' ) !== 'On';
290 wpbc_render_admin_cost_correction_control(
291 array(
292 'input_id' => 'wpbc_add_appointment_cost_correction',
293 'wrapper_class' => 'wpbc_add_appointment__setting_row wpbc_add_appointment__cost_correction',
294 'label_class' => 'wpbc_add_appointment__cost_correction_label',
295 'control_class' => 'wpbc_add_appointment__cost_correction_control',
296 'help_class' => 'wpbc_add_appointment__tool_help',
297 'help' => __( 'Leave the number empty to use the calculated Appointment cost. Enter an exact total to replace the final cost for this Appointment.', 'booking' ),
298 )
299 );
300 ?>
301 <div class="wpbc_add_appointment__setting_row wpbc_add_appointment__setting_row--toggle">
302 <?php
303 wpbc_flex_toggle(
304 array(
305 'id' => 'is_send_email_for_pending',
306 'name' => 'is_send_email_for_pending',
307 'label' => array( 'title' => __( 'Emails sending', 'booking' ) ),
308 'value' => 'On',
309 'selected' => $send_emails,
310 'legend' => __( 'Send email notifications for this Appointment', 'booking' ),
311 )
312 );
313 ?>
314 <p><?php esc_html_e( 'Send the normal Booking Calendar notifications when this Appointment is created.', 'booking' ); ?></p>
315 </div>
316 <div class="wpbc_add_appointment__panel_actions wpbc_add_appointment__tool_actions">
317 <button type="button" class="button wpbc_add_appointment__autofill" data-wpbc-add-appointment-autofill disabled><?php esc_html_e( 'Auto-fill form', 'booking' ); ?></button>
318 <button type="button" class="button wpbc_add_appointment__start_over" data-wpbc-add-appointment-start-over hidden><?php esc_html_e( 'Start over', 'booking' ); ?></button>
319 </div>
320 <p class="wpbc_add_appointment__tool_help"><?php esc_html_e( 'Auto-fill becomes available after the Booking Form loads. It fills sample contact details but never submits the Appointment.', 'booking' ); ?></p>
321 <?php
322 }
323 );
324 WPBC_UI_Sidebar_Panels::render_collapsible_group(
325 array(
326 'id' => 'wpbc_add_appointment_booking_options_group',
327 'group' => 'add-appointment-booking-options',
328 'title' => __( 'Booking options', 'booking' ),
329 'open' => false,
330 ),
331 function () use ( $settings ) {
332 ?>
333 <form class="wpbc_add_appointment__settings_form" method="get" action="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>">
334 <input type="hidden" name="page" value="wpbc">
335 <input type="hidden" name="tab" value="add-appointment">
336 <div class="wpbc_add_appointment__setting_row">
337 <label for="wpbc_add_appointment__months"><strong><?php esc_html_e( 'Calendar months', 'booking' ); ?></strong></label>
338 <select id="wpbc_add_appointment__months" name="appointment_months">
339 <?php foreach ( array( 1, 2, 3, 4, 6, 12 ) as $months ) : ?>
340 <option value="<?php echo absint( $months ); ?>" <?php selected( $settings['calendar_months'], $months ); ?>><?php echo absint( $months ); ?></option>
341 <?php endforeach; ?>
342 </select>
343 <p><?php esc_html_e( 'Number of months shown in the Provider calendar.', 'booking' ); ?></p>
344 </div>
345 <div class="wpbc_add_appointment__setting_row wpbc_add_appointment__setting_row--toggle">
346 <?php
347 wpbc_flex_toggle(
348 array(
349 'id' => 'wpbc_add_appointment__auto_provider',
350 'name' => 'appointment_auto_select_provider',
351 'label' => array( 'title' => __( 'Auto-select the only Provider', 'booking' ) ),
352 'value' => '1',
353 'selected' => $settings['auto_select_provider'],
354 'legend' => __( 'Auto-select the only compatible Provider', 'booking' ),
355 )
356 );
357 ?>
358 <p><?php esc_html_e( 'Skip Provider selection when only one compatible Provider exists.', 'booking' ); ?></p>
359 </div>
360 <div class="wpbc_add_appointment__setting_row wpbc_add_appointment__setting_row--toggle">
361 <?php
362 wpbc_flex_toggle(
363 array(
364 'id' => 'wpbc_add_appointment__allow_past',
365 'name' => 'allow_past',
366 'label' => array( 'title' => __( 'Allow booking in the past', 'booking' ) ),
367 'value' => '1',
368 'selected' => $settings['allow_past'],
369 'legend' => __( 'Allow booking in the past', 'booking' ),
370 )
371 );
372 ?>
373 <p><?php esc_html_e( 'Use only for historical or manually entered Appointments.', 'booking' ); ?></p>
374 </div>
375 <button type="submit" class="button button-primary wpbc_add_appointment__apply_settings"><?php esc_html_e( 'Apply settings', 'booking' ); ?></button>
376 <p class="wpbc_add_appointment__settings_notice"><?php esc_html_e( 'Applying settings restarts the current selection.', 'booking' ); ?></p>
377 </form>
378 <?php
379 }
380 );
381 WPBC_UI_Sidebar_Panels::render_collapsible_group(
382 array(
383 'id' => 'wpbc_add_appointment_manage_setup_group',
384 'group' => 'add-appointment-manage-setup',
385 'title' => __( 'Manage setup', 'booking' ),
386 'open' => false,
387 ),
388 function () {
389 ?>
390 <p><?php
391 if ( wpbc_appointment_services_is_pricing_available() ) {
392 esc_html_e( 'Services define duration, buffers, price, and Booking Form. Providers retain their existing calendars and availability.', 'booking' );
393 } else {
394 esc_html_e( 'Services define duration, buffers, and Booking Form. Providers retain their existing calendars and availability.', 'booking' );
395 }
396 ?></p>
397 <div class="wpbc_add_appointment__panel_actions">
398 <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=wpbc-services' ) ); ?>"><?php esc_html_e( 'Manage Services', 'booking' ); ?></a>
399 <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=wpbc-resources' ) ); ?>"><?php esc_html_e( 'Manage Providers', 'booking' ); ?></a>
400 </div>
401 <?php
402 }
403 );
404 do_action( 'wpbc_admin_add_appointment_overview_panel' );
405 ?>
406 </div>
407 <?php
408 }
409
410 /**
411 * Render a live, read-only summary of the current Appointment stage.
412 *
413 * @return void
414 */
415 public function render_summary_content() {
416 ?>
417 <dl class="wpbc_add_appointment__summary" aria-live="polite">
418 <div class="wpbc_add_appointment__summary_status"><dt><?php esc_html_e( 'Current step', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="step"><?php esc_html_e( 'Service', 'booking' ); ?></dd></div>
419 <div><dt><?php esc_html_e( 'Service', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="service">&mdash;</dd></div>
420 <div><dt><?php esc_html_e( 'Provider', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="provider">&mdash;</dd></div>
421 <div><dt><?php esc_html_e( 'Duration', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="duration">&mdash;</dd></div>
422 <div><dt><?php esc_html_e( 'Buffers (before / after)', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="buffers">&mdash;</dd></div>
423 <?php if ( wpbc_appointment_services_is_pricing_available() ) : ?>
424 <div><dt><?php esc_html_e( 'Service price', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="price">&mdash;</dd></div>
425 <?php endif; ?>
426 <div><dt><?php esc_html_e( 'Date', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="date">&mdash;</dd></div>
427 <div><dt><?php esc_html_e( 'Start time', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="time">&mdash;</dd></div>
428 <div><dt><?php esc_html_e( 'Customer', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="customer">&mdash;</dd></div>
429 <div><dt><?php esc_html_e( 'Booking Form', 'booking' ); ?></dt><dd data-wpbc-add-appointment-summary="form">&mdash;</dd></div>
430 <div>
431 <dt><?php esc_html_e( 'Email notifications', 'booking' ); ?></dt>
432 <dd>
433 <a href="#wpbc_add_appointment_booking_tools_group"
434 class="wpbc_add_appointment__summary_link"
435 data-wpbc-add-appointment-open-group="add-appointment-booking-tools"
436 data-wpbc-add-appointment-focus="#is_send_email_for_pending"
437 data-wpbc-add-appointment-summary="emails"
438 aria-label="<?php esc_attr_e( 'Change email notification setting', 'booking' ); ?>">&mdash;</a>
439 </dd>
440 </div>
441 </dl>
442 <?php do_action( 'wpbc_admin_add_appointment_summary_panel' ); ?>
443 <?php
444 }
445
446 /**
447 * Render contextual help without duplicating Booking Calendar settings.
448 *
449 * @return void
450 */
451 public function render_help_panel() {
452 WPBC_UI_Sidebar_Panels::render_inspector_header( __( 'Appointment Help', 'booking' ), __( 'This page creates a normal Booking with an attached Service snapshot.', 'booking' ) );
453 ?>
454 <div class="wpbc_bfb__inspector__body wpbc_bfb__inspector__body--content wpbc_add_appointment__help_body">
455 <div class="wpbc_add_appointment__hint"><span class="wpbc_icn_info_outline" aria-hidden="true"></span><p><?php esc_html_e( 'Availability belongs to the selected Provider booking resource. Service duration and buffers are validated before submission.', 'booking' ); ?></p></div>
456 <div class="wpbc_add_appointment__help_section">
457 <h4><?php esc_html_e( 'Appointment Flow', 'booking' ); ?></h4>
458 <ol class="wpbc_add_appointment__steps">
459 <li><span>1</span><?php esc_html_e( 'Choose a Service', 'booking' ); ?></li>
460 <li><span>2</span><?php esc_html_e( 'Choose a Provider', 'booking' ); ?></li>
461 <li><span>3</span><?php esc_html_e( 'Select date, time, and details', 'booking' ); ?></li>
462 </ol>
463 </div>
464 <div class="wpbc_add_appointment__help_section">
465 <h4><?php esc_html_e( 'When to use this page', 'booking' ); ?></h4>
466 <p><?php esc_html_e( 'Use Add Appointment for the guided Service-first workflow. Use Add Booking for unrestricted resource-first administration.', 'booking' ); ?></p>
467 </div>
468 <a class="button wpbc_add_appointment__help_action" href="<?php echo esc_url( wpbc_get_new_booking_url() ); ?>"><?php esc_html_e( 'Open Add Booking', 'booking' ); ?></a>
469 <?php do_action( 'wpbc_admin_add_appointment_help_panel' ); ?>
470 </div>
471 <?php
472 }
473
474 /**
475 * Render the existing AJAX Appointment component inside the admin canvas.
476 *
477 * @return void|false False when the current user cannot add bookings.
478 */
479 public function content() {
480 do_action( 'wpbc_hook_add_booking_page_header', 'add_appointment' );
481
482 if ( ! wpbc_is_mu_user_can_be_here( 'activated_user' ) || ! WPBC_Add_Booking_Component::current_user_can_add_booking() ) {
483 return false;
484 }
485
486 if ( function_exists( 'wpbc_js_for_bookings_page' ) ) {
487 wpbc_js_for_bookings_page();
488 }
489
490 $settings = wpbc_add_appointment_page_get_settings();
491 $config = array(
492 'cal_count' => $settings['calendar_months'],
493 'auto_select_provider' => $settings['auto_select_provider'],
494 'allow_past' => $settings['allow_past'],
495 );
496 $config = (array) apply_filters( 'wpbc_admin_add_appointment_config', $config, $settings );
497 wpbc_add_appointment_page_print_booking_context( $settings['allow_past'] );
498 ?>
499 <div class="wpbc_add_appointment_page wpdevelop" data-wpbc-add-appointment-page="1">
500 <div class="wpbc_add_appointment__canvas">
501 <?php echo wpbc_booking_appointment_shortcode( $config ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
502 </div>
503 </div>
504 <?php
505
506 do_action( 'wpbc_hook_add_booking_page_footer', 'add_appointment' );
507 }
508 }
509 add_action( 'wpbc_menu_created', array( new WPBC_Page_Add_Appointment(), '__construct' ) );
510