| 1 |
<?php |
| 2 |
/** |
| 3 |
* AJAX list endpoint for the template-driven Appointment Services catalog. |
| 4 |
* |
| 5 |
* @package Booking Calendar |
| 6 |
* @since 11.6.0 |
| 7 |
*/ |
| 8 |
|
| 9 |
if ( ! defined( 'ABSPATH' ) ) { |
| 10 |
exit; |
| 11 |
} |
| 12 |
|
| 13 |
/** |
| 14 |
* Return a safe request sequence from an untrusted payload. |
| 15 |
* |
| 16 |
* @param mixed $request_values Untrusted request values. |
| 17 |
* |
| 18 |
* @return int Non-negative request sequence or zero. |
| 19 |
*/ |
| 20 |
function wpbc_appointment_services_get_catalog_request_id( $request_values ) { |
| 21 |
if ( ! is_array( $request_values ) || ! isset( $request_values['request_id'] ) || ! is_scalar( $request_values['request_id'] ) ) { |
| 22 |
return 0; |
| 23 |
} |
| 24 |
|
| 25 |
return preg_match( '/^\d+$/', (string) $request_values['request_id'] ) ? (int) $request_values['request_id'] : 0; |
| 26 |
} |
| 27 |
|
| 28 |
/** |
| 29 |
* Send a normalized Services catalog error. |
| 30 |
* |
| 31 |
* @param int $request_id Client request sequence. |
| 32 |
* @param WP_Error $error Safe error. |
| 33 |
* @param int $status HTTP status. |
| 34 |
* @param bool $retryable Whether the browser may retry. |
| 35 |
* |
| 36 |
* @return void Terminates the AJAX request. |
| 37 |
*/ |
| 38 |
function wpbc_appointment_services_send_catalog_error( $request_id, $error, $status, $retryable = false ) { |
| 39 |
wp_send_json( |
| 40 |
WPBC_UI_Catalog_Response::from_wp_error( 'appointment_services_catalog', $request_id, $error, $retryable ), |
| 41 |
absint( $status ) |
| 42 |
); |
| 43 |
} |
| 44 |
|
| 45 |
/** |
| 46 |
* Serve the authorized Service list through the shared catalog contract. |
| 47 |
* |
| 48 |
* Transport authorization and request normalization stay here. The Service |
| 49 |
* repository owns SQL and ownership, while the DTO owns the item contract. |
| 50 |
* |
| 51 |
* @return void Terminates the AJAX request. |
| 52 |
*/ |
| 53 |
function wpbc_appointment_services_ajax_list() { |
| 54 |
$configuration = WPBC_UI_Catalog_Registry::get_instance()->get_configuration( 'appointment_services_catalog' ); |
| 55 |
if ( empty( $configuration ) ) { |
| 56 |
wpbc_appointment_services_send_catalog_error( 0, new WP_Error( 'wpbc_appointment_services_unavailable', __( 'The Services catalog is unavailable.', 'booking' ) ), 503, true ); |
| 57 |
} |
| 58 |
|
| 59 |
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified immediately below. |
| 60 |
$raw_request = is_array( $_POST ) ? wp_unslash( $_POST ) : array(); |
| 61 |
$request_id = wpbc_appointment_services_get_catalog_request_id( $raw_request ); |
| 62 |
if ( false === check_ajax_referer( $configuration['nonce_name'], 'nonce', false ) ) { |
| 63 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_invalid_nonce', __( 'Security check failed.', 'booking' ) ), 403 ); |
| 64 |
} |
| 65 |
if ( ! current_user_can( wpbc_appointment_services_get_manage_capability() ) ) { |
| 66 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_forbidden', __( 'You do not have permission to view Services.', 'booking' ) ), 403 ); |
| 67 |
} |
| 68 |
|
| 69 |
$preference_action = isset( $raw_request['preference_action'] ) && is_scalar( $raw_request['preference_action'] ) ? sanitize_key( (string) $raw_request['preference_action'] ) : ''; |
| 70 |
if ( ! in_array( $preference_action, array( '', 'save', 'reset' ), true ) ) { |
| 71 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_invalid_preferences', __( 'The catalog preference request is invalid.', 'booking' ) ), 400 ); |
| 72 |
} |
| 73 |
$preference_revision = isset( $raw_request['preference_revision'] ) && is_scalar( $raw_request['preference_revision'] ) && preg_match( '/^\d+$/', (string) $raw_request['preference_revision'] ) |
| 74 |
? (string) $raw_request['preference_revision'] |
| 75 |
: '0'; |
| 76 |
if ( isset( $raw_request['preferences_only'] ) && ( ! is_scalar( $raw_request['preferences_only'] ) || ! in_array( (string) $raw_request['preferences_only'], array( '0', '1' ), true ) ) ) { |
| 77 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_invalid_preferences', __( 'The catalog preference request is invalid.', 'booking' ) ), 400 ); |
| 78 |
} |
| 79 |
$preferences_only = isset( $raw_request['preferences_only'] ) && '1' === (string) $raw_request['preferences_only']; |
| 80 |
if ( '' !== $preference_action && '0' === $preference_revision ) { |
| 81 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_invalid_preferences', __( 'The catalog preference revision is invalid.', 'booking' ) ), 400 ); |
| 82 |
} |
| 83 |
if ( $preferences_only && 'save' !== $preference_action ) { |
| 84 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_invalid_preferences', __( 'The catalog preference request is invalid.', 'booking' ) ), 400 ); |
| 85 |
} |
| 86 |
if ( 'reset' === $preference_action && ! WPBC_UI_Catalog_Preferences::reset( 'appointment_services_catalog', 0, $preference_revision ) ) { |
| 87 |
wpbc_appointment_services_send_catalog_error( $request_id, new WP_Error( 'wpbc_appointment_services_preference_reset_failed', __( 'The catalog preferences could not be reset.', 'booking' ) ), 500, true ); |
| 88 |
} |
| 89 |
|
| 90 |
$stored_preferences = WPBC_UI_Catalog_Preferences::load( 'appointment_services_catalog' ); |
| 91 |
$shared_keys = array( 'request_id', 'page_number', 'items_per_page', 'sort_by', 'sort_order', 'search', 'visible_columns', 'column_order', 'template_pack' ); |
| 92 |
$shared_request = WPBC_UI_Catalog_Request::create( |
| 93 |
$configuration, |
| 94 |
array_intersect_key( $raw_request, array_fill_keys( $shared_keys, true ) ), |
| 95 |
$stored_preferences |
| 96 |
); |
| 97 |
if ( is_wp_error( $shared_request ) ) { |
| 98 |
wpbc_appointment_services_send_catalog_error( $request_id, $shared_request, 400 ); |
| 99 |
} |
| 100 |
|
| 101 |
$service_values = array( |
| 102 |
'status' => isset( $stored_preferences['status'] ) ? $stored_preferences['status'] : 'all', |
| 103 |
'resource_id' => isset( $stored_preferences['resource_id'] ) ? $stored_preferences['resource_id'] : 0, |
| 104 |
); |
| 105 |
foreach ( array( 'status', 'resource_id' ) as $service_key ) { |
| 106 |
if ( array_key_exists( $service_key, $raw_request ) ) { |
| 107 |
$service_values[ $service_key ] = $raw_request[ $service_key ]; |
| 108 |
} |
| 109 |
} |
| 110 |
$service_request = WPBC_Appointment_Services_Catalog_Request::create( $service_values ); |
| 111 |
if ( is_wp_error( $service_request ) ) { |
| 112 |
wpbc_appointment_services_send_catalog_error( $request_id, $service_request, 400 ); |
| 113 |
} |
| 114 |
|
| 115 |
if ( 'save' === $preference_action ) { |
| 116 |
$preference_result = WPBC_UI_Catalog_Preferences::save( |
| 117 |
'appointment_services_catalog', |
| 118 |
$shared_request, |
| 119 |
array( |
| 120 |
'status' => $service_request->get( 'status', 'all' ), |
| 121 |
'resource_id' => $service_request->get( 'resource_id', 0 ), |
| 122 |
), |
| 123 |
0, |
| 124 |
$preference_revision |
| 125 |
); |
| 126 |
if ( is_wp_error( $preference_result ) ) { |
| 127 |
wpbc_appointment_services_send_catalog_error( $request_id, $preference_result, 400 ); |
| 128 |
} |
| 129 |
} |
| 130 |
if ( $preferences_only ) { |
| 131 |
wp_send_json( array( 'success' => true, 'request_id' => $request_id ), 200 ); |
| 132 |
} |
| 133 |
|
| 134 |
$response = ( new WPBC_Appointment_Services_Catalog_Provider( wpbc_appointment_services_get_data_provider(), null, $service_request ) )->get_response( $shared_request ); |
| 135 |
if ( is_wp_error( $response ) ) { |
| 136 |
wpbc_appointment_services_send_catalog_error( $request_id, $response, 500, true ); |
| 137 |
} |
| 138 |
|
| 139 |
wp_send_json( $response->to_array(), 200 ); |
| 140 |
} |
| 141 |
add_action( 'wp_ajax_WPBC_AJX_APPOINTMENT_SERVICES_LIST', 'wpbc_appointment_services_ajax_list' ); |
| 142 |
|