[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/includes/ -> privacy-tools.php (source)

   1  <?php
   2  /**
   3   * WordPress Administration Privacy Tools API.
   4   *
   5   * @package WordPress
   6   * @subpackage Administration
   7   */
   8  
   9  /**
  10   * Resend an existing request and return the result.
  11   *
  12   * @since 4.9.6
  13   * @access private
  14   *
  15   * @param int $request_id Request ID.
  16   * @return bool|WP_Error Returns true/false based on the success of sending the email, or a WP_Error object.
  17   */
  18  function _wp_privacy_resend_request( $request_id ) {
  19      $request_id = absint( $request_id );
  20      $request    = get_post( $request_id );
  21  
  22      if ( ! $request || 'user_request' !== $request->post_type ) {
  23          return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) );
  24      }
  25  
  26      $result = wp_send_user_request( $request_id );
  27  
  28      if ( is_wp_error( $result ) ) {
  29          return $result;
  30      } elseif ( ! $result ) {
  31          return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation request.' ) );
  32      }
  33  
  34      return true;
  35  }
  36  
  37  /**
  38   * Marks a request as completed by the admin and logs the current timestamp.
  39   *
  40   * @since 4.9.6
  41   * @access private
  42   *
  43   * @param  int          $request_id Request ID.
  44   * @return int|WP_Error $result Request ID on success or WP_Error.
  45   */
  46  function _wp_privacy_completed_request( $request_id ) {
  47      $request_id = absint( $request_id );
  48      $request    = wp_get_user_request_data( $request_id );
  49  
  50      if ( ! $request ) {
  51          return new WP_Error( 'privacy_request_error', __( 'Invalid request.' ) );
  52      }
  53  
  54      update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() );
  55  
  56      $result = wp_update_post(
  57          array(
  58              'ID'          => $request_id,
  59              'post_status' => 'request-completed',
  60          )
  61      );
  62  
  63      return $result;
  64  }
  65  
  66  /**
  67   * Handle list table actions.
  68   *
  69   * @since 4.9.6
  70   * @access private
  71   */
  72  function _wp_personal_data_handle_actions() {
  73      if ( isset( $_POST['privacy_action_email_retry'] ) ) {
  74          check_admin_referer( 'bulk-privacy_requests' );
  75  
  76          $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['privacy_action_email_retry'] ) ) ) );
  77          $result     = _wp_privacy_resend_request( $request_id );
  78  
  79          if ( is_wp_error( $result ) ) {
  80              add_settings_error(
  81                  'privacy_action_email_retry',
  82                  'privacy_action_email_retry',
  83                  $result->get_error_message(),
  84                  'error'
  85              );
  86          } else {
  87              add_settings_error(
  88                  'privacy_action_email_retry',
  89                  'privacy_action_email_retry',
  90                  __( 'Confirmation request sent again successfully.' ),
  91                  'success'
  92              );
  93          }
  94      } elseif ( isset( $_POST['action'] ) ) {
  95          $action = ! empty( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : '';
  96  
  97          switch ( $action ) {
  98              case 'add_export_personal_data_request':
  99              case 'add_remove_personal_data_request':
 100                  check_admin_referer( 'personal-data-request' );
 101  
 102                  if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) {
 103                      add_settings_error(
 104                          'action_type',
 105                          'action_type',
 106                          __( 'Invalid action.' ),
 107                          'error'
 108                      );
 109                  }
 110                  $action_type               = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) );
 111                  $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) );
 112                  $email_address             = '';
 113  
 114                  if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) {
 115                      add_settings_error(
 116                          'action_type',
 117                          'action_type',
 118                          __( 'Invalid action.' ),
 119                          'error'
 120                      );
 121                  }
 122  
 123                  if ( ! is_email( $username_or_email_address ) ) {
 124                      $user = get_user_by( 'login', $username_or_email_address );
 125                      if ( ! $user instanceof WP_User ) {
 126                          add_settings_error(
 127                              'username_or_email_for_privacy_request',
 128                              'username_or_email_for_privacy_request',
 129                              __( 'Unable to add this request. A valid email address or username must be supplied.' ),
 130                              'error'
 131                          );
 132                      } else {
 133                          $email_address = $user->user_email;
 134                      }
 135                  } else {
 136                      $email_address = $username_or_email_address;
 137                  }
 138  
 139                  if ( empty( $email_address ) ) {
 140                      break;
 141                  }
 142  
 143                  $request_id = wp_create_user_request( $email_address, $action_type );
 144  
 145                  if ( is_wp_error( $request_id ) ) {
 146                      add_settings_error(
 147                          'username_or_email_for_privacy_request',
 148                          'username_or_email_for_privacy_request',
 149                          $request_id->get_error_message(),
 150                          'error'
 151                      );
 152                      break;
 153                  } elseif ( ! $request_id ) {
 154                      add_settings_error(
 155                          'username_or_email_for_privacy_request',
 156                          'username_or_email_for_privacy_request',
 157                          __( 'Unable to initiate confirmation request.' ),
 158                          'error'
 159                      );
 160                      break;
 161                  }
 162  
 163                  wp_send_user_request( $request_id );
 164  
 165                  add_settings_error(
 166                      'username_or_email_for_privacy_request',
 167                      'username_or_email_for_privacy_request',
 168                      __( 'Confirmation request initiated successfully.' ),
 169                      'success'
 170                  );
 171                  break;
 172          }
 173      }
 174  }
 175  
 176  /**
 177   * Cleans up failed and expired requests before displaying the list table.
 178   *
 179   * @since 4.9.6
 180   * @access private
 181   */
 182  function _wp_personal_data_cleanup_requests() {
 183      /** This filter is documented in wp-includes/user.php */
 184      $expires = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS );
 185  
 186      $requests_query = new WP_Query(
 187          array(
 188              'post_type'      => 'user_request',
 189              'posts_per_page' => -1,
 190              'post_status'    => 'request-pending',
 191              'fields'         => 'ids',
 192              'date_query'     => array(
 193                  array(
 194                      'column' => 'post_modified_gmt',
 195                      'before' => $expires . ' seconds ago',
 196                  ),
 197              ),
 198          )
 199      );
 200  
 201      $request_ids = $requests_query->posts;
 202  
 203      foreach ( $request_ids as $request_id ) {
 204          wp_update_post(
 205              array(
 206                  'ID'            => $request_id,
 207                  'post_status'   => 'request-failed',
 208                  'post_password' => '',
 209              )
 210          );
 211      }
 212  }
 213  
 214  /**
 215   * Generate a single group for the personal data export report.
 216   *
 217   * @since 4.9.6
 218   *
 219   * @param array $group_data {
 220   *     The group data to render.
 221   *
 222   *     @type string $group_label  The user-facing heading for the group, e.g. 'Comments'.
 223   *     @type array  $items        {
 224   *         An array of group items.
 225   *
 226   *         @type array  $group_item_data  {
 227   *             An array of name-value pairs for the item.
 228   *
 229   *             @type string $name   The user-facing name of an item name-value pair, e.g. 'IP Address'.
 230   *             @type string $value  The user-facing value of an item data pair, e.g. '50.60.70.0'.
 231   *         }
 232   *     }
 233   * }
 234   * @return string The HTML for this group and its items.
 235   */
 236  function wp_privacy_generate_personal_data_export_group_html( $group_data ) {
 237      $group_html = '<h2>' . esc_html( $group_data['group_label'] ) . '</h2>';
 238  
 239      if ( ! empty( $group_data['group_description'] ) ) {
 240          $group_html .= '<p>' . esc_html( $group_data['group_description'] ) . '</p>';
 241      }
 242  
 243      $group_html .= '<div>';
 244  
 245      foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) {
 246          $group_html .= '<table>';
 247          $group_html .= '<tbody>';
 248  
 249          foreach ( (array) $group_item_data as $group_item_datum ) {
 250              $value = $group_item_datum['value'];
 251              // If it looks like a link, make it a link.
 252              if ( false === strpos( $value, ' ' ) && ( 0 === strpos( $value, 'http://' ) || 0 === strpos( $value, 'https://' ) ) ) {
 253                  $value = '<a href="' . esc_url( $value ) . '">' . esc_html( $value ) . '</a>';
 254              }
 255  
 256              $group_html .= '<tr>';
 257              $group_html .= '<th>' . esc_html( $group_item_datum['name'] ) . '</th>';
 258              $group_html .= '<td>' . wp_kses( $value, 'personal_data_export' ) . '</td>';
 259              $group_html .= '</tr>';
 260          }
 261  
 262          $group_html .= '</tbody>';
 263          $group_html .= '</table>';
 264      }
 265  
 266      $group_html .= '</div>';
 267  
 268      return $group_html;
 269  }
 270  
 271  /**
 272   * Generate the personal data export file.
 273   *
 274   * @since 4.9.6
 275   *
 276   * @param int $request_id The export request ID.
 277   */
 278  function wp_privacy_generate_personal_data_export_file( $request_id ) {
 279      if ( ! class_exists( 'ZipArchive' ) ) {
 280          wp_send_json_error( __( 'Unable to generate export file. ZipArchive not available.' ) );
 281      }
 282  
 283      // Get the request data.
 284      $request = wp_get_user_request_data( $request_id );
 285  
 286      if ( ! $request || 'export_personal_data' !== $request->action_name ) {
 287          wp_send_json_error( __( 'Invalid request ID when generating export file.' ) );
 288      }
 289  
 290      $email_address = $request->email;
 291  
 292      if ( ! is_email( $email_address ) ) {
 293          wp_send_json_error( __( 'Invalid email address when generating export file.' ) );
 294      }
 295  
 296      // Create the exports folder if needed.
 297      $exports_dir = wp_privacy_exports_dir();
 298      $exports_url = wp_privacy_exports_url();
 299  
 300      if ( ! wp_mkdir_p( $exports_dir ) ) {
 301          wp_send_json_error( __( 'Unable to create export folder.' ) );
 302      }
 303  
 304      // Protect export folder from browsing.
 305      $index_pathname = $exports_dir . 'index.html';
 306      if ( ! file_exists( $index_pathname ) ) {
 307          $file = fopen( $index_pathname, 'w' );
 308          if ( false === $file ) {
 309              wp_send_json_error( __( 'Unable to protect export folder from browsing.' ) );
 310          }
 311          fwrite( $file, '<!-- Silence is golden. -->' );
 312          fclose( $file );
 313      }
 314  
 315      $stripped_email       = str_replace( '@', '-at-', $email_address );
 316      $stripped_email       = sanitize_title( $stripped_email ); // slugify the email address
 317      $obscura              = wp_generate_password( 32, false, false );
 318      $file_basename        = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
 319      $html_report_filename = $file_basename . '.html';
 320      $html_report_pathname = wp_normalize_path( $exports_dir . $html_report_filename );
 321      $file                 = fopen( $html_report_pathname, 'w' );
 322      if ( false === $file ) {
 323          wp_send_json_error( __( 'Unable to open export file (HTML report) for writing.' ) );
 324      }
 325  
 326      $title = sprintf(
 327          /* translators: %s: User's email address. */
 328          __( 'Personal Data Export for %s' ),
 329          $email_address
 330      );
 331  
 332      // Open HTML.
 333      fwrite( $file, "<!DOCTYPE html>\n" );
 334      fwrite( $file, "<html>\n" );
 335  
 336      // Head.
 337      fwrite( $file, "<head>\n" );
 338      fwrite( $file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n" );
 339      fwrite( $file, "<style type='text/css'>" );
 340      fwrite( $file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }' );
 341      fwrite( $file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }' );
 342      fwrite( $file, 'th { padding: 5px; text-align: left; width: 20%; }' );
 343      fwrite( $file, 'td { padding: 5px; }' );
 344      fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' );
 345      fwrite( $file, '</style>' );
 346      fwrite( $file, '<title>' );
 347      fwrite( $file, esc_html( $title ) );
 348      fwrite( $file, '</title>' );
 349      fwrite( $file, "</head>\n" );
 350  
 351      // Body.
 352      fwrite( $file, "<body>\n" );
 353  
 354      // Heading.
 355      fwrite( $file, '<h1>' . esc_html__( 'Personal Data Export' ) . '</h1>' );
 356  
 357      // And now, all the Groups.
 358      $groups = get_post_meta( $request_id, '_export_data_grouped', true );
 359  
 360      // First, build an "About" group on the fly for this report.
 361      $about_group = array(
 362          /* translators: Header for the About section in a personal data export. */
 363          'group_label'       => _x( 'About', 'personal data group label' ),
 364          /* translators: Description for the About section in a personal data export. */
 365          'group_description' => _x( 'Overview of export report.', 'personal data group description' ),
 366          'items'             => array(
 367              'about-1' => array(
 368                  array(
 369                      'name'  => _x( 'Report generated for', 'email address' ),
 370                      'value' => $email_address,
 371                  ),
 372                  array(
 373                      'name'  => _x( 'For site', 'website name' ),
 374                      'value' => get_bloginfo( 'name' ),
 375                  ),
 376                  array(
 377                      'name'  => _x( 'At URL', 'website URL' ),
 378                      'value' => get_bloginfo( 'url' ),
 379                  ),
 380                  array(
 381                      'name'  => _x( 'On', 'date/time' ),
 382                      'value' => current_time( 'mysql' ),
 383                  ),
 384              ),
 385          ),
 386      );
 387  
 388      // Merge in the special about group.
 389      $groups = array_merge( array( 'about' => $about_group ), $groups );
 390  
 391      // Now, iterate over every group in $groups and have the formatter render it in HTML.
 392      foreach ( (array) $groups as $group_id => $group_data ) {
 393          fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data ) );
 394      }
 395  
 396      fwrite( $file, "</body>\n" );
 397  
 398      // Close HTML.
 399      fwrite( $file, "</html>\n" );
 400      fclose( $file );
 401  
 402      /*
 403       * Now, generate the ZIP.
 404       *
 405       * If an archive has already been generated, then remove it and reuse the
 406       * filename, to avoid breaking any URLs that may have been previously sent
 407       * via email.
 408       */
 409      $error            = false;
 410      $archive_url      = get_post_meta( $request_id, '_export_file_url', true );
 411      $archive_pathname = get_post_meta( $request_id, '_export_file_path', true );
 412  
 413      if ( empty( $archive_pathname ) || empty( $archive_url ) ) {
 414          $archive_filename = $file_basename . '.zip';
 415          $archive_pathname = $exports_dir . $archive_filename;
 416          $archive_url      = $exports_url . $archive_filename;
 417  
 418          update_post_meta( $request_id, '_export_file_url', $archive_url );
 419          update_post_meta( $request_id, '_export_file_path', wp_normalize_path( $archive_pathname ) );
 420      }
 421  
 422      if ( ! empty( $archive_pathname ) && file_exists( $archive_pathname ) ) {
 423          wp_delete_file( $archive_pathname );
 424      }
 425  
 426      $zip = new ZipArchive;
 427      if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) {
 428          if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) {
 429              $error = __( 'Unable to add data to export file.' );
 430          }
 431  
 432          $zip->close();
 433  
 434          if ( ! $error ) {
 435              /**
 436               * Fires right after all personal data has been written to the export file.
 437               *
 438               * @since 4.9.6
 439               *
 440               * @param string $archive_pathname     The full path to the export file on the filesystem.
 441               * @param string $archive_url          The URL of the archive file.
 442               * @param string $html_report_pathname The full path to the personal data report on the filesystem.
 443               * @param int    $request_id           The export request ID.
 444               */
 445              do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id );
 446          }
 447      } else {
 448          $error = __( 'Unable to open export file (archive) for writing.' );
 449      }
 450  
 451      // And remove the HTML file.
 452      unlink( $html_report_pathname );
 453  
 454      if ( $error ) {
 455          wp_send_json_error( $error );
 456      }
 457  }
 458  
 459  /**
 460   * Send an email to the user with a link to the personal data export file
 461   *
 462   * @since 4.9.6
 463   *
 464   * @param int $request_id The request ID for this personal data export.
 465   * @return true|WP_Error True on success or `WP_Error` on failure.
 466   */
 467  function wp_privacy_send_personal_data_export_email( $request_id ) {
 468      // Get the request data.
 469      $request = wp_get_user_request_data( $request_id );
 470  
 471      if ( ! $request || 'export_personal_data' !== $request->action_name ) {
 472          return new WP_Error( 'invalid_request', __( 'Invalid request ID when sending personal data export email.' ) );
 473      }
 474  
 475      // Localize message content for user; fallback to site default for visitors.
 476      if ( ! empty( $request->user_id ) ) {
 477          $locale = get_user_locale( $request->user_id );
 478      } else {
 479          $locale = get_locale();
 480      }
 481  
 482      $switched_locale = switch_to_locale( $locale );
 483  
 484      /** This filter is documented in wp-includes/functions.php */
 485      $expiration      = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS );
 486      $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration );
 487  
 488      /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */
 489      $email_text = __(
 490          'Howdy,
 491  
 492  Your request for an export of personal data has been completed. You may
 493  download your personal data by clicking on the link below. For privacy
 494  and security, we will automatically delete the file on ###EXPIRATION###,
 495  so please download it before then.
 496  
 497  ###LINK###
 498  
 499  Regards,
 500  All at ###SITENAME###
 501  ###SITEURL###'
 502      );
 503  
 504      /**
 505       * Filters the text of the email sent with a personal data export file.
 506       *
 507       * The following strings have a special meaning and will get replaced dynamically:
 508       * ###EXPIRATION###         The date when the URL will be automatically deleted.
 509       * ###LINK###               URL of the personal data export file for the user.
 510       * ###SITENAME###           The name of the site.
 511       * ###SITEURL###            The URL to the site.
 512       *
 513       * @since 4.9.6
 514       *
 515       * @param string $email_text     Text in the email.
 516       * @param int    $request_id     The request ID for this personal data export.
 517       */
 518      $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id );
 519  
 520      $email_address   = $request->email;
 521      $export_file_url = get_post_meta( $request_id, '_export_file_url', true );
 522      $site_name       = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
 523      $site_url        = home_url();
 524  
 525      $content = str_replace( '###EXPIRATION###', $expiration_date, $content );
 526      $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content );
 527      $content = str_replace( '###EMAIL###', $email_address, $content );
 528      $content = str_replace( '###SITENAME###', $site_name, $content );
 529      $content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content );
 530  
 531      $mail_success = wp_mail(
 532          $email_address,
 533          sprintf(
 534              /* translators: Personal data export notification email subject. %s: Site title. */
 535              __( '[%s] Personal Data Export' ),
 536              $site_name
 537          ),
 538          $content
 539      );
 540  
 541      if ( $switched_locale ) {
 542          restore_previous_locale();
 543      }
 544  
 545      if ( ! $mail_success ) {
 546          return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export email.' ) );
 547      }
 548  
 549      return true;
 550  }
 551  
 552  /**
 553   * Intercept personal data exporter page Ajax responses in order to assemble the personal data export file.
 554   * @see wp_privacy_personal_data_export_page
 555   * @since 4.9.6
 556   *
 557   * @param array  $response        The response from the personal data exporter for the given page.
 558   * @param int    $exporter_index  The index of the personal data exporter. Begins at 1.
 559   * @param string $email_address   The email address of the user whose personal data this is.
 560   * @param int    $page            The page of personal data for this exporter. Begins at 1.
 561   * @param int    $request_id      The request ID for this personal data export.
 562   * @param bool   $send_as_email   Whether the final results of the export should be emailed to the user.
 563   * @param string $exporter_key    The slug (key) of the exporter.
 564   * @return array The filtered response.
 565   */
 566  function wp_privacy_process_personal_data_export_page( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) {
 567      /* Do some simple checks on the shape of the response from the exporter.
 568       * If the exporter response is malformed, don't attempt to consume it - let it
 569       * pass through to generate a warning to the user by default Ajax processing.
 570       */
 571      if ( ! is_array( $response ) ) {
 572          return $response;
 573      }
 574  
 575      if ( ! array_key_exists( 'done', $response ) ) {
 576          return $response;
 577      }
 578  
 579      if ( ! array_key_exists( 'data', $response ) ) {
 580          return $response;
 581      }
 582  
 583      if ( ! is_array( $response['data'] ) ) {
 584          return $response;
 585      }
 586  
 587      // Get the request data.
 588      $request = wp_get_user_request_data( $request_id );
 589  
 590      if ( ! $request || 'export_personal_data' !== $request->action_name ) {
 591          wp_send_json_error( __( 'Invalid request ID when merging exporter data.' ) );
 592      }
 593  
 594      $export_data = array();
 595  
 596      // First exporter, first page? Reset the report data accumulation array.
 597      if ( 1 === $exporter_index && 1 === $page ) {
 598          update_post_meta( $request_id, '_export_data_raw', $export_data );
 599      } else {
 600          $export_data = get_post_meta( $request_id, '_export_data_raw', true );
 601      }
 602  
 603      // Now, merge the data from the exporter response into the data we have accumulated already.
 604      $export_data = array_merge( $export_data, $response['data'] );
 605      update_post_meta( $request_id, '_export_data_raw', $export_data );
 606  
 607      // If we are not yet on the last page of the last exporter, return now.
 608      /** This filter is documented in wp-admin/includes/ajax-actions.php */
 609      $exporters        = apply_filters( 'wp_privacy_personal_data_exporters', array() );
 610      $is_last_exporter = $exporter_index === count( $exporters );
 611      $exporter_done    = $response['done'];
 612      if ( ! $is_last_exporter || ! $exporter_done ) {
 613          return $response;
 614      }
 615  
 616      // Last exporter, last page - let's prepare the export file.
 617  
 618      // First we need to re-organize the raw data hierarchically in groups and items.
 619      $groups = array();
 620      foreach ( (array) $export_data as $export_datum ) {
 621          $group_id    = $export_datum['group_id'];
 622          $group_label = $export_datum['group_label'];
 623  
 624          $group_description = '';
 625          if ( ! empty( $export_datum['group_description'] ) ) {
 626              $group_description = $export_datum['group_description'];
 627          }
 628  
 629          if ( ! array_key_exists( $group_id, $groups ) ) {
 630              $groups[ $group_id ] = array(
 631                  'group_label'       => $group_label,
 632                  'group_description' => $group_description,
 633                  'items'             => array(),
 634              );
 635          }
 636  
 637          $item_id = $export_datum['item_id'];
 638          if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) {
 639              $groups[ $group_id ]['items'][ $item_id ] = array();
 640          }
 641  
 642          $old_item_data                            = $groups[ $group_id ]['items'][ $item_id ];
 643          $merged_item_data                         = array_merge( $export_datum['data'], $old_item_data );
 644          $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data;
 645      }
 646  
 647      // Then save the grouped data into the request.
 648      delete_post_meta( $request_id, '_export_data_raw' );
 649      update_post_meta( $request_id, '_export_data_grouped', $groups );
 650  
 651      /**
 652       * Generate the export file from the collected, grouped personal data.
 653       *
 654       * @since 4.9.6
 655       *
 656       * @param int $request_id The export request ID.
 657       */
 658      do_action( 'wp_privacy_personal_data_export_file', $request_id );
 659  
 660      // Clear the grouped data now that it is no longer needed.
 661      delete_post_meta( $request_id, '_export_data_grouped' );
 662  
 663      // If the destination is email, send it now.
 664      if ( $send_as_email ) {
 665          $mail_success = wp_privacy_send_personal_data_export_email( $request_id );
 666          if ( is_wp_error( $mail_success ) ) {
 667              wp_send_json_error( $mail_success->get_error_message() );
 668          }
 669  
 670          // Update the request to completed state when the export email is sent.
 671          _wp_privacy_completed_request( $request_id );
 672      } else {
 673          // Modify the response to include the URL of the export file so the browser can fetch it.
 674          $export_file_url = get_post_meta( $request_id, '_export_file_url', true );
 675          if ( ! empty( $export_file_url ) ) {
 676              $response['url'] = $export_file_url;
 677          }
 678      }
 679  
 680      return $response;
 681  }
 682  
 683  /**
 684   * Mark erasure requests as completed after processing is finished.
 685   *
 686   * This intercepts the Ajax responses to personal data eraser page requests, and
 687   * monitors the status of a request. Once all of the processing has finished, the
 688   * request is marked as completed.
 689   *
 690   * @since 4.9.6
 691   *
 692   * @see wp_privacy_personal_data_erasure_page
 693   *
 694   * @param array  $response      The response from the personal data eraser for
 695   *                              the given page.
 696   * @param int    $eraser_index  The index of the personal data eraser. Begins
 697   *                              at 1.
 698   * @param string $email_address The email address of the user whose personal
 699   *                              data this is.
 700   * @param int    $page          The page of personal data for this eraser.
 701   *                              Begins at 1.
 702   * @param int    $request_id    The request ID for this personal data erasure.
 703   * @return array The filtered response.
 704   */
 705  function wp_privacy_process_personal_data_erasure_page( $response, $eraser_index, $email_address, $page, $request_id ) {
 706      /*
 707       * If the eraser response is malformed, don't attempt to consume it; let it
 708       * pass through, so that the default Ajax processing will generate a warning
 709       * to the user.
 710       */
 711      if ( ! is_array( $response ) ) {
 712          return $response;
 713      }
 714  
 715      if ( ! array_key_exists( 'done', $response ) ) {
 716          return $response;
 717      }
 718  
 719      if ( ! array_key_exists( 'items_removed', $response ) ) {
 720          return $response;
 721      }
 722  
 723      if ( ! array_key_exists( 'items_retained', $response ) ) {
 724          return $response;
 725      }
 726  
 727      if ( ! array_key_exists( 'messages', $response ) ) {
 728          return $response;
 729      }
 730  
 731      $request = wp_get_user_request_data( $request_id );
 732  
 733      if ( ! $request || 'remove_personal_data' !== $request->action_name ) {
 734          wp_send_json_error( __( 'Invalid request ID when processing eraser data.' ) );
 735      }
 736  
 737      /** This filter is documented in wp-admin/includes/ajax-actions.php */
 738      $erasers        = apply_filters( 'wp_privacy_personal_data_erasers', array() );
 739      $is_last_eraser = count( $erasers ) === $eraser_index;
 740      $eraser_done    = $response['done'];
 741  
 742      if ( ! $is_last_eraser || ! $eraser_done ) {
 743          return $response;
 744      }
 745  
 746      _wp_privacy_completed_request( $request_id );
 747  
 748      /**
 749       * Fires immediately after a personal data erasure request has been marked completed.
 750       *
 751       * @since 4.9.6
 752       *
 753       * @param int $request_id The privacy request post ID associated with this request.
 754       */
 755      do_action( 'wp_privacy_personal_data_erased', $request_id );
 756  
 757      return $response;
 758  }


Generated: Thu Sep 19 01:00:03 2019 Cross-referenced by PHPXref 0.7.1