| 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 | * eraser_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 | } |