WordPress.org

Make WordPress Core

Changeset 43080


Ignore:
Timestamp:
05/02/2018 12:20:31 AM (19 months ago)
Author:
SergeyBiryukov
Message:

Privacy: add functionality to anonymize commenters.

Props xkon, fclaussen, allendav, birgire, azaozz.
Merges [42994] to the 4.9 branch.
See #43442.

Location:
branches/4.9
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • branches/4.9

  • branches/4.9/src/wp-admin/js/xfn.js

    r43078 r43080  
    6868            var summaryMessage = strings.noDataFound;
    6969            var classes = 'notice-success';
    70             if ( 0 == removedCount ) {
    71                 if ( 0 == retainedCount ) {
     70            if ( 0 === removedCount ) {
     71                if ( 0 === retainedCount ) {
    7272                    summaryMessage = strings.noDataFound;
    7373                } else {
     
    7676                }
    7777            } else {
    78                 if ( 0 == retainedCount ) {
     78                if ( 0 === retainedCount ) {
    7979                    summaryMessage = strings.foundAndRemoved;
    8080                } else {
  • branches/4.9/src/wp-includes/comment.php

    r43079 r43080  
    32503250    );
    32513251}
     3252
     3253/**
     3254 * Registers the personal data eraser for comments.
     3255 *
     3256 * @since 4.9.6
     3257 *
     3258 * @param  array $erasers An array of personal data erasers.
     3259 * @return array $erasers An array of personal data erasers.
     3260 */
     3261function wp_register_comment_personal_data_eraser( $erasers ) {
     3262    $erasers[] = array(
     3263        'eraser_friendly_name' => __( 'WordPress Comments' ),
     3264        'callback'             => 'wp_comments_personal_data_eraser',
     3265    );
     3266
     3267    return $erasers;
     3268}
     3269
     3270/**
     3271 * Erases personal data associated with an email address from the comments table.
     3272 *
     3273 * @since 4.9.6
     3274 *
     3275 * @param  string $email_address The comment author email address.
     3276 * @param  int    $page          Comment page.
     3277 * @return array
     3278 */
     3279function wp_comments_personal_data_eraser( $email_address, $page = 1 ) {
     3280    global $wpdb;
     3281
     3282    if ( empty( $email_address ) ) {
     3283        return array(
     3284            'num_items_removed'  => 0,
     3285            'num_items_retained' => 0,
     3286            'messages'           => array(),
     3287            'done'               => true,
     3288        );
     3289    }
     3290
     3291    // Limit us to 500 comments at a time to avoid timing out.
     3292    $number            = 500;
     3293    $page              = (int) $page;
     3294    $num_items_removed = 0;
     3295
     3296    $comments = get_comments(
     3297        array(
     3298            'author_email'       => $email_address,
     3299            'number'             => $number,
     3300            'paged'              => $page,
     3301            'order_by'           => 'comment_ID',
     3302            'order'              => 'ASC',
     3303            'include_unapproved' => true,
     3304        )
     3305    );
     3306
     3307    $anon_author = __( 'Anonymous' );
     3308    $messages    = array();
     3309
     3310    foreach ( (array) $comments as $comment ) {
     3311        $anonymized_comment                         = array();
     3312        $anonymized_comment['comment_agent']        = '';
     3313        $anonymized_comment['comment_author']       = $anon_author;
     3314        $anonymized_comment['comment_author_email'] = wp_privacy_anonymize_data( 'email', $comment->comment_author_email );
     3315        $anonymized_comment['comment_author_IP']    = wp_privacy_anonymize_data( 'ip', $comment->comment_author_IP );
     3316        $anonymized_comment['comment_author_url']   = wp_privacy_anonymize_data( 'url', $comment->comment_author_url );
     3317        $anonymized_comment['user_id']              = 0;
     3318
     3319        $comment_id = (int) $comment->comment_ID;
     3320
     3321        /**
     3322         * Filters whether to anonymize the comment.
     3323         *
     3324         * @since 4.9.6
     3325         *
     3326         * @param bool|string                    Whether to apply the comment anonymization (bool).
     3327         *                                       Custom prevention message (string). Default true.
     3328         * @param WP_Comment $comment            WP_Comment object.
     3329         * @param array      $anonymized_comment Anonymized comment data.
     3330         */
     3331        $anon_message = apply_filters( 'wp_anonymize_comment', true, $comment, $anonymized_comment );
     3332
     3333        if ( true !== $anon_message ) {
     3334            if ( $anon_message && is_string( $anon_message ) ) {
     3335                $messages[] = esc_html( $anon_message );
     3336            } else {
     3337                /* translators: %d: Comment ID */
     3338                $messages[] = sprintf( __( 'Comment %d contains personal data but could not be anonymized.' ), $comment_id );
     3339            }
     3340
     3341            continue;
     3342        }
     3343
     3344        $args = array(
     3345            'comment_ID' => $comment_id,
     3346        );
     3347
     3348        $updated = $wpdb->update( $wpdb->comments, $anonymized_comment, $args );
     3349
     3350        if ( $updated ) {
     3351            $num_items_removed++;
     3352            clean_comment_cache( $comment_id );
     3353        }
     3354    }
     3355
     3356    $done = count( $comments ) < $number;
     3357
     3358    return array(
     3359        'num_items_removed'  => $num_items_removed,
     3360        'num_items_retained' => count( $comments ) - $num_items_removed,
     3361        'messages'           => $messages,
     3362        'done'               => $done,
     3363    );
     3364}
  • branches/4.9/src/wp-includes/default-filters.php

    r43076 r43080  
    303303add_action( 'set_comment_cookies',        'wp_set_comment_cookies',                  10, 2 );
    304304add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter', 10 );
     305add_filter( 'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser', 10 );
    305306add_action( 'sanitize_comment_cookies',   'sanitize_comment_cookies'                       );
    306307add_action( 'admin_print_scripts',        'print_emoji_detection_script'                   );
  • branches/4.9/tests/phpunit/tests/comment.php

    r43077 r43080  
    701701    }
    702702
     703    /**
     704     * The `wp_comments_personal_data_eraser()` function should erase user's comments.
     705     *
     706     * @ticket 43442
     707     */
     708    public function test_wp_comments_personal_data_eraser() {
     709
     710        $post_id = self::factory()->post->create();
     711        $user_id = self::factory()->user->create();
     712
     713        $args       = array(
     714            'user_id'              => $user_id,
     715            'comment_post_ID'      => $post_id,
     716            'comment_author'       => 'Comment Author',
     717            'comment_author_email' => 'personal@local.host',
     718            'comment_author_url'   => 'https://local.host/',
     719            'comment_author_IP'    => '192.168.0.1',
     720            'comment_date'         => '2018-04-14 17:20:00',
     721            'comment_agent'        => 'COMMENT_AGENT',
     722            'comment_content'      => 'Comment Content',
     723        );
     724        $comment_id = self::factory()->comment->create( $args );
     725
     726        wp_comments_personal_data_eraser( $args['comment_author_email'] );
     727
     728        $comment = get_comment( $comment_id );
     729
     730        $actual = array(
     731            'comment_ID'           => $comment->comment_ID,
     732            'user_id'              => $comment->user_id,
     733            'comment_author'       => $comment->comment_author,
     734            'comment_author_email' => $comment->comment_author_email,
     735            'comment_author_url'   => $comment->comment_author_url,
     736            'comment_author_IP'    => $comment->comment_author_IP,
     737            'comment_date'         => $comment->comment_date,
     738            'comment_date_gmt'     => $comment->comment_date_gmt,
     739            'comment_agent'        => $comment->comment_agent,
     740            'comment_content'      => $comment->comment_content,
     741        );
     742
     743        $expected = array(
     744            'comment_ID'           => (string) $comment_id,
     745            'user_id'              => '0', // Anonymized.
     746            'comment_author'       => 'Anonymous', // Anonymized.
     747            'comment_author_email' => 'deleted@site.invalid', // Anonymized.
     748            'comment_author_url'   => 'https://site.invalid', // Anonymized.
     749            'comment_author_IP'    => '192.168.0.0', // Anonymized.
     750            'comment_date'         => '2018-04-14 17:20:00',
     751            'comment_date_gmt'     => '2018-04-14 17:20:00',
     752            'comment_agent'        => '', // Anonymized.
     753            'comment_content'      => 'Comment Content',
     754        );
     755
     756        $this->assertSame( $expected, $actual );
     757    }
     758
     759    /**
     760     * Testing the `wp_comments_personal_data_eraser()` function's output on an empty first page.
     761     *
     762     * @ticket 43442
     763     */
     764    public function test_wp_comments_personal_data_eraser_empty_first_page_output() {
     765
     766        $actual   = wp_comments_personal_data_eraser( 'nocommentsfound@local.host' );
     767        $expected = array(
     768            'num_items_removed'  => 0,
     769            'num_items_retained' => 0,
     770            'messages'           => array(),
     771            'done'               => true,
     772        );
     773
     774        $this->assertSame( $expected, $actual );
     775    }
     776
     777    /**
     778     * Testing the `wp_comments_personal_data_eraser()` function's output, for the non-empty first page.
     779     *
     780     * @ticket 43442
     781     */
     782    public function test_wp_comments_personal_data_eraser_non_empty_first_page_output() {
     783
     784        $post_id = self::factory()->post->create();
     785        $args    = array(
     786            'comment_post_ID'      => $post_id,
     787            'comment_author'       => 'Comment Author',
     788            'comment_author_email' => 'personal@local.host',
     789            'comment_author_url'   => 'https://local.host/',
     790            'comment_author_IP'    => '192.168.0.1',
     791            'comment_date'         => '2018-04-14 17:20:00',
     792            'comment_agent'        => 'COMMENT_AGENT',
     793            'comment_content'      => 'Comment Content',
     794        );
     795        self::factory()->comment->create( $args );
     796
     797        $actual   = wp_comments_personal_data_eraser( $args['comment_author_email'] );
     798        $expected = array(
     799            'num_items_removed'  => 1,
     800            'num_items_retained' => 0,
     801            'messages'           => array(),
     802            'done'               => true,
     803        );
     804
     805        $this->assertSame( $expected, $actual );
     806    }
     807
     808    /**
     809     * Testing the `wp_comments_personal_data_eraser()` function's output, for an empty second page.
     810     *
     811     * @ticket 43442
     812     */
     813    public function test_wp_comments_personal_data_eraser_empty_second_page_output() {
     814
     815        $post_id = self::factory()->post->create();
     816        $args    = array(
     817            'comment_post_ID'      => $post_id,
     818            'comment_author'       => 'Comment Author',
     819            'comment_author_email' => 'personal@local.host',
     820            'comment_author_url'   => 'https://local.host/',
     821            'comment_author_IP'    => '192.168.0.1',
     822            'comment_date'         => '2018-04-14 17:20:00',
     823            'comment_agent'        => 'COMMENT_AGENT',
     824            'comment_content'      => 'Comment Content',
     825        );
     826        self::factory()->comment->create( $args );
     827
     828        $actual   = wp_comments_personal_data_eraser( $args['comment_author_email'], 2 );
     829        $expected = array(
     830            'num_items_removed'  => 0,
     831            'num_items_retained' => 0,
     832            'messages'           => array(),
     833            'done'               => true,
     834        );
     835
     836        $this->assertSame( $expected, $actual );
     837    }
     838
     839    /**
     840     * Testing the `wp_anonymize_comment` filter, to prevent comment anonymization.
     841     *
     842     * @ticket 43442
     843     */
     844    public function test_wp_anonymize_comment_filter_to_prevent_comment_anonymization() {
     845
     846        $post_id    = self::factory()->post->create();
     847        $args       = array(
     848            'comment_post_ID'      => $post_id,
     849            'comment_author'       => 'Comment Author',
     850            'comment_author_email' => 'personal@local.host',
     851            'comment_author_url'   => 'https://local.host/',
     852            'comment_author_IP'    => '192.168.0.1',
     853            'comment_date'         => '2018-04-14 17:20:00',
     854            'comment_agent'        => 'COMMENT_AGENT',
     855            'comment_content'      => 'Comment Content',
     856        );
     857        $comment_id = self::factory()->comment->create( $args );
     858
     859        add_filter( 'wp_anonymize_comment', '__return_false' );
     860        $actual = wp_comments_personal_data_eraser( $args['comment_author_email'] );
     861        remove_filter( 'wp_anonymize_comment', '__return_false' );
     862
     863        $message = sprintf( 'Comment %d contains personal data but could not be anonymized.', $comment_id );
     864
     865        $expected = array(
     866            'num_items_removed'  => 0,
     867            'num_items_retained' => 1,
     868            'messages'           => array( $message ),
     869            'done'               => true,
     870        );
     871
     872        $this->assertSame( $expected, $actual );
     873    }
     874
     875    /**
     876     * Testing the `wp_anonymize_comment` filter, to prevent comment anonymization, with a custom message.
     877     *
     878     * @ticket 43442
     879     */
     880    public function test_wp_anonymize_comment_filter_to_prevent_comment_anonymization_with_custom_message() {
     881
     882        $post_id    = self::factory()->post->create();
     883        $args       = array(
     884            'comment_post_ID'      => $post_id,
     885            'comment_author'       => 'Comment Author',
     886            'comment_author_email' => 'personal@local.host',
     887            'comment_author_url'   => 'https://local.host/',
     888            'comment_author_IP'    => '192.168.0.1',
     889            'comment_date'         => '2018-04-14 17:20:00',
     890            'comment_agent'        => 'COMMENT_AGENT',
     891            'comment_content'      => 'Comment Content',
     892        );
     893        $comment_id = self::factory()->comment->create( $args );
     894
     895        add_filter( 'wp_anonymize_comment', array( $this, 'wp_anonymize_comment_custom_message' ), 10, 3 );
     896        $actual = wp_comments_personal_data_eraser( $args['comment_author_email'] );
     897        remove_filter( 'wp_anonymize_comment', array( $this, 'wp_anonymize_comment_custom_message' ) );
     898
     899        $message = sprintf( 'Some custom message for comment %d.', $comment_id );
     900
     901        $expected = array(
     902            'num_items_removed'  => 0,
     903            'num_items_retained' => 1,
     904            'messages'           => array( $message ),
     905            'done'               => true,
     906        );
     907
     908        $this->assertSame( $expected, $actual );
     909    }
     910
     911    /**
     912     * Callback for the `wp_anonymize_comment` filter.
     913     *
     914     * @param  bool|string $anonymize          Whether to apply the comment anonymization (bool).
     915     *                                         Custom prevention message (string). Default true.
     916     * @param  WP_Comment  $comment            WP_Comment object.
     917     * @param  array       $anonymized_comment Anonymized comment data.
     918     * @return string
     919     */
     920    public function wp_anonymize_comment_custom_message( $anonymize, $comment, $anonymized_comment ) {
     921        return sprintf( 'Some custom message for comment %d.', $comment->comment_ID );
     922    }
     923
    703924    public function test_update_should_invalidate_comment_cache() {
    704925        global $wpdb;
Note: See TracChangeset for help on using the changeset viewer.