Ticket #43602: 43602.2.diff
File 43602.2.diff, 18.2 KB (added by , 5 years ago) |
---|
-
src/wp-admin/includes/ajax-actions.php
4341 4341 /** 4342 4342 * Filters the array of exporter callbacks. 4343 4343 * 4344 * @since 4.9. 5.4344 * @since 4.9.6 4345 4345 * 4346 4346 * @param array $args { 4347 4347 * An array of callable exporters of personal data. Default empty array. … … 4429 4429 * 4430 4430 * Allows the export response to be consumed by destinations in addition to Ajax. 4431 4431 * 4432 * @since 4.9. 54432 * @since 4.9.6 4433 4433 * 4434 4434 * @param array $response The personal data for the given exporter and page. 4435 4435 * @param int $exporter_index The index of the exporter that provided this data. … … 4443 4443 4444 4444 wp_send_json_success( $response ); 4445 4445 } 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 } -
src/wp-admin/includes/user.php
709 709 * @access private 710 710 */ 711 711 function _wp_personal_data_handle_actions() { 712 if ( isset( $_POST[' export_personal_data_email_retry'] ) ) { // WPCS: input var ok.712 if ( isset( $_POST['privacy_action_email_retry'] ) ) { // WPCS: input var ok. 713 713 check_admin_referer( 'bulk-privacy_requests' ); 714 714 715 $request_id = absint( current( array_keys( (array) wp_unslash( $_POST[' export_personal_data_email_retry'] ) ) ) ); // WPCS: input var ok, sanitization ok.715 $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['privacy_action_email_retry'] ) ) ) ); // WPCS: input var ok, sanitization ok. 716 716 $result = _wp_privacy_resend_request( $request_id ); 717 717 718 718 if ( is_wp_error( $result ) ) { 719 719 add_settings_error( 720 ' export_personal_data_email_retry',721 ' export_personal_data_email_retry',720 'privacy_action_email_retry', 721 'privacy_action_email_retry', 722 722 $result->get_error_message(), 723 723 'error' 724 724 ); 725 725 } else { 726 726 add_settings_error( 727 ' export_personal_data_email_retry',728 ' export_personal_data_email_retry',727 'privacy_action_email_retry', 728 'privacy_action_email_retry', 729 729 __( 'Confirmation request re-resent successfully.' ), 730 730 'updated' 731 731 ); … … 908 908 909 909 _wp_personal_data_handle_actions(); 910 910 911 // "Borrow" xfn.js for now so we don't have to create new files. 912 wp_enqueue_script( 'xfn' ); 913 911 914 $requests_table = new WP_Privacy_Data_Removal_Requests_Table( array( 912 915 'plural' => 'privacy_requests', 913 916 'singular' => 'privacy_request', 914 917 ) ); 918 $requests_table->enqueue_strings(); 915 919 $requests_table->process_bulk_action(); 916 920 $requests_table->prepare_items(); 917 921 ?> … … 1358 1362 * @return string 1359 1363 */ 1360 1364 public function column_email( $item ) { 1365 $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); 1366 $exporters_count = count( $exporters ); 1367 $request_id = $item['request_id']; 1368 $nonce = wp_create_nonce( 'wp-privacy-export-personal-data-' . $request_id ); 1369 1370 $download_data_markup = '<div class="download_personal_data" data-exporters-count="' . esc_attr( $exporters_count ) . '" data-request-id="' . esc_attr( $request_id ) . '" data-nonce="' . esc_attr( $nonce ) . '">' . 1371 '<span class="download_personal_data_idle"><a href="#" >' . __( 'Download Personal Data' ) . '</a></span>' . 1372 '<span style="display:none" class="download_personal_data_processing" >' . __( 'Downloading Data...' ) . '</span>' . 1373 '<span style="display:none" class="download_personal_data_failed">' . __( 'Download Failed!' ) . ' <a href="#" >' . __( 'Retry' ) . '</a></span>'; 1374 1361 1375 $row_actions = array( 1362 'download_data' => __( 'Download Personal Data' ),1376 'download_data' => $download_data_markup, 1363 1377 ); 1364 1378 1365 1379 return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) ); … … 1383 1397 // TODO Complete in follow on patch. 1384 1398 break; 1385 1399 case 'request-failed': 1386 submit_button( __( 'Retry' ), 'secondary', ' export_personal_data_email_retry[' . $item['request_id'] . ']', false );1400 submit_button( __( 'Retry' ), 'secondary', 'privacy_action_email_retry[' . $item['request_id'] . ']', false ); 1387 1401 break; 1388 1402 case 'request-completed': 1389 1403 echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array( … … 1419 1433 */ 1420 1434 protected $post_type = 'user_remove_request'; 1421 1435 1436 public function enqueue_strings() { 1437 $removal_request_strings = array( 1438 'noDataFound' => __( 'No personal data was found for this user.' ), 1439 'foundAndRemoved' => __( 'All of the personal data found for this user was removed.' ), 1440 'noneRemoved' => __( 'Personal data was found for this user but was not removed.' ), 1441 'someNotRemoved' => __( 'Personal data was found for this user but some of the personal data found was not removed.' ), 1442 'anErrorOccurred' => __( 'An error occurred while attempting to find and remove personal data.' ) 1443 ); 1444 1445 echo '<script type="text/javascript">'; 1446 echo 'var removalRequestStrings = ' . wp_json_encode( $removal_request_strings ) . ';'; 1447 echo "</script>"; 1448 } 1449 1422 1450 /** 1423 1451 * Actions column. 1424 1452 * … … 1428 1456 * @return string 1429 1457 */ 1430 1458 public function column_email( $item ) { 1431 $row_actions = array( 1432 // TODO Complete in follow on patch. 1433 'remove_data' => __( 'Remove Personal Data' ), 1434 ); 1459 $row_actions = array(); 1435 1460 1436 // If we have a user ID, include a delete user action. 1437 if ( ! empty( $item['user_id'] ) ) { 1438 // TODO Complete in follow on patch. 1439 $row_actions['delete_user'] = __( 'Delete User' ); 1461 // Allow the administrator to "force remove" the personal data even if confirmation has not yet been received 1462 $status = get_post_status( $item['request_id'] ); 1463 if ( 'request-confirmed' !== $status ) { 1464 $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); 1465 $erasers_count = count( $erasers ); 1466 $request_id = $item['request_id']; 1467 $nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ); 1468 1469 $remove_data_markup = '<div class="remove_personal_data force_remove_personal_data" data-erasers-count="' . esc_attr( $erasers_count ) . '" data-request-id="' . esc_attr( $request_id ) . '" data-nonce="' . esc_attr( $nonce ) . '">' . 1470 '<span class="remove_personal_data_idle"><a href="#" >' . __( 'Force Remove Personal Data' ) . '</a></span>' . 1471 '<span style="display:none" class="remove_personal_data_processing" >' . __( 'Removing Data...' ) . '</span>' . 1472 '<span style="display:none" class="remove_personal_data_failed">' . __( 'Force Remove Failed!' ) . ' <a href="#" >' . __( 'Retry' ) . '</a></span>'; 1473 1474 $row_actions = array( 1475 'remove_data' => $remove_data_markup, 1476 ); 1440 1477 } 1441 1478 1442 1479 return sprintf( '%1$s %2$s', $item['email'], $this->row_actions( $row_actions ) ); … … 1450 1487 * @param array $item Item being shown. 1451 1488 */ 1452 1489 public function column_next_steps( $item ) { 1490 $status = get_post_status( $item['request_id'] ); 1491 1492 switch ( $status ) { 1493 case 'request-pending': 1494 esc_html_e( 'Waiting for confirmation' ); 1495 break; 1496 case 'request-confirmed': 1497 $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); 1498 $erasers_count = count( $erasers ); 1499 $request_id = $item['request_id']; 1500 $nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id ); 1501 1502 $remove_data_markup = '<div class="remove_personal_data" data-force-erase="1" data-erasers-count="' . esc_attr( $erasers_count ) . '" data-request-id="' . esc_attr( $request_id ) . '" data-nonce="' . esc_attr( $nonce ) . '">' . 1503 '<span class="remove_personal_data_idle"><a class="button" href="#" >' . __( 'Remove Personal Data' ) . '</a></span>' . 1504 '<span style="display:none" class="remove_personal_data_processing button updating-message" >' . __( 'Removing Data...' ) . '</span>' . 1505 '<span style="display:none" class="remove_personal_data_failed">' . __( 'Removing Data Failed!' ) . ' <a class="button" href="#" >' . __( 'Retry' ) . '</a></span>'; 1506 1507 echo $remove_data_markup; 1508 break; 1509 case 'request-failed': 1510 submit_button( __( 'Retry' ), 'secondary', 'privacy_action_email_retry[' . $item['request_id'] . ']', false ); 1511 break; 1512 case 'request-completed': 1513 echo '<a href="' . esc_url( wp_nonce_url( add_query_arg( array( 1514 'action' => 'delete', 1515 'request_id' => array( $item['request_id'] ) 1516 ), admin_url( 'tools.php?page=remove_personal_data' ) ), 'bulk-privacy_requests' ) ) . '">' . esc_html__( 'Remove request' ) . '</a>'; 1517 break; 1518 } 1453 1519 } 1454 1520 1455 1521 } -
src/wp-admin/js/xfn.js
20 20 $( '#link_rel' ).val( ( isMe ) ? 'me' : inputs.substr( 0,inputs.length - 1 ) ); 21 21 }); 22 22 }); 23 24 // Privacy request action handling 25 26 jQuery( document ).ready( function( $ ) { 27 function set_action_state( $action, state ) { 28 $action.children().hide(); 29 $action.children( '.' + state ).show(); 30 } 31 32 function clearResultsAfterRow( $requestRow ) { 33 if ( $requestRow.next().hasClass( 'request-results' ) ) { 34 $requestRow.next().remove(); 35 } 36 } 37 38 function appendResultsAfterRow( $requestRow, classes, summaryMessage, additionalMessages ) { 39 clearResultsAfterRow( $requestRow ); 40 if ( additionalMessages.length ) { 41 // TODO - render additionalMessages after the summaryMessage 42 } 43 44 $requestRow.after( function() { 45 return '<tr class="request-results"><td colspan="5"><div class="notice inline notice-alt ' + classes + '"><p>' + 46 summaryMessage + 47 '</p></div></td></tr>'; 48 } ); 49 } 50 51 $( '.remove_personal_data a' ).click( function( event ) { 52 event.preventDefault(); 53 event.stopPropagation(); 54 55 var $this = $( this ); 56 var $action = $this.parents( '.remove_personal_data' ); 57 var $requestRow = $this.parents( 'tr' ); 58 var requestID = $action.data( 'request-id' ); 59 var nonce = $action.data( 'nonce' ); 60 var erasersCount = $action.data( 'erasers-count' ); 61 62 var removedCount = 0; 63 var retainedCount = 0; 64 var messages = []; 65 66 $action.blur(); 67 clearResultsAfterRow( $requestRow ); 68 69 function on_erasure_done_success() { 70 set_action_state( $action, 'remove_personal_data_idle' ); 71 var summaryMessage = removalRequestStrings.noDataFound; 72 var classes = 'notice-success'; 73 if ( 0 == removedCount ) { 74 if ( 0 == retainedCount ) { 75 summaryMessage = removalRequestStrings.noDataFound; 76 } else { 77 summaryMessage = removalRequestStrings.noneRemoved; 78 classes = 'notice-warning'; 79 } 80 } else { 81 if ( 0 == retainedCount ) { 82 summaryMessage = removalRequestStrings.foundAndRemoved; 83 } else { 84 summaryMessage = removalRequestStrings.someNotRemoved; 85 classes = 'notice-warning'; 86 } 87 } 88 appendResultsAfterRow( $requestRow, 'notice-success', summaryMessage, [] ); 89 } 90 91 function on_erasure_failure( textStatus, error ) { 92 set_action_state( $action, 'remove_personal_data_failed' ); 93 appendResultsAfterRow( $requestRow, 'notice-error', removalRequestStrings.anErrorOccurred, [] ); 94 } 95 96 function do_next_erasure( eraserIndex, pageIndex ) { 97 $.ajax( { 98 url: ajaxurl, 99 data: { 100 action: 'wp-privacy-erase-personal-data', 101 eraser: eraserIndex, 102 id: requestID, 103 page: pageIndex, 104 security: nonce, 105 }, 106 method: 'post' 107 } ).done( function( response ) { 108 if ( ! response.success ) { 109 on_erasure_failure( 'error', response.data ); 110 return; 111 } 112 var responseData = response.data; 113 if ( responseData.num_items_removed ) { 114 removedCount += responseData.num_items_removed; 115 } 116 if ( responseData.num_items_retained ) { 117 retainedCount += responseData.num_items_removed; 118 } 119 if ( responseData.messages ) { 120 messages = messages.concat( responseData.messages ); 121 } 122 if ( ! responseData.done ) { 123 setTimeout( do_next_erasure( eraserIndex, pageIndex + 1 ) ); 124 } else { 125 if ( eraserIndex < erasersCount ) { 126 setTimeout( do_next_erasure( eraserIndex + 1, 1 ) ); 127 } else { 128 on_erasure_done_success(); 129 } 130 } 131 } ).fail( function( jqxhr, textStatus, error ) { 132 on_erasure_failure( textStatus, error ); 133 } ); 134 } 135 136 // And now, let's begin 137 set_action_state( $action, 'remove_personal_data_processing' ); 138 139 do_next_erasure( 1, 1 ); 140 } ) 141 } );