Make WordPress Core

Ticket #43637: 43637.diff

File 43637.diff, 10.8 KB (added by allendav, 7 years ago)

Adds personal data eraser filtering and ajax supportr

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

     
    130130        'get-community-events',
    131131        'edit-theme-plugin-file',
    132132        'wp-privacy-export-personal-data',
     133        'wp-privacy-erase-personal-data',
    133134);
    134135
    135136// Deprecated
  • src/wp-admin/includes/ajax-actions.php

     
    43274327        }
    43284328}
    43294329
     4330/**
     4331 * Ajax handler for exporting a page of personal data.
     4332 *
     4333 * @since 4.9.5
     4334 */
    43304335function wp_ajax_wp_privacy_export_personal_data() {
    4331 //      check_ajax_referer( 'wp-privacy-export-personal-data', 'security' );
     4336        check_ajax_referer( 'wp-privacy-export-personal-data', 'security' );
    43324337
     4338        $email_address   = sanitize_text_field( $_POST['email'] );
     4339        $processor_index = (int) $_POST['exporter'];
     4340        $page            = (int) $_POST['page'];
     4341
     4342        wp_privacy_do_personal_data_callback( 'export', $processor_index, $email_address, $page );
     4343}
     4344
     4345/**
     4346 * Ajax handler for erasing a page of personal data.
     4347 *
     4348 * @since 4.9.5
     4349 */
     4350function wp_ajax_wp_privacy_erase_personal_data() {
     4351        check_ajax_referer( 'wp-privacy-erase-personal-data', 'security' );
     4352
     4353        $email_address   = sanitize_text_field( $_POST['email'] );
     4354        $processor_index = (int) $_POST['eraser'];
     4355        $page            = (int) $_POST['page'];
     4356
     4357        wp_privacy_do_personal_data_callback( 'erase', $processor_index, $email_address, $page );
     4358}
     4359
     4360/**
     4361 * Handler for exporting or erasing a page of personal data.
     4362 *
     4363 * @since 4.9.5
     4364 *
     4365 * @param string $type            The type of processing requested: "export" or "erase"
     4366 * @param int    $processor_index The index of the processor that should be called.
     4367 * @param string $email_address   The email address for which processing is being performed.
     4368 * @param int    $page            The 1-based page of data to be processed.
     4369 */
     4370
     4371function wp_privacy_do_personal_data_callback( $type, $processor_index, $email_address, $page ) {
    43334372        if ( ! current_user_can( 'manage_options' ) ) {
    43344373                wp_send_json_error( 'access denied' );
    43354374        }
    43364375
    4337         $email_address  = sanitize_text_field( $_POST['email'] );
    4338         $exporter_index = (int) $_POST['exporter'];
    4339         $page           = (int) $_POST['page'];
     4376        $allowed_types = array( 'export', 'erase' );
    43404377
    4341         /**
    4342          * Filters the array of exporter callbacks.
    4343          *
    4344          * @since 4.9.5.
    4345          *
    4346          * @param array $args {
    4347          *     An array of callable exporters of personal data. Default empty array.
    4348          *     [
    4349          *         callback               string  Callable exporter that accepts an email address and
    4350          *                                        a page and returns an array of name => value
    4351          *                                        pairs of personal data
    4352          *         exporter_friendly_name string  Translated user facing friendly name for the exporter
    4353          *     ]
    4354          * }
    4355          */
    4356         $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() );
     4378        if ( ! in_array( $type, $allowed_types ) ) {
     4379                wp_send_json_error( 'invalid processor type' );
     4380        }
    43574381
    4358         if ( ! is_array( $exporters ) ) {
    4359                 wp_send_json_error( 'An exporter has improperly used the registration filter.' );
     4382        $processors = array();
     4383        if ( 'export' === $type ) {
     4384                /**
     4385                 * Filters the array of exporter callbacks.
     4386                 *
     4387                 * @since 4.9.5.
     4388                 *
     4389                 * @param array $args {
     4390                 *     An array of callable exporters of personal data. Default empty array.
     4391                 *     [
     4392                 *         callback               string  Callable exporter that accepts an email address and
     4393                 *                                        a page and returns an array of personal data
     4394                 *         exporter_friendly_name string  Translated user facing friendly name for the exporter
     4395                 *     ]
     4396                 * }
     4397                 */
     4398                $processors = apply_filters( 'wp_privacy_personal_data_exporters', $processors );
     4399        } else if ( 'erase' === $type ) {
     4400                /**
     4401                 * Filters the array of eraser callbacks.
     4402                 *
     4403                 * @since 4.9.5.
     4404                 *
     4405                 * @param array $args {
     4406                 *     An array of callable erasers of personal data. Default empty array.
     4407                 *     [
     4408                 *         callback             string  Callable eraser that accepts an email address and page
     4409                 *         eraser_friendly_name string  Translated user facing friendly name for the eraser
     4410                 *     ]
     4411                 * }
     4412                 */
     4413                $processors = apply_filters( 'wp_privacy_personal_data_erasers', $processors );
    43604414        }
     4415        if ( ! is_array( $processors ) ) {
     4416                wp_send_json_error( 'A processor has improperly used the registration filter.' );
     4417        }
    43614418
    4362         // Do we have any registered exporters?
    4363         if ( 0 < count( $exporters ) ) {
    4364                 if ( $exporter_index < 1 ) {
    4365                         wp_send_json_error( 'Exporter index cannot be negative.' );
    4366                 }
     4419        // Initialize the default response
     4420        $response = array(
     4421                'data' => array(),
     4422                'done' => true,
     4423        );
    43674424
    4368                 if ( $exporter_index > count( $exporters ) ) {
    4369                         wp_send_json_error( 'Exporter index out of range.' );
    4370                 }
     4425        // No registered processors?  We're done
     4426        if ( 0 === count( $processors ) ) {
     4427                wp_send_json_success( $response );
     4428        }
    43714429
    4372                 $index = $exporter_index - 1;
     4430        // Otherwise, let's validate and handle the request
     4431        if ( $processor_index < 1 ) {
     4432                wp_send_json_error( 'Processor index cannot be less than one.' );
     4433        }
    43734434
    4374                 if ( $page < 1 ) {
    4375                         wp_send_json_error( 'Page index cannot be less than one.' );
    4376                 }
     4435        if ( $processor_index > count( $processors ) ) {
     4436                wp_send_json_error( 'Processor index out of range.' );
     4437        }
    43774438
    4378                 // Surprisingly, email addresses can contain mutli-byte characters now
    4379                 $email_address = trim( mb_strtolower( $email_address ) );
     4439        if ( $page < 1 ) {
     4440                wp_send_json_error( 'Page index cannot be less than one.' );
     4441        }
    43804442
    4381                 if ( ! is_email( $email_address ) ) {
    4382                         wp_send_json_error( 'A valid email address must be given.' );
    4383                 }
     4443        // Surprisingly, email addresses can contain mutli-byte characters now
     4444        $email_address = trim( mb_strtolower( $email_address ) );
    43844445
    4385                 $exporter = $exporters[ $index ];
    4386                 if ( ! is_array( $exporter ) ) {
    4387                         wp_send_json_error( "Expected an array describing the exporter at index {$exporter_index}." );
    4388                 }
    4389                 if ( ! array_key_exists( 'callback', $exporter ) ) {
    4390                         wp_send_json_error( "Exporter array at index {$exporter_index} does not include a callback." );
    4391                 }
    4392                 if ( ! is_callable( $exporter['callback'] ) ) {
    4393                         wp_send_json_error( "Exporter callback at index {$exporter_index} is not a valid callback." );
    4394                 }
    4395                 if ( ! array_key_exists( 'exporter_friendly_name', $exporter ) ) {
    4396                         wp_send_json_error( "Exporter array at index {$exporter_index} does not include a friendly name." );
    4397                 }
     4446        if ( ! is_email( $email_address ) ) {
     4447                wp_send_json_error( 'A valid email address must be given.' );
     4448        }
    43984449
    4399                 $callback = $exporters[ $index ]['callback'];
    4400                 $exporter_friendly_name = $exporters[ $index ]['exporter_friendly_name'];
     4450        $processor = $processors[ $processor_index - 1 ];
     4451        if ( ! is_array( $processor ) ) {
     4452                wp_send_json_error( "Expected an array describing the processor at index {$processor_index}." );
     4453        }
     4454        if ( ! array_key_exists( 'callback', $processor ) ) {
     4455                wp_send_json_error( "Processor array at index {$processor_index} does not include a callback." );
     4456        }
     4457        if ( ! is_callable( $processor['callback'] ) ) {
     4458                wp_send_json_error( "Processor callback at index {$processor_index} is not a valid callback." );
     4459        }
    44014460
    4402                 $response = call_user_func( $callback, $email_address, $page );
    4403                 if ( is_wp_error( $response ) ) {
    4404                         wp_send_json_error( $response );
    4405                 }
     4461        $response = call_user_func( $processor['callback'], $email_address, $page );
     4462        if ( is_wp_error( $response ) ) {
     4463                wp_send_json_error( $response );
     4464        }
    44064465
    4407                 if ( ! is_array( $response ) ) {
    4408                         wp_send_json_error( "Expected response as an array from exporter: {$exporter_friendly_name}." );
     4466        if ( 'export' === $type ) {
     4467                $processor_friendly_name = __( 'Unnamed exporter' );
     4468        } else if ( 'erase' === $type ) {
     4469                $processor_friendly_name = __( 'Unnamed eraser' );
     4470        }
     4471
     4472        if ( array_key_exists( 'friendly_name', $processor ) ) {
     4473                if ( ! empty( $processor['friendly_name'] ) ) {
     4474                        $processor_friendly_name = $processor['friendly_name'];
    44094475                }
    4410                 if ( ! array_key_exists( 'data', $response ) ) {
    4411                         wp_send_json_error( "Expected data in response array from exporter: {$exporter_friendly_name}." );
    4412                 }
    4413                 if ( ! is_array( $response['data'] ) ) {
    4414                         wp_send_json_error( "Expected data array in response array from exporter: {$exporter_friendly_name}." );
    4415                 }
    4416                 if ( ! array_key_exists( 'done', $response ) ) {
    4417                         wp_send_json_error( "Expected done (boolean) in response array from exporter: {$exporter_friendly_name}." );
    4418                 }
    4419         } else {
    4420                 // No exporters, so we're done
    4421                 $response = array(
    4422                         'data' => array(),
    4423                         'done' => true,
    4424                 );
    44254476        }
    44264477
    4427         /**
    4428          * Filters a page of personal data exporter data. Used to build the export report.
    4429          *
    4430          * Allows the export response to be consumed by destinations in addition to Ajax.
    4431          *
    4432          * @since 4.9.5
    4433          *
    4434          * @param array  $response        The personal data for the given exporter and page.
    4435          * @param int    $exporter_index  The index of the exporter that provided this data.
    4436          * @param string $email_address   The email address associated with this personal data.
    4437          * @param int    $page            The zero-based page for this response.
    4438          */
    4439         $response = apply_filters( 'wp_privacy_personal_data_export_page', $response, $exporter_index, $email_address, $page );
     4478        if ( ! is_array( $response ) ) {
     4479                wp_send_json_error( "Expected response as an array from {$processor_friendly_name}." );
     4480        }
     4481        if ( ! array_key_exists( 'data', $response ) ) {
     4482                wp_send_json_error( "Expected data in response array from {$processor_friendly_name}." );
     4483        }
     4484        if ( ! is_array( $response['data'] ) ) {
     4485                wp_send_json_error( "Expected data array in response array from {$processor_friendly_name}." );
     4486        }
     4487        if ( ! array_key_exists( 'done', $response ) ) {
     4488                wp_send_json_error( "Expected done (boolean) in response array from {$processor_friendly_name}." );
     4489        }
     4490
     4491        // If we're doing an export, run the export filter (for things like export file assembly)
     4492        if ( 'export' === $type ) {
     4493                /**
     4494                 * Filters a page of personal data exporter data. Used to build the export report.
     4495                 *
     4496                 * Allows the export response to be consumed by destinations in addition to Ajax.
     4497                 *
     4498                 * @since 4.9.5
     4499                 *
     4500                 * @param array  $response        The personal data for the given exporter and page.
     4501                 * @param int    $processor_index The index of the exporter that provided this data.
     4502                 * @param string $email_address   The email address associated with this personal data.
     4503                 * @param int    $page            The 1-based page for this response.
     4504                 */
     4505                $response = apply_filters( 'wp_privacy_personal_data_export_page', $response, $processor_index, $email_address, $page );
     4506        }
     4507
     4508        // And lastly, send our response
    44404509        if ( is_wp_error( $response ) ) {
    44414510                wp_send_json_error( $response );
    44424511        }