Make WordPress Core

Ticket #43438: 43438.3.diff

File 43438.3.diff, 17.6 KB (added by allendav, 7 years ago)

Added Ajax referrer check; current_user_can check; only get comments with email match; break comments up into batches of 100; coding standards

  • src/wp-admin/admin-ajax.php

     
    129129        'get-post-thumbnail-html',
    130130        'get-community-events',
    131131        'edit-theme-plugin-file',
     132        'wp-privacy-export-personal-data',
    132133);
    133134
    134135// Deprecated
  • src/wp-admin/includes/ajax-actions.php

     
    43264326                );
    43274327        }
    43284328}
     4329
     4330function wp_ajax_wp_privacy_export_personal_data() {
     4331        check_ajax_referer( 'wp-privacy-export-personal-data', 'security' );
     4332
     4333        if ( ! current_user_can( 'manage_options' ) ) {
     4334                wp_send_json_error( 'access denied' );
     4335        }
     4336
     4337        $email_address  = $_POST['email'];
     4338        $exporter_index = $_POST['exporter'];
     4339        $page           = $_POST['page'];
     4340
     4341        $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() );
     4342
     4343        if ( ! is_email( $email_address ) ) {
     4344                wp_send_json_error( 'a valid email address must be given' );
     4345        }
     4346
     4347        if ( $exporter_index < 0 ) {
     4348                wp_send_json_error( 'exporter index cannot be negative' );
     4349        }
     4350
     4351        if ( $exporter_index > count( $exporters ) - 1 ) {
     4352                wp_send_json_error( 'exporter index out of range' );
     4353        }
     4354
     4355        if ( $page < 0 ) {
     4356                wp_send_json_error( 'page index cannot be negative' );
     4357        }
     4358
     4359        $response = call_user_func( $exporters[ $exporter_index ]['callback'], $email_address, $page );
     4360
     4361        wp_send_json_success( $response );
     4362}
  • src/wp-admin/privacy.php

     
    1616// "Borrow" xfn.js for now so we don't have to create new files.
    1717// wp_enqueue_script( 'xfn' );
    1818
     19$title = __( 'Privacy Tools' );
     20
    1921$action = isset( $_POST['action'] ) ? $_POST['action'] : '';
    2022
     23$doing_personal_data_export_for_email = '';
     24
    2125if ( ! empty( $action ) ) {
    2226        check_admin_referer( $action );
    2327
     
    2428        if ( 'set-privacy-page' === $action ) {
    2529                $privacy_policy_page_id = isset( $_POST['page_for_privacy_policy'] ) ? (int) $_POST['page_for_privacy_policy'] : 0;
    2630                update_option( 'wp_page_for_privacy_policy', $privacy_policy_page_id );
    27 
    2831                add_settings_error(
    2932                        'page_for_privacy_policy',
    3033                        'page_for_privacy_policy',
     
    5760                                'updated'
    5861                        );
    5962                }
     63        } elseif ( 'export-personal-data' === $action ) {
     64                $username_or_email_address = isset( $_POST['username_or_email_to_export'] ) ? $_POST['username_or_email_to_export'] : '';
     65                $username_or_email_address = sanitize_text_field( $username_or_email_address );
     66
     67                if ( ! is_email( $username_or_email_address ) ) {
     68                        $user = get_user_by( 'login', $username_or_email_address );
     69                        if ( ! $user instanceof WP_User ) {
     70                                add_settings_error(
     71                                        'username_or_email_to_export',
     72                                        'username_or_email_to_export',
     73                                        __( 'Unable to export personal data. Username not recognized.' ),
     74                                        'error'
     75                                );
     76                        } else {
     77                                $doing_personal_data_export_for_email = $user->user_email;
     78                        }
     79                } else {
     80                        $doing_personal_data_export_for_email = $username_or_email_address;
     81                }
     82
     83                if ( ! empty( $doing_personal_data_export_for_email ) ) {
     84                        $title = __( 'Export Personal Data' );
     85                }
    6086        }
    6187}
    6288
    63 // If a privacy policy page ID is available, make sure the page actually exists. If not, display a warning
    64 $privacy_policy_page_exists = false;
    65 $privacy_policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
     89if ( empty( $doing_personal_data_export_for_email ) ) {
     90        // If a privacy policy page ID is available, make sure the page actually exists. If not, display a warning
     91        $privacy_policy_page_exists = false;
     92        $privacy_policy_page_id     = (int) get_option( 'wp_page_for_privacy_policy' );
    6693
    67 if ( ! empty( $privacy_policy_page_id ) ) {
     94        if ( ! empty( $privacy_policy_page_id ) ) {
    6895                $privacy_policy_page = get_post( $privacy_policy_page_id );
    6996                if ( ! $privacy_policy_page instanceof WP_Post ) {
    7097                        add_settings_error(
     
    79106                                        'page_for_privacy_policy',
    80107                                        'page_for_privacy_policy',
    81108                                        sprintf(
     109                                                /* translators: URL to trashed pages list view */
    82110                                                __( 'The currently selected privacy policy page is in the trash. Please create or select new privacy policy page or <a href="%s">restore the current page</a>.' ),
    83111                                                'edit.php?post_status=trash&post_type=page'
    84112                                        ),
     
    88116                                $privacy_policy_page_exists = true;
    89117                        }
    90118                }
    91 }
     119        }
    92120
    93 $title = __( 'Privacy Tools' );
     121        get_current_screen()->add_help_tab( array(
     122                'id'      => 'privacy',
     123                'title'   => __( 'Privacy' ),
     124                'content' => '<p>' . __( 'This page provides tools with which you can manage your user\'s personal data and site\'s privacy policy.' ) . '</p>',
     125        ) );
    94126
    95 get_current_screen()->add_help_tab( array(
    96         'id'      => 'privacy',
    97         'title'   => __( 'Privacy' ),
    98         'content' => '<p>' . __( 'This page provides tools with which you can manage your user\'s personal data and site\'s privacy policy.' ) . '</p>',
    99 ) );
     127        get_current_screen()->set_help_sidebar(
     128                '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
     129                '<p>' . __( '<a href="#">Documentation on privacy</a>' ) . '</p>'
     130        );
     131}
    100132
    101 get_current_screen()->set_help_sidebar(
    102         '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
    103         '<p>' . __( '<a href="#">Documentation on privacy</a>' ) . '</p>'
    104 );
    105 
    106133require_once( ABSPATH . 'wp-admin/admin-header.php' );
    107134
    108135?>
     
    109136<div class="wrap">
    110137        <h1><?php echo esc_html( $title ); ?></h1>
    111138        <?php settings_errors(); ?>
     139        <?php
    112140
    113         <h2 class="title"><?php _e( 'Privacy policy page' ); ?></h2>
    114         <table class="form-table">
     141        // If we have an email address, bootstrap the exporter
     142        if ( ! empty( $doing_personal_data_export_for_email ) ) {
     143                if ( $username_or_email_address === $doing_personal_data_export_for_email ) {
     144                        $heading = sprintf(
     145                                /* translators: An email address */
     146                                __( 'Exporting personal data for %s' ),
     147                                $doing_personal_data_export_for_email
     148                        );
     149                } else {
     150                        $heading = sprintf(
     151                                /* translators: An email address and a username */
     152                                __( 'Exporting personal data for %1$s (%2$s)' ),
     153                                $doing_personal_data_export_for_email,
     154                                $username_or_email_address
     155                        );
     156                }
     157                ?>
     158                <p>
     159                        <?php echo esc_html( $heading ); ?>
     160                </p>
     161
     162                <div id="exporters-container">
     163                        <ul id="exporter-list">
     164                        </ul>
     165                        <table class="wp-list-table widefat striped" id="exported-data-table">
     166                                <tr>
     167                                        <th><?php _e( 'Name' ); ?></th>
     168                                        <th><?php _e( 'Value' ); ?></th>
     169                                </tr>
     170                        </table>
     171                </div>
     172
    115173                <?php
     174                // Build the array of exporters and emit it as an array accessible to javascript along with a nonce
     175                $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() );
    116176
    117                 if ( $privacy_policy_page_exists ) {
    118                         $edit_href = add_query_arg(
    119                                 array(
    120                                         'post'  => $privacy_policy_page_id,
    121                                         'action' => 'edit',
    122                                 ),
    123                                 admin_url( 'post.php' )
    124                         );
    125                         $view_href = get_permalink( $privacy_policy_page_id );
    126 
    127                         ?>
    128                         <tr>
    129                                 <th colspan="2">
    130                                         <?php
    131                                         printf(
    132                                                 __( '<a href="%1$s">Edit</a> or <a href="%2$s">view</a> your privacy policy.' ),
    133                                                 $edit_href,
    134                                                 $view_href
    135                                         );
    136                                         ?>
    137                                 </th>
    138                         </tr>
    139                         <?php
     177                $exporter_names = array();
     178                foreach ( (array) $exporters as $exporter ) {
     179                        $exporter_names[] = $exporter['exporter_friendly_name'];
    140180                }
    141181
    142182                ?>
    143                 <tr>
    144                         <th scope="row">
     183
     184                <!-- scripts for the exporter -->
     185
     186                <script>
     187                        ( function( $ ) {
     188                                $( document ).ready( function() {
     189                                        var nonce = <?php echo json_encode( wp_create_nonce( 'wp-privacy-export-personal-data' ) ); ?>;
     190                                        var exportForEmail = <?php echo json_encode( $doing_personal_data_export_for_email ); ?>;
     191                                        var exporterNames = <?php echo json_encode( $exporter_names ); ?>;
     192                                        var successMessage = "<?php echo esc_attr( __( 'Export completed successfully' ) ); ?>";
     193                                        var failureMessage = "<?php echo esc_attr( __( 'A failure occurred during export' ) ); ?>";
     194
     195                                        function on_exports_done_success() {
     196                                                $( '#exporters-container' ).append( '<p>' + successMessage + '</p>' );
     197                                        }
     198
     199                                        function on_export_failure( textStatus, error ) {
     200                                                $( '#exporters-container' ).append( '<p>' + failureMessage + '</p>' );
     201                                                $( '#exporters-container' ).append( '<p>' + textStatus + '</p>' );
     202                                        }
     203
     204                                        function do_next_export( exporterIndex, pageIndex ) {
     205                                                $.ajax( {
     206                                                        url: ajaxurl,
     207                                                        data: {
     208                                                                action: 'wp-privacy-export-personal-data',
     209                                                                email: exportForEmail,
     210                                                                exporter: exporterIndex,
     211                                                                page: pageIndex,
     212                                                                security: nonce,
     213                                                        },
     214                                                        method: 'post'
     215                                                } ).done( function( response ) {
     216                                                        var responseData = response.data;
     217                                                        for ( var dataIndex = 0; dataIndex < responseData.data.length; dataIndex++ ) {
     218                                                                $( '#exported-data-table' ).append( '<tr><td>' + responseData.data[ dataIndex ].name + '</td><td>' + responseData.data[ dataIndex ].value + '</td></tr>' );
     219                                                        }
     220                                                        if ( ! responseData.done ) {
     221                                                                setTimeout( do_next_export( exporterIndex, pageIndex + 1 ) );
     222                                                        } else {
     223                                                                if ( exporterIndex < exporterNames.length - 1 ) {
     224                                                                        setTimeout( do_next_export( exporterIndex + 1, 0 ) );
     225                                                                } else {
     226                                                                        on_exports_done_success();
     227                                                                }
     228                                                        }
     229                                                } ).fail( function( jqxhr, textStatus, error ) {
     230                                                        on_export_failure( textStatus, error );
     231                                                } );
     232                                        }
     233
     234                                        // And now, let's begin
     235                                        do_next_export( 0, 0 );
     236                                } )
     237                        } ) ( jQuery );
     238                </script>
     239
     240                <p>
     241                        <a href="privacy.php"><?php esc_html_e( 'Return to privacy tools' ); ?></a>
     242                </p>
     243                <?php
     244        } else {
     245                // No email address to export for? Then just display the privacy tools form table
     246                ?>
     247                <h2 class="title"><?php _e( 'Privacy policy page' ); ?></h2>
     248                <table class="form-table">
    145249                        <?php
    146250
    147251                        if ( $privacy_policy_page_exists ) {
    148                                 _e( 'Select another page for your privacy policy' );
    149                         } else {
    150                                 _e( 'Select an existing privacy policy page' );
     252                                $edit_href = add_query_arg(
     253                                        array(
     254                                                'post'   => $privacy_policy_page_id,
     255                                                'action' => 'edit',
     256                                        ),
     257                                        admin_url( 'post.php' )
     258                                );
     259                                $view_href = get_permalink( $privacy_policy_page_id );
     260
     261                                ?>
     262                                <tr>
     263                                        <th colspan="2">
     264                                                <?php
     265                                                printf(
     266                                                        /* translators: URL to edit the privacy policy page, URL to view the privacy policy page */
     267                                                        __( '<a href="%1$s">Edit</a> or <a href="%2$s">view</a> your privacy policy.' ),
     268                                                        $edit_href,
     269                                                        $view_href
     270                                                );
     271                                                ?>
     272                                        </th>
     273                                </tr>
     274                                <?php
    151275                        }
    152276
    153277                        ?>
    154                         </th>
    155                         <td id="front-static-pages">
    156                                 <form method="post" action="">
    157                                         <?php wp_nonce_field( 'set-privacy-page' ); ?>
    158                                         <input type="hidden" name="action" value="set-privacy-page" />
    159                                         <fieldset>
    160                                                 <legend class="screen-reader-text"><span><?php _e( 'Select your privacy policy page.' ); ?></span></legend>
    161                                                 <label for="page_for_privacy_policy">
    162                                                         <?php wp_dropdown_pages(
    163                                                                 array(
    164                                                                         'name'              => 'page_for_privacy_policy',
    165                                                                         'show_option_none'  => __( '&mdash; Select &mdash;' ),
    166                                                                         'option_none_value' => '0',
    167                                                                         'selected'          => $privacy_policy_page_id,
    168                                                                         'post_status'       => array( 'draft', 'publish' ),
    169                                                                 )
    170                                                         );
    171                                                         ?>
    172                                                 </label>
    173                                         </fieldset>
    174                                                 <?php submit_button( __( 'Set Page' ) ); ?>
    175                                 </form>
    176                         </td>
    177                 </tr>
    178                 <?php
     278                        <tr>
     279                                <th scope="row">
     280                                <?php
    179281
    180                 if ( ! $privacy_policy_page_exists ) {
     282                                if ( $privacy_policy_page_exists ) {
     283                                        _e( 'Select another page for your privacy policy' );
     284                                } else {
     285                                        _e( 'Select an existing privacy policy page' );
     286                                }
     287
     288                                ?>
     289                                </th>
     290                                <td id="privacy-policy-page">
     291                                        <form method="post" action="">
     292                                                <?php wp_nonce_field( 'set-privacy-page' ); ?>
     293                                                <input type="hidden" name="action" value="set-privacy-page" />
     294                                                <fieldset>
     295                                                        <legend class="screen-reader-text"><span><?php _e( 'Select your privacy policy page.' ); ?></span></legend>
     296                                                        <label for="page_for_privacy_policy">
     297                                                                <?php
     298                                                                wp_dropdown_pages(
     299                                                                        array(
     300                                                                                'name'             => 'page_for_privacy_policy',
     301                                                                                'show_option_none' => __( '&mdash; Select &mdash;' ),
     302                                                                                'option_none_value' => '0',
     303                                                                                'selected'         => $privacy_policy_page_id,
     304                                                                                'post_status'      => array( 'draft', 'publish' ),
     305                                                                        )
     306                                                                );
     307                                                                ?>
     308                                                        </label>
     309                                                </fieldset>
     310                                                        <?php submit_button( __( 'Set Page' ) ); ?>
     311                                        </form>
     312                                </td>
     313                        </tr>
     314                        <?php
     315
     316                        if ( ! $privacy_policy_page_exists ) {
     317                                ?>
     318                                <tr>
     319                                        <th scope="row"><?php _e( 'Create new page for your privacy policy' ); ?></th>
     320                                        <td>
     321                                                <form method="post" action="">
     322                                                        <input type="hidden" name="action" value="create-privacy-page" />
     323                                                        <?php wp_nonce_field( 'create-privacy-page' ); ?>
     324                                                        <?php submit_button( __( 'Create Page' ) ); ?>
     325                                                </form>
     326                                        </td>
     327                                </tr>
     328                                <?php
     329                        }
    181330                        ?>
     331                </table>
     332
     333                <h2 class="title"><?php _e( 'Personal Data Management' ); ?></h2>
     334                <table class="form-table">
    182335                        <tr>
    183                                 <th scope="row"><?php _e( 'Create new page for your privacy policy' ); ?></th>
    184                                 <td>
     336                                <th scope="row"><?php _e( 'Export personal data' ); ?></th>
     337                                <td id="export-personal-data-form-container">
    185338                                        <form method="post" action="">
    186                                                 <input type="hidden" name="action" value="create-privacy-page" />
    187                                                 <?php wp_nonce_field( 'create-privacy-page' ); ?>
    188                                                 <?php submit_button( __( 'Create Page' ) ); ?>
     339                                                <input type="hidden" name="action" value="export-personal-data" />
     340                                                <?php wp_nonce_field( 'export-personal-data' ); ?>
     341                                                <fieldset>
     342                                                        <legend class="screen-reader-text"><span><?php _e( 'Enter the username or email address of the user whose personal data you wish to export' ); ?></span></legend>
     343                                                        <label for="username_or_email_to_export">
     344                                                                <input type="text" class="regular-text" name="username_or_email_to_export" />
     345                                                        </label>
     346                                                        <p class="description"><?php _e( 'The username or email address of the user whose personal data you wish to export' ); ?></p>
     347                                                </fieldset>
     348                                                <?php submit_button( __( 'Begin Export' ) ); ?>
    189349                                        </form>
    190350                                </td>
    191351                        </tr>
    192                         <?php
    193                 }
    194 
    195                 ?>
    196         </table>
     352                </table>
     353                <?php
     354        }
     355        ?>
    197356</div>
    198357
    199358<?php
  • src/wp-includes/comment.php

     
    32743274
    32753275        return get_comment( $comment_id );
    32763276}
     3277
     3278/**
     3279 * Registers the personal data exporter for comments
     3280 *
     3281 * @param array   $exporters   An array of personal data exporters.
     3282 * @return array  An array of personal data exporters.
     3283 */
     3284function wp_register_comment_personal_data_exporter( $exporters ) {
     3285        $exporters[] = array(
     3286                'exporter_friendly_name' => __( 'WordPress Comments' ),
     3287                'callback'               => 'wp_comments_personal_data_exporter',
     3288        );
     3289
     3290        return $exporters;
     3291}
     3292
     3293/**
     3294 * Finds and exports data which could be used to identify a person from comments assocated with an email address.
     3295 *
     3296 * @param string  $email  The comment author email address.
     3297 * @param int     $page   Comment page, zero based.
     3298 * @return array  An array of personal data in name value pairs
     3299 */
     3300function wp_comments_personal_data_exporter( $email_address, $page ) {
     3301
     3302        $personal_data = array();
     3303
     3304        // Technically, strtolower isn't necessary since get_comments will match email insensitive
     3305        // But it is a good example for plugin developers whose targets might not be as generous
     3306        $email_address = trim( strtolower( $email_address ) );
     3307
     3308        // Limit us to 100 comments at a time to avoid timing out
     3309        $number = 100;
     3310        $offset = $number * (int) $page;
     3311
     3312        $comments = get_comments(
     3313                array(
     3314                        'author_email' => $email_address,
     3315                        'number'       => $number,
     3316                        'offset'       => $offset,
     3317                        'order_by'     => 'comment_ID',
     3318                        'order'        => 'ASC',
     3319                )
     3320        );
     3321
     3322        $comment_personal_data_props = array(
     3323                'comment_author'       => __( 'Comment Author' ),
     3324                'comment_author_email' => __( 'Comment Author Email' ),
     3325                'comment_author_url'   => __( 'Comment Author URL' ),
     3326                'comment_author_IP'    => __( 'Comment Author IP' ),
     3327                'comment_agent'        => __( 'Comment Agent' ),
     3328        );
     3329
     3330        foreach ( (array) $comments as $comment ) {
     3331                foreach ( (array) $comment_personal_data_props as $key => $description ) {
     3332                        $value = $comment->$key;
     3333                        if ( ! empty( $value ) ) {
     3334                                /* translators: %1$d: ID of a comment, %2$s: Description of a comment field */
     3335                                $name            = sprintf( __( 'Comment %1$d : %2$s' ), $comment->comment_ID, $description );
     3336                                $personal_data[] = array(
     3337                                        'name'  => $name,
     3338                                        'value' => $value,
     3339                                );
     3340                        }
     3341                }
     3342        }
     3343
     3344        $done = count( $comments ) <= 0;
     3345
     3346        return array(
     3347                'data' => $personal_data,
     3348                'done' => $done,
     3349        );
     3350}
  • src/wp-includes/default-filters.php

     
    328328add_action( 'do_pings', 'do_all_pings', 10, 1 );
    329329add_action( 'do_robots', 'do_robots' );
    330330add_action( 'set_comment_cookies', 'wp_set_comment_cookies', 10, 3 );
     331add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter', 10 );
    331332add_action( 'sanitize_comment_cookies', 'sanitize_comment_cookies' );
    332333add_action( 'admin_print_scripts', 'print_emoji_detection_script' );
    333334add_action( 'admin_print_scripts', 'print_head_scripts', 20 );