| @@ -28,14 +28,11 @@ | ||
| 28 | 28 | // Response AJAX parameters |
| 29 | 29 | $ajx_data_arr = array(); |
| 30 | 30 | $ajx_data_arr['status'] = 'ok'; |
| 31 | 31 | |
| 32 | - $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // 'wp-admin/admin.php?' | |
| 33 | - $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ | |
| 34 | - // Local parameters | |
| 35 | - $local_params = array(); | |
| 36 | - $local_params['is_from_admin_panel'] = ( false !== strpos( $server_http_referer_uri, $admin_uri ) ); // true | false | |
| 37 | - $local_params['user_id'] = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // 1 | |
| 32 | + // Local parameters | |
| 33 | + $local_params = array(); | |
| 34 | + $local_params['user_id'] = ( isset( $_REQUEST['wpbc_ajx_user_id'] ) ) ? intval( $_REQUEST['wpbc_ajx_user_id'] ) : wpbc_get_current_user_id(); // 1 | |
| 38 | 35 | |
| 39 | 36 | // Request parameters for the released Appointment and Resource Selector workflows. |
| 40 | 37 | $workflow_request_rules = array( |
| 41 | 38 | 'service_id' => array( 'validate' => 'd', 'default' => 0 ), |
| @@ -42,8 +39,9 @@ | ||
| 42 | 39 | 'appointment_service_required' => array( 'validate' => 'd', 'default' => 0 ), |
| 43 | 40 | 'appointment_context_token' => array( 'validate' => 'strong', 'default' => '' ), |
| 44 | 41 | 'resource_selector_required' => array( 'validate' => 'd', 'default' => 0 ), |
| 45 | 42 | 'resource_selector_context_token' => array( 'validate' => 'strong', 'default' => '' ), |
| 43 | + 'wpbc_admin_booking_nonce' => array( 'validate' => 'strong', 'default' => '' ), | |
| 46 | 44 | ); |
| 47 | 45 | |
| 48 | 46 | $user_request = new WPBC_AJX__REQUEST( array( // Using this class here only for escaping variables |
| 49 | 47 | 'db_option_name' => 'booking__wpbc_booking_create__request_params', // Not necessary, because we not save request, only sanitize it |
| @@ -78,15 +76,16 @@ | ||
| 78 | 76 | $request_prefix = 'calendar_request_params'; |
| 79 | 77 | |
| 80 | 78 | //$_REQUEST['calendar_request_params']['dates_ddmmyy_csv'] .= "'%2b(select+'box'+from(select+sleep(2)+from+dual+where+1=1*)a)%2b'-02-21+00:00:00"; |
| 81 | 79 | |
| 82 | - $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id'] | |
| 83 | - $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ | |
| 84 | - $request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters. | |
| 80 | + $request_params = $user_request->get_sanitized__in_request__value_or_default( $request_prefix ); // NOT Direct: $_REQUEST['calendar_request_params']['resource_id'] | |
| 81 | + $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ | |
| 82 | + $request_params['request_uri'] = $server_http_referer_uri; // Parameter needed for Error in booking saving and reloading calendar again with these actual parameters. | |
| 83 | + $is_authorized_admin_booking_request = wpbc_is_authorized_admin_booking_request( $request_params['wpbc_admin_booking_nonce'] ); | |
| 85 | 84 | |
| 86 | 85 | // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- CAPTCHA " > |
| 87 | 86 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 88 | - wpbc_captcha__in_ajx__check( $request_params, $local_params['is_from_admin_panel'], $_REQUEST[ $request_prefix ] ); | |
| 87 | + wpbc_captcha__in_ajx__check( $request_params, $is_authorized_admin_booking_request, $_REQUEST[ $request_prefix ] ); | |
| 89 | 88 | // </editor-fold> |
| 90 | 89 | |
| 91 | 90 | // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING_RESOURCE ID " > |
| 92 | 91 | if ( $request_params['resource_id'] <= 0 ) { |
| @@ -93,16 +92,13 @@ | ||
| 93 | 92 | $ajx_data_arr['status'] = 'error'; |
| 94 | 93 | $ajx_data_arr['status_error'] = 'resource_id_incorrect'; |
| 95 | 94 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized |
| 96 | 95 | $ajx_data_arr['ajx_after_action_message'] = 'Wrong ID of booking resource: ' . ' [ request ID: ' . $_REQUEST['calendar_request_params']['resource_id'] . ' | parsed ID: ' . $request_params['resource_id'] . ' ]'; |
| 97 | - $ajx_data_arr['ajx_after_action_message_status'] = 'error'; | |
| 98 | - wp_send_json( array( | |
| 99 | - 'ajx_data' => $ajx_data_arr, | |
| 100 | - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 101 | - 'ajx_search_params' => $_REQUEST[ $request_prefix ], | |
| 102 | - 'ajx_cleaned_params' => $request_params, | |
| 103 | - 'resource_id' => $request_params['resource_id'], | |
| 104 | - ) ); | |
| 96 | + $ajx_data_arr['ajx_after_action_message_status'] = 'error'; | |
| 97 | + wp_send_json( array( | |
| 98 | + 'ajx_data' => $ajx_data_arr, | |
| 99 | + 'resource_id' => $request_params['resource_id'], | |
| 100 | + ) ); | |
| 105 | 101 | } |
| 106 | 102 | // </editor-fold> |
| 107 | 103 | |
| 108 | 104 | $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */ |
| @@ -135,19 +131,20 @@ | ||
| 135 | 131 | $request_save_params['appointment_service_required'] = $request_params['appointment_service_required']; |
| 136 | 132 | $request_save_params['appointment_context_token'] = $request_params['appointment_context_token']; |
| 137 | 133 | $request_save_params['resource_selector_required'] = $request_params['resource_selector_required']; |
| 138 | 134 | $request_save_params['resource_selector_context_token'] = $request_params['resource_selector_context_token']; |
| 135 | + $request_save_params['wpbc_admin_booking_nonce'] = $request_params['wpbc_admin_booking_nonce']; | |
| 139 | 136 | $booking_save_arr = wpbc_booking_save( $request_save_params ); |
| 140 | 137 | |
| 141 | 138 | // <editor-fold defaultstate="collapsed" desc=" :: ERROR :: <- BOOKING " > |
| 142 | 139 | if ( 'ok' !== $booking_save_arr['ajx_data']['status'] ) { |
| 143 | 140 | |
| 144 | - wp_send_json( array( 'ajx_data' => $booking_save_arr['ajx_data'], | |
| 145 | - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 146 | - 'ajx_search_params' => $_REQUEST[ $request_prefix ], | |
| 147 | - 'ajx_cleaned_params' => $request_params, | |
| 148 | - 'resource_id' => $request_params['resource_id'] | |
| 149 | - )); | |
| 141 | + wp_send_json( | |
| 142 | + array( | |
| 143 | + 'ajx_data' => $booking_save_arr['ajx_data'], | |
| 144 | + 'resource_id' => $request_params['resource_id'], | |
| 145 | + ) | |
| 146 | + ); | |
| 150 | 147 | } |
| 151 | 148 | // </editor-fold> |
| 152 | 149 | |
| 153 | 150 | $ajx_data_arr = $booking_save_arr['ajx_data']; |
| @@ -306,9 +303,12 @@ | ||
| 306 | 303 | $validate_arr_rules['appointment_service_required'] = array( 'validate' => 'd', 'default' => 0 ); |
| 307 | 304 | $validate_arr_rules['appointment_context_token'] = array( 'validate' => 'strong', 'default' => '' ); |
| 308 | 305 | $validate_arr_rules['resource_selector_required'] = array( 'validate' => 'd', 'default' => 0 ); |
| 309 | 306 | $validate_arr_rules['resource_selector_context_token'] = array( 'validate' => 'strong', 'default' => '' ); |
| 307 | + $validate_arr_rules['wpbc_admin_booking_nonce'] = array( 'validate' => 'strong', 'default' => '' ); | |
| 310 | 308 | $re_cleaned_params = wpbc_sanitize_params_in_arr( $request_params, $validate_arr_rules ); |
| 309 | + $has_verified_appointment_context = false; | |
| 310 | + $has_verified_resource_selector_context = false; | |
| 311 | 311 | if ( ! empty( $re_cleaned_params['appointment_service_required'] ) && empty( $re_cleaned_params['service_id'] ) ) { |
| 312 | 312 | $ajx_data_arr['status'] = 'error'; |
| 313 | 313 | $ajx_data_arr['status_error'] = 'appointment_service_required'; |
| 314 | 314 | $ajx_data_arr['ajx_after_action_message'] = __( 'Please select a Service.', 'booking' ); |
| @@ -331,13 +331,14 @@ | ||
| 331 | 331 | $ajx_data_arr['ajx_after_action_message'] = $appointment_context_check->get_error_message(); |
| 332 | 332 | $ajx_data_arr['ajx_after_action_message_status'] = 'warning'; |
| 333 | 333 | return array( 'ajx_data' => $ajx_data_arr ); |
| 334 | 334 | } |
| 335 | + $has_verified_appointment_context = true; | |
| 335 | 336 | |
| 336 | 337 | // A client value cannot enable past Appointment creation; trust only the site-authored signed context. |
| 337 | 338 | $re_cleaned_params['allow_past'] = wpbc_booking_appointment_is_past_booking_enabled( $appointment_context_check ) ? 1 : 0; |
| 338 | 339 | } |
| 339 | - if ( ! empty( $re_cleaned_params['resource_selector_required'] ) ) { | |
| 340 | + if ( ! empty( $re_cleaned_params['resource_selector_required'] ) || ! empty( $re_cleaned_params['resource_selector_context_token'] ) ) { | |
| 340 | 341 | if ( ! function_exists( 'wpbc_booking_resource_selector_validate_submission_context' ) ) { |
| 341 | 342 | $resource_selector_context_check = new WP_Error( 'resource_selector_context_unavailable', __( 'The Booking Resource selection cannot be verified. Please reload the page and try again.', 'booking' ) ); |
| 342 | 343 | } else { |
| 343 | 344 | $resource_selector_context_check = wpbc_booking_resource_selector_validate_submission_context( |
| @@ -351,16 +352,15 @@ | ||
| 351 | 352 | $ajx_data_arr['ajx_after_action_message'] = $resource_selector_context_check->get_error_message(); |
| 352 | 353 | $ajx_data_arr['ajx_after_action_message_status'] = 'warning'; |
| 353 | 354 | return array( 'ajx_data' => $ajx_data_arr ); |
| 354 | 355 | } |
| 356 | + $has_verified_resource_selector_context = true; | |
| 355 | 357 | |
| 356 | 358 | // Trust only the site-authored signed selector context for public past bookings. |
| 357 | 359 | $re_cleaned_params['allow_past'] = wpbc_booking_resource_selector_is_past_booking_enabled( $resource_selector_context_check ) ? 1 : 0; |
| 358 | 360 | } |
| 359 | 361 | |
| 360 | - $admin_uri = ltrim( str_replace( get_site_url( null, '', 'admin' ), '', admin_url( 'admin.php?' ) ), '/' ); // wp-admin/admin.php? | |
| 361 | - | |
| 362 | - $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] ); | |
| 362 | + $re_cleaned_params['form_status'] = sanitize_key( $re_cleaned_params['form_status'] ); | |
| 363 | 363 | if ( 'preview' !== $re_cleaned_params['form_status'] ) { |
| 364 | 364 | $re_cleaned_params['form_status'] = 'published'; |
| 365 | 365 | } |
| 366 | 366 | // FixIn: 2026-02-05 - make preview/published available to form parsing/templates during this request. |
| @@ -377,10 +377,11 @@ | ||
| 377 | 377 | |
| 378 | 378 | // ----------------------------------------------------------------------------------------------------------------- |
| 379 | 379 | // Local parameters |
| 380 | 380 | // ----------------------------------------------------------------------------------------------------------------- |
| 381 | - $local_params = array(); | |
| 382 | - $local_params['is_from_admin_panel'] = ( false !== strpos( $re_cleaned_params['request_uri'], $admin_uri ) ); // true | false | |
| 381 | + $local_params = array(); | |
| 382 | + $is_authorized_admin_booking_request = wpbc_is_authorized_admin_booking_request( $re_cleaned_params['wpbc_admin_booking_nonce'] ); | |
| 383 | + $local_params['is_from_admin_panel'] = $is_authorized_admin_booking_request; | |
| 383 | 384 | $local_params['user_id'] = $re_cleaned_params['user_id']; // 1 |
| 384 | 385 | $local_params['sync_gid'] = $re_cleaned_params['sync_gid']; // '' |
| 385 | 386 | $local_params['is_approve_booking'] = $re_cleaned_params['is_approve_booking']; // 0 | 1 |
| 386 | 387 | $local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true |
| @@ -386,13 +387,8 @@ | ||
| 386 | 387 | $local_params['is_use_booking_recurrent_time'] = ( 1 === $re_cleaned_params['is_use_booking_recurrent_time'] ); // false | true |
| 387 | 388 | $request_action = isset( $_REQUEST['action'] ) && is_scalar( $_REQUEST['action'] ) |
| 388 | 389 | ? sanitize_key( (string) wp_unslash( $_REQUEST['action'] ) ) |
| 389 | 390 | : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 390 | - $is_authorized_admin_booking_request = $local_params['is_from_admin_panel'] | |
| 391 | - && is_user_logged_in() | |
| 392 | - && class_exists( 'WPBC_Add_Booking_Component' ) | |
| 393 | - && WPBC_Add_Booking_Component::current_user_can_add_booking() | |
| 394 | - && wpbc_is_mu_user_can_be_here( 'activated_user' ); | |
| 395 | 391 | $is_public_booking_create_request = wp_doing_ajax() |
| 396 | 392 | && 'wpbc_ajx_booking__create' === strtolower( $request_action ) |
| 397 | 393 | && ! $is_authorized_admin_booking_request; |
| 398 | 394 | |
| @@ -507,8 +503,9 @@ | ||
| 507 | 503 | // [ '2023-09-10', '2023-09-11' ] |
| 508 | 504 | $local_params['dates_only_sql_arr'] = wpbc_convert_dates_str__dd_mm_yyyy__to__yyyy_mm_dd( $re_cleaned_params["dates_ddmmyy_csv"] ); |
| 509 | 505 | $local_params['dates_only_sql_arr'] = explode( ',', $local_params['dates_only_sql_arr'] ); |
| 510 | 506 | |
| 507 | + $classic_context = array(); | |
| 511 | 508 | $has_verified_classic_context = false; |
| 512 | 509 | if ( ! empty( $re_cleaned_params['classic_booking_context_token'] ) && function_exists( 'wpbc_classic_booking_context_validate_submission' ) ) { |
| 513 | 510 | $classic_context = wpbc_classic_booking_context_validate_submission( |
| 514 | 511 | $re_cleaned_params['classic_booking_context_token'], |
| @@ -524,22 +521,38 @@ | ||
| 524 | 521 | $ajx_data_arr['ajx_after_action_message_status'] = 'warning'; |
| 525 | 522 | return array( 'ajx_data' => $ajx_data_arr ); |
| 526 | 523 | } |
| 527 | 524 | |
| 528 | - $has_verified_classic_context = true; | |
| 525 | + $has_verified_classic_context = true; | |
| 529 | 526 | $re_cleaned_params['allow_past'] = ! empty( $classic_context['allow_past'] ) ? 1 : 0; |
| 527 | + // Pass only the signed canonical set into final availability and persistence decisions. | |
| 528 | + $re_cleaned_params['aggregate_resource_id_arr'] = implode( ',', $classic_context['aggregate_resource_ids'] ); | |
| 530 | 529 | } |
| 531 | 530 | |
| 532 | - $has_verified_workflow_context = ( | |
| 533 | - ( ! empty( $re_cleaned_params['service_id'] ) && ! empty( $re_cleaned_params['appointment_context_token'] ) ) | |
| 534 | - || ( ! empty( $re_cleaned_params['resource_selector_required'] ) && ! empty( $re_cleaned_params['resource_selector_context_token'] ) ) | |
| 531 | + if ( $is_public_booking_create_request && ! $has_verified_classic_context ) { | |
| 532 | + $ajx_data_arr['status'] = 'error'; | |
| 533 | + $ajx_data_arr['status_error'] = 'classic_booking_context_required'; | |
| 534 | + $ajx_data_arr['ajx_after_action_message'] = __( 'The booking form context has expired. Please reload the page and try again.', 'booking' ); | |
| 535 | + $ajx_data_arr['ajx_after_action_message_status'] = 'warning'; | |
| 536 | + return array( 'ajx_data' => $ajx_data_arr ); | |
| 537 | + } | |
| 538 | + | |
| 539 | + if ( $has_verified_classic_context ) { | |
| 540 | + $workflow_context_error = wpbc_booking_create_validate_required_workflow( | |
| 541 | + $classic_context, | |
| 542 | + $has_verified_appointment_context, | |
| 543 | + $has_verified_resource_selector_context | |
| 535 | 544 | ); |
| 536 | - if ( $is_public_booking_create_request && ! $has_verified_classic_context && ! $has_verified_workflow_context ) { | |
| 537 | - $re_cleaned_params['allow_past'] = 0; | |
| 538 | - $re_cleaned_params['request_uri'] = remove_query_arg( 'allow_past', $re_cleaned_params['request_uri'] ); | |
| 545 | + if ( is_wp_error( $workflow_context_error ) ) { | |
| 546 | + $ajx_data_arr['status'] = 'error'; | |
| 547 | + $ajx_data_arr['status_error'] = $workflow_context_error->get_error_code(); | |
| 548 | + $ajx_data_arr['ajx_after_action_message'] = $workflow_context_error->get_error_message(); | |
| 549 | + $ajx_data_arr['ajx_after_action_message_status'] = 'warning'; | |
| 550 | + return array( 'ajx_data' => $ajx_data_arr ); | |
| 551 | + } | |
| 539 | 552 | } |
| 540 | - | |
| 541 | - if ( | |
| 553 | + | |
| 554 | + if ( | |
| 542 | 555 | ( ! empty( $local_params['time_override_arr'] ) ) |
| 543 | 556 | && ( 'times_availability' === $local_params['time_override_arr']['source'] ) |
| 544 | 557 | && ( count( array_filter( $local_params['dates_only_sql_arr'] ) ) > 1 ) |
| 545 | 558 | ) { |
| @@ -548,13 +561,16 @@ | ||
| 548 | 561 | |
| 549 | 562 | $local_params['is_show_payment_form'] = $re_cleaned_params["is_show_payment_form"]; |
| 550 | 563 | |
| 551 | 564 | // FixIn: 9.9.0.35. |
| 552 | - if ( $local_params['is_show_payment_form'] ) { | |
| 553 | - $local_params['is_show_payment_form'] = ( false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) ) | |
| 554 | - ? 0 | |
| 555 | - : $local_params['is_show_payment_form']; // 1|0 | |
| 556 | - } | |
| 565 | + if ( $local_params['is_show_payment_form'] ) { | |
| 566 | + $local_params['is_show_payment_form'] = ( | |
| 567 | + $is_authorized_admin_booking_request | |
| 568 | + && false !== strpos( $re_cleaned_params['request_uri'], 'is_show_payment_form=Off' ) | |
| 569 | + ) | |
| 570 | + ? 0 | |
| 571 | + : $local_params['is_show_payment_form']; // 1|0 | |
| 572 | + } | |
| 557 | 573 | |
| 558 | 574 | // Get EDIT booking data |
| 559 | 575 | $local_params['edit_resource_id'] = ''; |
| 560 | 576 | $local_params['skip_booking_id'] = ''; |
| @@ -1250,9 +1266,9 @@ | ||
| 1250 | 1266 | $sql_field_arr[] = array( 'name' => 'form', 'type' => '%s', 'value' => $form_data ); |
| 1251 | 1267 | $sql_field_arr[] = array( 'name' => 'booking_type', 'type' => '%d', 'value' => $create_params['resource_id'] ); |
| 1252 | 1268 | $sql_field_arr[] = array( 'name' => 'modification_date', 'type' => '%s', 'value' => gmdate( 'Y-m-d H:i:s' ) ); |
| 1253 | 1269 | $sql_field_arr[] = array( 'name' => 'sort_date', 'type' => '%s', 'value' => $create_params['dates_only_sql_arr'][0] . ' ' . $create_params['time_as_his_arr'][0] ); |
| 1254 | - $sql_field_arr[] = array( 'name' => 'hash', 'type' => 'MD5(%s)', 'value' => time() . '_' . wp_rand( 1000, 1000000 ) ); | |
| 1270 | + $sql_field_arr[] = array( 'name' => 'hash', 'type' => '%s', 'value' => wpbc_hash__generate_booking_hash() ); | |
| 1255 | 1271 | |
| 1256 | 1272 | |
| 1257 | 1273 | if ( |
| 1258 | 1274 | ( 0 == $create_params['is_edit_booking'] ) || // If not edit, then INSERT. |
| @@ -1273,12 +1289,15 @@ | ||
| 1273 | 1289 | $sql_prepare_arr['name'] = implode( ', ', $sql_prepare_arr['name'] ); |
| 1274 | 1290 | $sql_prepare_arr['type'] = implode( ', ', $sql_prepare_arr['type'] ); |
| 1275 | 1291 | /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare */ |
| 1276 | 1292 | $sql = $wpdb->prepare( "INSERT INTO {$wpdb->prefix}booking " . " ( {$sql_prepare_arr['name']} )" . " VALUES ( {$sql_prepare_arr['type']} )", $sql_prepare_arr['value'] ); |
| 1277 | - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 1278 | - if ( false === $wpdb->query( $sql ) ) { | |
| 1279 | - return array( 'status' => 'error', 'message' => 'Error. INSERT New Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql ); | |
| 1280 | - } | |
| 1293 | + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 1294 | + if ( false === $wpdb->query( $sql ) ) { | |
| 1295 | + return array( | |
| 1296 | + 'status' => 'error', | |
| 1297 | + 'message' => __( 'The booking could not be saved because of a database error. Please try again or contact the website administrator.', 'booking' ), | |
| 1298 | + ); | |
| 1299 | + } | |
| 1281 | 1300 | // Get ID of booking |
| 1282 | 1301 | $booking_id = (int) $wpdb->insert_id; |
| 1283 | 1302 | |
| 1284 | 1303 | } else { // Edit - UPDATE |
| @@ -1292,14 +1311,15 @@ | ||
| 1292 | 1311 | $sql_prepare_arr['set'] = implode( ', ', $sql_prepare_arr['set'] ); |
| 1293 | 1312 | |
| 1294 | 1313 | // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare |
| 1295 | 1314 | $sql = $wpdb->prepare( "UPDATE {$wpdb->prefix}booking SET {$sql_prepare_arr['set']} WHERE booking_id={$booking_id};", $sql_prepare_arr['value'] ); |
| 1296 | - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 1297 | - if ( false === $wpdb->query( $sql ) ) { | |
| 1298 | - return array( 'status' => 'error', | |
| 1299 | - 'message' => 'Error. UPDATE Exist Data in DB.' . ' FILE:' . __FILE__ . ' LINE:' . __LINE__ . ' SQL:' . $sql, | |
| 1300 | - ); | |
| 1301 | - } | |
| 1315 | + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter | |
| 1316 | + if ( false === $wpdb->query( $sql ) ) { | |
| 1317 | + return array( | |
| 1318 | + 'status' => 'error', | |
| 1319 | + 'message' => __( 'The booking could not be updated because of a database error. Please try again or contact the website administrator.', 'booking' ), | |
| 1320 | + ); | |
| 1321 | + } | |
| 1302 | 1322 | |
| 1303 | 1323 | // Check if dates previously was approved. |
| 1304 | 1324 | $slct_sql = "SELECT approved FROM {$wpdb->prefix}bookingdates WHERE booking_id IN ({$booking_id}) LIMIT 0,1"; |
| 1305 | 1325 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter |
| @@ -1590,9 +1610,9 @@ | ||
| 1590 | 1610 | * |
| 1591 | 1611 | * // Now get start/end times as seconds: [ 64800, 72000 ] |
| 1592 | 1612 | * $time_as_seconds_arr = wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $structured_booking_data_arr ); |
| 1593 | 1613 | */ |
| 1594 | - function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){ | |
| 1614 | + function wpbc_get_in_booking_form__time_to_book_as_seconds_arr( $booking_form_data__arr ){ | |
| 1595 | 1615 | |
| 1596 | 1616 | $selected_time_fields = wpbc_get__selected_time_fields__in_booking_form__as_arr( $booking_form_data__arr ); |
| 1597 | 1617 | |
| 1598 | 1618 | // 2.2 Get selected SECONDS to book --------------------------------------------------------------------------- |
| @@ -1631,12 +1651,69 @@ | ||
| 1631 | 1651 | } |
| 1632 | 1652 | } |
| 1633 | 1653 | } |
| 1634 | 1654 | |
| 1635 | - return $time_as_seconds_arr; | |
| 1636 | - } | |
| 1637 | - | |
| 1638 | - | |
| 1655 | + return $time_as_seconds_arr; | |
| 1656 | + } | |
| 1657 | + | |
| 1658 | + | |
| 1659 | + /** | |
| 1660 | + * Determine whether a booking-create request is an authorized administration workflow. | |
| 1661 | + * | |
| 1662 | + * The public booking action is intentionally available to signed-out visitors. A | |
| 1663 | + * Referer, request path, or caller-supplied Boolean therefore cannot establish an | |
| 1664 | + * administrator security context. The Add Booking UI supplies this user-bound nonce, | |
| 1665 | + * and the server independently rechecks login, capability, and MultiUser access. | |
| 1666 | + * | |
| 1667 | + * @param mixed $admin_booking_nonce Candidate Add Booking administration nonce. | |
| 1668 | + * | |
| 1669 | + * @return bool True only for an authorized Add Booking administration request. | |
| 1670 | + */ | |
| 1671 | + function wpbc_is_authorized_admin_booking_request( $admin_booking_nonce ) { | |
| 1672 | + | |
| 1673 | + if ( | |
| 1674 | + ! is_scalar( $admin_booking_nonce ) | |
| 1675 | + || '' === trim( (string) $admin_booking_nonce ) | |
| 1676 | + || ! is_user_logged_in() | |
| 1677 | + || ! wp_verify_nonce( sanitize_text_field( (string) $admin_booking_nonce ), 'wpbc_admin_booking_create' ) | |
| 1678 | + || ! class_exists( 'WPBC_Add_Booking_Component' ) | |
| 1679 | + || ! WPBC_Add_Booking_Component::current_user_can_add_booking() | |
| 1680 | + || ! wpbc_is_mu_user_can_be_here( 'activated_user' ) | |
| 1681 | + ) { | |
| 1682 | + return false; | |
| 1683 | + } | |
| 1684 | + | |
| 1685 | + return true; | |
| 1686 | + } | |
| 1687 | + | |
| 1688 | + | |
| 1689 | + /** | |
| 1690 | + * Require the signed workflow proof declared by a verified Booking Form context. | |
| 1691 | + * | |
| 1692 | + * Appointment and Resource Selector JavaScript flags are presentation hints only. | |
| 1693 | + * The signed Booking Form context identifies the server-rendered workflow, so removing | |
| 1694 | + * a flag or domain token cannot downgrade that form to a different workflow. | |
| 1695 | + * | |
| 1696 | + * @param array $classic_context Verified Booking Form context. | |
| 1697 | + * @param bool $has_verified_appointment_context Whether Service and Provider proof passed validation. | |
| 1698 | + * @param bool $has_verified_resource_selector_context Whether Resource Selector proof passed validation. | |
| 1699 | + * | |
| 1700 | + * @return true|WP_Error True when the required proof is present, otherwise a safe validation error. | |
| 1701 | + */ | |
| 1702 | + function wpbc_booking_create_validate_required_workflow( $classic_context, $has_verified_appointment_context, $has_verified_resource_selector_context ) { | |
| 1703 | + | |
| 1704 | + $booking_workflow = isset( $classic_context['booking_workflow'] ) ? sanitize_key( $classic_context['booking_workflow'] ) : ''; | |
| 1705 | + if ( 'appointment' === $booking_workflow && ! $has_verified_appointment_context ) { | |
| 1706 | + return new WP_Error( 'appointment_context_required', __( 'The Appointment selection has expired. Please start over and try again.', 'booking' ) ); | |
| 1707 | + } | |
| 1708 | + if ( 'resource_selector' === $booking_workflow && ! $has_verified_resource_selector_context ) { | |
| 1709 | + return new WP_Error( 'resource_selector_context_required', __( 'The Booking Resource selection has expired. Please start over and try again.', 'booking' ) ); | |
| 1710 | + } | |
| 1711 | + | |
| 1712 | + return true; | |
| 1713 | + } | |
| 1714 | + | |
| 1715 | + | |
| 1639 | 1716 | /** |
| 1640 | 1717 | * Remove administrator time-override values from an unauthorized booking request. |
| 1641 | 1718 | * |
| 1642 | 1719 | * The public booking endpoint intentionally accepts unauthenticated requests, so |