| 4329 | |
| 4330 | function wp_ajax_wp_privacy_export_personal_data() { |
| 4331 | check_ajax_referer( 'wp-privacy-export-personal-data', 'security' ); |
| 4332 | |
| 4333 | if ( ! current_user_can( 'manage_options' ) ) { |
| 4334 | wp_send_json_error( 'access denied' ); |
| 4335 | } |
| 4336 | |
| 4337 | $email_address = sanitize_text_field( $_POST['email'] ); |
| 4338 | $exporter_index = (int) sanitize_text_field( $_POST['exporter'] ); |
| 4339 | $page = (int) sanitize_text_field( $_POST['page'] ); |
| 4340 | |
| 4341 | /** |
| 4342 | * Filters the array of exporter callbacks. |
| 4343 | * |
| 4344 | * @since 4.9.5. |
| 4345 | * |
| 4346 | * @param array $args { |
| 4347 | * An array of callable exporters of personal data. Default empty array. |
| 4348 | * [ |
| 4349 | * callback string Callable exporter that accepts an email address and |
| 4350 | * a zero-based page and returns an array of name value |
| 4351 | * pairs of personal data |
| 4352 | * exporter_friendly_name string Translated user facing friendly name for the exporter |
| 4353 | * ] |
| 4354 | * } |
| 4355 | */ |
| 4356 | $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); |
| 4357 | |
| 4358 | if ( ! is_array( $exporters ) ) { |
| 4359 | wp_send_json_error( 'an exporter has improperly used the registration filter' ); |
| 4360 | } |
| 4361 | |
| 4362 | // Do we have any registered exporters? |
| 4363 | if ( 0 < count( $exporters ) ) { |
| 4364 | if ( $exporter_index < 0 ) { |
| 4365 | wp_send_json_error( 'exporter index cannot be negative' ); |
| 4366 | } |
| 4367 | |
| 4368 | if ( $exporter_index > count( $exporters ) - 1 ) { |
| 4369 | wp_send_json_error( 'exporter index out of range' ); |
| 4370 | } |
| 4371 | |
| 4372 | if ( $page < 0 ) { |
| 4373 | wp_send_json_error( 'page index cannot be negative' ); |
| 4374 | } |
| 4375 | |
| 4376 | // Surprisingly, email addresses can contain mutli-byte characters now |
| 4377 | $email_address = trim( mb_strtolower( $email_address ) ); |
| 4378 | |
| 4379 | if ( ! is_email( $email_address ) ) { |
| 4380 | wp_send_json_error( 'a valid email address must be given' ); |
| 4381 | } |
| 4382 | |
| 4383 | $exporter = $exporters[ $exporter_index ]; |
| 4384 | if ( ! is_array( $exporter ) ) { |
| 4385 | wp_send_json_error( "expected an array describing the exporter at index $exporter_index" ); |
| 4386 | } |
| 4387 | if ( ! array_key_exists( 'callback', $exporter ) ) { |
| 4388 | wp_send_json_error( "exporter array at index $exporter_index does not include a callback" ); |
| 4389 | } |
| 4390 | if ( ! is_callable( $exporter['callback'] ) ) { |
| 4391 | wp_send_json_error( "exporter callback at index $exporter_index is not a valid callback" ); |
| 4392 | } |
| 4393 | if ( ! array_key_exists( 'exporter_friendly_name', $exporter ) ) { |
| 4394 | wp_send_json_error( "exporter array at index $exporter_index does not include a friendly name" ); |
| 4395 | } |
| 4396 | |
| 4397 | $callback = $exporters[ $exporter_index ]['callback']; |
| 4398 | $exporter_friendly_name = $exporters[ $exporter_index ]['exporter_friendly_name']; |
| 4399 | |
| 4400 | $response = call_user_func( $callback, $email_address, $page ); |
| 4401 | if ( is_wp_error( $response ) ) { |
| 4402 | wp_send_json_error( $response ); |
| 4403 | } |
| 4404 | |
| 4405 | if ( ! is_array( $response ) ) { |
| 4406 | wp_send_json_error( "expected response as an array from exporter: $exporter_friendly_name" ); |
| 4407 | } |
| 4408 | if ( ! array_key_exists( 'data', $response ) ) { |
| 4409 | wp_send_json_error( "expected data in response array from exporter: $exporter_friendly_name" ); |
| 4410 | } |
| 4411 | if ( ! is_array( $response['data'] ) ) { |
| 4412 | wp_send_json_error( "expected data array in response array from exporter: $exporter_friendly_name" ); |
| 4413 | } |
| 4414 | if ( ! array_key_exists( 'done', $response ) ) { |
| 4415 | wp_send_json_error( "expected done (boolean) in response array from exporter: $exporter_friendly_name" ); |
| 4416 | } |
| 4417 | } else { |
| 4418 | // No exporters, so we're done |
| 4419 | $response = array( |
| 4420 | 'data' => array(), |
| 4421 | 'done' => true, |
| 4422 | ); |
| 4423 | } |
| 4424 | |
| 4425 | /** |
| 4426 | * Filters a page of personal data exporter data. Used to build the export report. |
| 4427 | * |
| 4428 | * Allows the export response to be consumed by destinations in addition to Ajax. |
| 4429 | * |
| 4430 | * @since 4.9.5 |
| 4431 | * |
| 4432 | * @param array $response The personal data for the given exporter and page. |
| 4433 | * @param int $exporter_index The index of the exporter that provided this data. |
| 4434 | * @param string $email_address The email address associated with this personal data. |
| 4435 | * @param int $page The zero-based page for this response. |
| 4436 | */ |
| 4437 | $response = apply_filters( 'wp_privacy_personal_data_export_page', $response, $exporter_index, $email_address, $page ); |
| 4438 | if ( is_wp_error( $response ) ) { |
| 4439 | wp_send_json_error( $response ); |
| 4440 | } |
| 4441 | |
| 4442 | wp_send_json_success( $response ); |
| 4443 | } |