Privacy: add functionality to anonymize commenters.

Props xkon, fclaussen, allendav, birgire, azaozz.
See #43442.

git-svn-id: https://develop.svn.wordpress.org/trunk@42994 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Andrew Ozz 2018-04-20 12:18:35 +00:00
parent ad13b8bbda
commit 7e26130f12
4 changed files with 338 additions and 3 deletions

View File

@ -72,15 +72,15 @@ jQuery( document ).ready( function( $ ) {
set_action_state( $action, 'remove_personal_data_idle' );
var summaryMessage = strings.noDataFound;
var classes = 'notice-success';
if ( 0 == removedCount ) {
if ( 0 == retainedCount ) {
if ( 0 === removedCount ) {
if ( 0 === retainedCount ) {
summaryMessage = strings.noDataFound;
} else {
summaryMessage = strings.noneRemoved;
classes = 'notice-warning';
}
} else {
if ( 0 == retainedCount ) {
if ( 0 === retainedCount ) {
summaryMessage = strings.foundAndRemoved;
} else {
summaryMessage = strings.someNotRemoved;

View File

@ -3378,3 +3378,116 @@ function wp_comments_personal_data_exporter( $email_address, $page = 1 ) {
'done' => $done,
);
}
/**
* Registers the personal data eraser for comments.
*
* @since 4.9.6
*
* @param array $erasers An array of personal data erasers.
* @return array $erasers An array of personal data erasers.
*/
function wp_register_comment_personal_data_eraser( $erasers ) {
$erasers[] = array(
'eraser_friendly_name' => __( 'WordPress Comments' ),
'callback' => 'wp_comments_personal_data_eraser',
);
return $erasers;
}
/**
* Erases personal data associated with an email address from the comments table.
*
* @since 4.9.6
*
* @param string $email_address The comment author email address.
* @param int $page Comment page.
* @return array
*/
function wp_comments_personal_data_eraser( $email_address, $page = 1 ) {
global $wpdb;
if ( empty( $email_address ) ) {
return array(
'num_items_removed' => 0,
'num_items_retained' => 0,
'messages' => array(),
'done' => true,
);
}
// Limit us to 500 comments at a time to avoid timing out.
$number = 500;
$page = (int) $page;
$num_items_removed = 0;
$comments = get_comments(
array(
'author_email' => $email_address,
'number' => $number,
'paged' => $page,
'order_by' => 'comment_ID',
'order' => 'ASC',
'include_unapproved' => true,
)
);
$anon_author = __( 'Anonymous' );
$messages = array();
foreach ( (array) $comments as $comment ) {
$anonymized_comment = array();
$anonymized_comment['comment_agent'] = '';
$anonymized_comment['comment_author'] = $anon_author;
$anonymized_comment['comment_author_email'] = wp_privacy_anonymize_data( 'email', $comment->comment_author_email );
$anonymized_comment['comment_author_IP'] = wp_privacy_anonymize_data( 'ip', $comment->comment_author_IP );
$anonymized_comment['comment_author_url'] = wp_privacy_anonymize_data( 'url', $comment->comment_author_url );
$anonymized_comment['user_id'] = 0;
$comment_id = (int) $comment->comment_ID;
/**
* Filters whether to anonymize the comment.
*
* @since 4.9.6
*
* @param bool|string Whether to apply the comment anonymization (bool).
* Custom prevention message (string). Default true.
* @param WP_Comment $comment WP_Comment object.
* @param array $anonymized_comment Anonymized comment data.
*/
$anon_message = apply_filters( 'wp_anonymize_comment', true, $comment, $anonymized_comment );
if ( true !== $anon_message ) {
if ( $anon_message && is_string( $anon_message ) ) {
$messages[] = esc_html( $anon_message );
} else {
/* translators: %d: Comment ID */
$messages[] = sprintf( __( 'Comment %d contains personal data but could not be anonymized.' ), $comment_id );
}
continue;
}
$args = array(
'comment_ID' => $comment_id,
);
$updated = $wpdb->update( $wpdb->comments, $anonymized_comment, $args );
if ( $updated ) {
$num_items_removed++;
clean_comment_cache( $comment_id );
}
}
$done = count( $comments ) < $number;
return array(
'num_items_removed' => $num_items_removed,
'num_items_retained' => count( $comments ) - $num_items_removed,
'messages' => $messages,
'done' => $done,
);
}

View File

@ -329,6 +329,7 @@ add_action( 'do_pings', 'do_all_pings', 10, 1 );
add_action( 'do_robots', 'do_robots' );
add_action( 'set_comment_cookies', 'wp_set_comment_cookies', 10, 3 );
add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter', 10 );
add_filter( 'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser', 10 );
add_action( 'sanitize_comment_cookies', 'sanitize_comment_cookies' );
add_action( 'admin_print_scripts', 'print_emoji_detection_script' );
add_action( 'admin_print_scripts', 'print_head_scripts', 20 );

View File

@ -812,6 +812,227 @@ class Tests_Comment extends WP_UnitTestCase {
}
}
/**
* The `wp_comments_personal_data_eraser()` function should erase user's comments.
*
* @ticket 43442
*/
public function test_wp_comments_personal_data_eraser() {
$post_id = self::factory()->post->create();
$user_id = self::factory()->user->create();
$args = array(
'user_id' => $user_id,
'comment_post_ID' => $post_id,
'comment_author' => 'Comment Author',
'comment_author_email' => 'personal@local.host',
'comment_author_url' => 'https://local.host/',
'comment_author_IP' => '192.168.0.1',
'comment_date' => '2018-04-14 17:20:00',
'comment_agent' => 'COMMENT_AGENT',
'comment_content' => 'Comment Content',
);
$comment_id = self::factory()->comment->create( $args );
wp_comments_personal_data_eraser( $args['comment_author_email'] );
$comment = get_comment( $comment_id );
$actual = array(
'comment_ID' => $comment->comment_ID,
'user_id' => $comment->user_id,
'comment_author' => $comment->comment_author,
'comment_author_email' => $comment->comment_author_email,
'comment_author_url' => $comment->comment_author_url,
'comment_author_IP' => $comment->comment_author_IP,
'comment_date' => $comment->comment_date,
'comment_date_gmt' => $comment->comment_date_gmt,
'comment_agent' => $comment->comment_agent,
'comment_content' => $comment->comment_content,
);
$expected = array(
'comment_ID' => (string) $comment_id,
'user_id' => '0', // Anonymized.
'comment_author' => 'Anonymous', // Anonymized.
'comment_author_email' => 'deleted@site.invalid', // Anonymized.
'comment_author_url' => 'https://site.invalid', // Anonymized.
'comment_author_IP' => '192.168.0.0', // Anonymized.
'comment_date' => '2018-04-14 17:20:00',
'comment_date_gmt' => '2018-04-14 17:20:00',
'comment_agent' => '', // Anonymized.
'comment_content' => 'Comment Content',
);
$this->assertSame( $expected, $actual );
}
/**
* Testing the `wp_comments_personal_data_eraser()` function's output on an empty first page.
*
* @ticket 43442
*/
public function test_wp_comments_personal_data_eraser_empty_first_page_output() {
$actual = wp_comments_personal_data_eraser( 'nocommentsfound@local.host' );
$expected = array(
'num_items_removed' => 0,
'num_items_retained' => 0,
'messages' => array(),
'done' => true,
);
$this->assertSame( $expected, $actual );
}
/**
* Testing the `wp_comments_personal_data_eraser()` function's output, for the non-empty first page.
*
* @ticket 43442
*/
public function test_wp_comments_personal_data_eraser_non_empty_first_page_output() {
$post_id = self::factory()->post->create();
$args = array(
'comment_post_ID' => $post_id,
'comment_author' => 'Comment Author',
'comment_author_email' => 'personal@local.host',
'comment_author_url' => 'https://local.host/',
'comment_author_IP' => '192.168.0.1',
'comment_date' => '2018-04-14 17:20:00',
'comment_agent' => 'COMMENT_AGENT',
'comment_content' => 'Comment Content',
);
self::factory()->comment->create( $args );
$actual = wp_comments_personal_data_eraser( $args['comment_author_email'] );
$expected = array(
'num_items_removed' => 1,
'num_items_retained' => 0,
'messages' => array(),
'done' => true,
);
$this->assertSame( $expected, $actual );
}
/**
* Testing the `wp_comments_personal_data_eraser()` function's output, for an empty second page.
*
* @ticket 43442
*/
public function test_wp_comments_personal_data_eraser_empty_second_page_output() {
$post_id = self::factory()->post->create();
$args = array(
'comment_post_ID' => $post_id,
'comment_author' => 'Comment Author',
'comment_author_email' => 'personal@local.host',
'comment_author_url' => 'https://local.host/',
'comment_author_IP' => '192.168.0.1',
'comment_date' => '2018-04-14 17:20:00',
'comment_agent' => 'COMMENT_AGENT',
'comment_content' => 'Comment Content',
);
self::factory()->comment->create( $args );
$actual = wp_comments_personal_data_eraser( $args['comment_author_email'], 2 );
$expected = array(
'num_items_removed' => 0,
'num_items_retained' => 0,
'messages' => array(),
'done' => true,
);
$this->assertSame( $expected, $actual );
}
/**
* Testing the `wp_anonymize_comment` filter, to prevent comment anonymization.
*
* @ticket 43442
*/
public function test_wp_anonymize_comment_filter_to_prevent_comment_anonymization() {
$post_id = self::factory()->post->create();
$args = array(
'comment_post_ID' => $post_id,
'comment_author' => 'Comment Author',
'comment_author_email' => 'personal@local.host',
'comment_author_url' => 'https://local.host/',
'comment_author_IP' => '192.168.0.1',
'comment_date' => '2018-04-14 17:20:00',
'comment_agent' => 'COMMENT_AGENT',
'comment_content' => 'Comment Content',
);
$comment_id = self::factory()->comment->create( $args );
add_filter( 'wp_anonymize_comment', '__return_false' );
$actual = wp_comments_personal_data_eraser( $args['comment_author_email'] );
remove_filter( 'wp_anonymize_comment', '__return_false' );
$message = sprintf( 'Comment %d contains personal data but could not be anonymized.', $comment_id );
$expected = array(
'num_items_removed' => 0,
'num_items_retained' => 1,
'messages' => array( $message ),
'done' => true,
);
$this->assertSame( $expected, $actual );
}
/**
* Testing the `wp_anonymize_comment` filter, to prevent comment anonymization, with a custom message.
*
* @ticket 43442
*/
public function test_wp_anonymize_comment_filter_to_prevent_comment_anonymization_with_custom_message() {
$post_id = self::factory()->post->create();
$args = array(
'comment_post_ID' => $post_id,
'comment_author' => 'Comment Author',
'comment_author_email' => 'personal@local.host',
'comment_author_url' => 'https://local.host/',
'comment_author_IP' => '192.168.0.1',
'comment_date' => '2018-04-14 17:20:00',
'comment_agent' => 'COMMENT_AGENT',
'comment_content' => 'Comment Content',
);
$comment_id = self::factory()->comment->create( $args );
add_filter( 'wp_anonymize_comment', array( $this, 'wp_anonymize_comment_custom_message' ), 10, 3 );
$actual = wp_comments_personal_data_eraser( $args['comment_author_email'] );
remove_filter( 'wp_anonymize_comment', array( $this, 'wp_anonymize_comment_custom_message' ) );
$message = sprintf( 'Some custom message for comment %d.', $comment_id );
$expected = array(
'num_items_removed' => 0,
'num_items_retained' => 1,
'messages' => array( $message ),
'done' => true,
);
$this->assertSame( $expected, $actual );
}
/**
* Callback for the `wp_anonymize_comment` filter.
*
* @param bool|string $anonymize Whether to apply the comment anonymization (bool).
* Custom prevention message (string). Default true.
* @param WP_Comment $comment WP_Comment object.
* @param array $anonymized_comment Anonymized comment data.
* @return string
*/
public function wp_anonymize_comment_custom_message( $anonymize, $comment, $anonymized_comment ) {
return sprintf( 'Some custom message for comment %d.', $comment->comment_ID );
}
public function test_update_should_invalidate_comment_cache() {
global $wpdb;