| | 4446 | |
| | 4447 | /** |
| | 4448 | * Ajax handler for erasing personal data. |
| | 4449 | * |
| | 4450 | * @since 4.9.6 |
| | 4451 | */ |
| | 4452 | function wp_ajax_wp_privacy_erase_personal_data() { |
| | 4453 | $request_id = sanitize_text_field( $_POST['id'] ); |
| | 4454 | check_ajax_referer( 'wp-privacy-erase-personal-data-' . $request_id, 'security' ); |
| | 4455 | |
| | 4456 | // Find the request CPT |
| | 4457 | $request = get_post( $request_id ); |
| | 4458 | if ( 'user_remove_request' !== $request->post_type ) { |
| | 4459 | wp_send_json_error( __( 'Error: Invalid request ID.' ) ); |
| | 4460 | } |
| | 4461 | |
| | 4462 | $email_address = get_post_meta( $request_id, '_user_email', true ); |
| | 4463 | if ( function_exists( 'mb_strtolower' ) ) { |
| | 4464 | $email_address = trim( mb_strtolower( $email_address ) ); |
| | 4465 | } else { |
| | 4466 | $email_address = trim( strtolower( $email_address ) ); |
| | 4467 | } |
| | 4468 | |
| | 4469 | if ( ! is_email( $email_address ) ) { |
| | 4470 | wp_send_json_error( __( 'Error: Invalid email address in request.' ) ); |
| | 4471 | } |
| | 4472 | |
| | 4473 | $eraser_index = (int) $_POST['eraser']; |
| | 4474 | $page = (int) $_POST['page']; |
| | 4475 | |
| | 4476 | /** |
| | 4477 | * Filters the array of personal data eraser callbacks. |
| | 4478 | * |
| | 4479 | * @since 4.9.6 |
| | 4480 | * |
| | 4481 | * @param array $args { |
| | 4482 | * An array of callable erasers of personal data. Default empty array. |
| | 4483 | * [ |
| | 4484 | * callback string Callable eraser that accepts an email address and |
| | 4485 | * a page and returns an array with the number of items |
| | 4486 | * removed, the number of items retained and any messages |
| | 4487 | * from the eraser, as well as if additional pages are |
| | 4488 | * available |
| | 4489 | * exporter_friendly_name string Translated user facing friendly name for the eraser |
| | 4490 | * ] |
| | 4491 | * } |
| | 4492 | */ |
| | 4493 | $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); |
| | 4494 | |
| | 4495 | // Do we have any registered erasers? |
| | 4496 | if ( 0 < count( $erasers ) ) { |
| | 4497 | if ( $eraser_index < 1 ) { |
| | 4498 | wp_send_json_error( __( 'Error: Eraser index cannot be less than one.' ) ); |
| | 4499 | } |
| | 4500 | |
| | 4501 | if ( $eraser_index > count( $erasers ) ) { |
| | 4502 | wp_send_json_error( __( 'Error: Eraser index is out of range.' ) ); |
| | 4503 | } |
| | 4504 | |
| | 4505 | if ( $page < 1 ) { |
| | 4506 | wp_send_json_error( __( 'Error: Page index cannot be less than one.' ) ); |
| | 4507 | } |
| | 4508 | |
| | 4509 | $index = $eraser_index - 1; // Convert to zero based for eraser index |
| | 4510 | $eraser = $erasers[ $index ]; |
| | 4511 | if ( ! is_array( $eraser ) ) { |
| | 4512 | wp_send_json_error( |
| | 4513 | sprintf( |
| | 4514 | __( 'Error: Expected an array describing the eraser at index %d.' ), |
| | 4515 | $eraser_index |
| | 4516 | ) |
| | 4517 | ); |
| | 4518 | } |
| | 4519 | if ( ! array_key_exists( 'callback', $eraser ) ) { |
| | 4520 | wp_send_json_error( |
| | 4521 | sprintf( |
| | 4522 | __( 'Error: Eraser array at index %d does not include a callback.' ), |
| | 4523 | $eraser_index |
| | 4524 | ) |
| | 4525 | ); |
| | 4526 | } |
| | 4527 | if ( ! is_callable( $eraser['callback'] ) ) { |
| | 4528 | wp_send_json_error( |
| | 4529 | sprintf( |
| | 4530 | __( 'Error: Eraser callback at index %d is not a valid callback.' ), |
| | 4531 | $eraser_index |
| | 4532 | ) |
| | 4533 | ); |
| | 4534 | } |
| | 4535 | if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { |
| | 4536 | wp_send_json_error( |
| | 4537 | sprintf( |
| | 4538 | __( 'Error: Eraser array at index %d does not include a friendly name.' ), |
| | 4539 | $eraser_index |
| | 4540 | ) |
| | 4541 | ); |
| | 4542 | } |
| | 4543 | |
| | 4544 | $callback = $erasers[ $index ]['callback']; |
| | 4545 | $eraser_friendly_name = $erasers[ $index ]['eraser_friendly_name']; |
| | 4546 | |
| | 4547 | $response = call_user_func( $callback, $email_address, $page ); |
| | 4548 | if ( is_wp_error( $response ) ) { |
| | 4549 | wp_send_json_error( $response ); |
| | 4550 | } |
| | 4551 | |
| | 4552 | if ( ! is_array( $response ) ) { |
| | 4553 | wp_send_json_error( |
| | 4554 | sprintf( |
| | 4555 | __( 'Error: Did not receive array from %s eraser (index %d).' ), |
| | 4556 | $eraser_friendly_name, |
| | 4557 | $eraser_index |
| | 4558 | ) |
| | 4559 | ); |
| | 4560 | } |
| | 4561 | if ( ! array_key_exists( 'num_items_removed', $response ) ) { |
| | 4562 | wp_send_json_error( |
| | 4563 | sprintf( |
| | 4564 | __( 'Error: Expected num_items_removed key in response array from %s eraser (index %d).' ), |
| | 4565 | $eraser_friendly_name, |
| | 4566 | $eraser_index |
| | 4567 | ) |
| | 4568 | ); |
| | 4569 | } |
| | 4570 | if ( ! array_key_exists( 'num_items_retained', $response ) ) { |
| | 4571 | wp_send_json_error( |
| | 4572 | sprintf( |
| | 4573 | __( 'Error: Expected num_items_retained key in response array from %s eraser (index %d).' ), |
| | 4574 | $eraser_friendly_name, |
| | 4575 | $eraser_index |
| | 4576 | ) |
| | 4577 | ); |
| | 4578 | } |
| | 4579 | if ( ! array_key_exists( 'messages', $response ) ) { |
| | 4580 | wp_send_json_error( |
| | 4581 | sprintf( |
| | 4582 | __( 'Error: Expected messages key in response array from %s eraser (index %d).' ), |
| | 4583 | $eraser_friendly_name, |
| | 4584 | $eraser_index |
| | 4585 | ) |
| | 4586 | ); |
| | 4587 | } |
| | 4588 | if ( ! is_array( $response['messages'] ) ) { |
| | 4589 | wp_send_json_error( |
| | 4590 | sprintf( |
| | 4591 | __( 'Error: Expected messages key to reference an array in response array from %s eraser (index %d).' ), |
| | 4592 | $eraser_friendly_name, |
| | 4593 | $eraser_index |
| | 4594 | ) |
| | 4595 | ); |
| | 4596 | } |
| | 4597 | if ( ! array_key_exists( 'done', $response ) ) { |
| | 4598 | wp_send_json_error( |
| | 4599 | sprintf( |
| | 4600 | __( 'Error: Expected done flag in response array from %s eraser (index %d).' ), |
| | 4601 | $eraser_friendly_name, |
| | 4602 | $eraser_index |
| | 4603 | ) |
| | 4604 | ); |
| | 4605 | } |
| | 4606 | } else { |
| | 4607 | // No erasers, so we're done |
| | 4608 | $response = array( |
| | 4609 | 'num_items_removed' => 0, |
| | 4610 | 'num_items_retained' => 0, |
| | 4611 | 'messages' => array(), |
| | 4612 | 'done' => true, |
| | 4613 | ); |
| | 4614 | } |
| | 4615 | |
| | 4616 | /** |
| | 4617 | * Filters a page of personal data eraser data. |
| | 4618 | * |
| | 4619 | * Allows the erasure response to be consumed by destinations in addition to Ajax. |
| | 4620 | * |
| | 4621 | * @since 4.9.6 |
| | 4622 | * |
| | 4623 | * @param array $response The personal data for the given exporter and page. |
| | 4624 | * @param int $exporter_index The index of the exporter that provided this data. |
| | 4625 | * @param string $email_address The email address associated with this personal data. |
| | 4626 | * @param int $page The zero-based page for this response. |
| | 4627 | * @param int $request_id The privacy request post ID associated with this request. |
| | 4628 | */ |
| | 4629 | $response = apply_filters( 'wp_privacy_personal_data_erasure_page', $response, $eraser_index, $email_address, $page, $request_id ); |
| | 4630 | if ( is_wp_error( $response ) ) { |
| | 4631 | wp_send_json_error( $response ); |
| | 4632 | } |
| | 4633 | |
| | 4634 | wp_send_json_success( $response ); |
| | 4635 | } |