wp_privacy_generate_personal_data_export_file

The timeline below displays how wordpress function wp_privacy_generate_personal_data_export_file has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 5.8

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate personal data export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating personal data export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating personal data export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create personal data export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.php';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect personal data export folder from browsing.'));
        }
        fwrite($file, "<?php\n// Silence is golden.\n");
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    if (is_array($groups)) {
        // Merge in the special "About" group.
        $groups = array_merge(array('about' => $about_group), $groups);
        $groups_count = count($groups);
    } else {
        if (false !== $groups) {
            _doing_it_wrong(
                __FUNCTION__,
                /* translators: %s: Post meta key. */
                sprintf(__('The %s post meta must be an array.'), '<code>_export_data_grouped</code>'),
                '5.8.0'
            );
        }
        $groups = null;
        $groups_count = 0;
    }
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    if (false === $groups_json) {
        $error_message = sprintf(
            /* translators: %s: Error message. */
            __('Unable to encode the personal data for export. Error: %s'),
            json_last_error_msg()
        );
        wp_send_json_error($error_message);
    }
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open personal data export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open personal data export (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return-to-top { text-align: right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if ($groups_count > 1) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the filename,
     * to avoid breaking any URLs that may have been previously sent via email.
     */
    $error = false;
    // This meta value is used from version 5.5.
    $archive_filename = get_post_meta($request_id, '_export_file_name', true);
    // This one stored an absolute path and is used for backward compatibility.
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    // If a filename meta exists, use it.
    if (!empty($archive_filename)) {
        $archive_pathname = $exports_dir . $archive_filename;
    } elseif (!empty($archive_pathname)) {
        // If a full path meta exists, use it and create the new meta value.
        $archive_filename = basename($archive_pathname);
        update_post_meta($request_id, '_export_file_name', $archive_filename);
        // Remove the back-compat meta values.
        delete_post_meta($request_id, '_export_file_url');
        delete_post_meta($request_id, '_export_file_path');
    } else {
        // If there's no filename or full path stored, create a new file.
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        update_post_meta($request_id, '_export_file_name', $archive_filename);
    }
    $archive_url = $exports_url . $archive_filename;
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to archive the personal data export file (JSON format).');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to archive the personal data export file (HTML format).');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open personal data export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.7

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate personal data export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating personal data export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating personal data export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create personal data export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.php';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect personal data export folder from browsing.'));
        }
        fwrite($file, "<?php\n// Silence is golden.\n");
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    $groups_count = count($groups);
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open personal data export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open personal data export (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return-to-top { text-align: right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if ($groups_count > 1) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the filename,
     * to avoid breaking any URLs that may have been previously sent via email.
     */
    $error = false;
    // This meta value is used from version 5.5.
    $archive_filename = get_post_meta($request_id, '_export_file_name', true);
    // This one stored an absolute path and is used for backward compatibility.
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    // If a filename meta exists, use it.
    if (!empty($archive_filename)) {
        $archive_pathname = $exports_dir . $archive_filename;
    } elseif (!empty($archive_pathname)) {
        // If a full path meta exists, use it and create the new meta value.
        $archive_filename = basename($archive_pathname);
        update_post_meta($request_id, '_export_file_name', $archive_filename);
        // Remove the back-compat meta values.
        delete_post_meta($request_id, '_export_file_url');
        delete_post_meta($request_id, '_export_file_path');
    } else {
        // If there's no filename or full path stored, create a new file.
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        update_post_meta($request_id, '_export_file_name', $archive_filename);
    }
    $archive_url = $exports_url . $archive_filename;
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to archive the personal data export file (JSON format).');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to archive the personal data export file (HTML format).');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open personal data export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 6.1

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate user privacy export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating user privacy export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating user privacy export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create user privacy export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.php';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect user privacy export folder from browsing.'));
        }
        fwrite($file, "<?php\n// Silence is golden.\n");
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    $groups_count = count($groups);
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open user privacy export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open user privacy export file (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return-to-top { text-align: right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if ($groups_count > 1) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the filename,
     * to avoid breaking any URLs that may have been previously sent via email.
     */
    $error = false;
    // This meta value is used from version 5.5.
    $archive_filename = get_post_meta($request_id, '_export_file_name', true);
    // This one stored an absolute path and is used for backward compatibility.
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    // If a filename meta exists, use it.
    if (!empty($archive_filename)) {
        $archive_pathname = $exports_dir . $archive_filename;
    } elseif (!empty($archive_pathname)) {
        // If a full path meta exists, use it and create the new meta value.
        $archive_filename = basename($archive_pathname);
        update_post_meta($request_id, '_export_file_name', $archive_filename);
        // Remove the back-compat meta values.
        delete_post_meta($request_id, '_export_file_url');
        delete_post_meta($request_id, '_export_file_path');
    } else {
        // If there's no filename or full path stored, create a new file.
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        update_post_meta($request_id, '_export_file_name', $archive_filename);
    }
    $archive_url = $exports_url . $archive_filename;
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to add data to user privacy export file (JSON format).');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to user privacy export file (HTML format).');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open user privacy export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.6

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate user privacy export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating user privacy export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating user privacy export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create user privacy export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect user privacy export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    $groups_count = count($groups);
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open user privacy export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open user privacy export file (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return-to-top { text-align: right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if ($groups_count > 1) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the filename,
     * to avoid breaking any URLs that may have been previously sent via email.
     */
    $error = false;
    // This meta value is used from version 5.5.
    $archive_filename = get_post_meta($request_id, '_export_file_name', true);
    // This one stored an absolute path and is used for backward compatibility.
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    // If a filename meta exists, use it.
    if (!empty($archive_filename)) {
        $archive_pathname = $exports_dir . $archive_filename;
    } elseif (!empty($archive_pathname)) {
        // If a full path meta exists, use it and create the new meta value.
        $archive_filename = basename($archive_pathname);
        update_post_meta($request_id, '_export_file_name', $archive_filename);
        // Remove the back-compat meta values.
        delete_post_meta($request_id, '_export_file_url');
        delete_post_meta($request_id, '_export_file_path');
    } else {
        // If there's no filename or full path stored, create a new file.
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        update_post_meta($request_id, '_export_file_name', $archive_filename);
    }
    $archive_url = $exports_url . $archive_filename;
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to add data to user privacy export file (JSON format).');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to user privacy export file (HTML format).');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open user privacy export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.5

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    $groups_count = count($groups);
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return-to-top { text-align: right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if ($groups_count > 1) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the filename,
     * to avoid breaking any URLs that may have been previously sent via email.
     */
    $error = false;
    // This meta value is used from version 5.5.
    $archive_filename = get_post_meta($request_id, '_export_file_name', true);
    // This one stored an absolute path and is used for backward compatibility.
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    // If a filename meta exists, use it.
    if (!empty($archive_filename)) {
        $archive_pathname = $exports_dir . $archive_filename;
    } elseif (!empty($archive_pathname)) {
        // If a full path meta exists, use it and create the new meta value.
        $archive_filename = basename($archive_pathname);
        update_post_meta($request_id, '_export_file_name', $archive_filename);
        // Remove the back-compat meta values.
        delete_post_meta($request_id, '_export_file_url');
        delete_post_meta($request_id, '_export_file_path');
    } else {
        // If there's no filename or full path stored, create a new file.
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        update_post_meta($request_id, '_export_file_name', $archive_filename);
    }
    $archive_url = $exports_url . $archive_filename;
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to add data to JSON file.');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to HTML file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.4

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request.
    $request = wp_get_user_request($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $obscura;
    $html_report_filename = wp_unique_filename($exports_dir, $file_basename . '.html');
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $json_report_filename = $file_basename . '.json';
    $json_report_pathname = wp_normalize_path($exports_dir . $json_report_filename);
    /*
     * Gather general data needed.
     */
    // Title.
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    $groups_count = count($groups);
    // Convert the groups to JSON format.
    $groups_json = wp_json_encode($groups);
    /*
     * Handle the JSON export.
     */
    $file = fopen($json_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (JSON report) for writing.'));
    }
    fwrite($file, '{');
    fwrite($file, '"' . $title . '":');
    fwrite($file, $groups_json);
    fwrite($file, '}');
    fclose($file);
    /*
     * Handle the HTML export.
     */
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '.return_to_top { text-align:right; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    fwrite($file, "<body>\n");
    fwrite($file, '<h1 id="top">' . esc_html__('Personal Data Export') . '</h1>');
    // Create TOC.
    if (1 < $groups_count) {
        fwrite($file, '<div id="table_of_contents">');
        fwrite($file, '<h2>' . esc_html__('Table of Contents') . '</h2>');
        fwrite($file, '<ul>');
        foreach ((array) $groups as $group_id => $group_data) {
            $group_label = esc_html($group_data['group_label']);
            $group_id_attr = sanitize_title_with_dashes($group_data['group_label'] . '-' . $group_id);
            $group_items_count = count((array) $group_data['items']);
            if ($group_items_count > 1) {
                $group_label .= sprintf(' <span class="count">(%d)</span>', $group_items_count);
            }
            fwrite($file, '<li>');
            fwrite($file, '<a href="#' . esc_attr($group_id_attr) . '">' . $group_label . '</a>');
            fwrite($file, '</li>');
        }
        fwrite($file, '</ul>');
        fwrite($file, '</div>');
    }
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data, $group_id, $groups_count));
    }
    fwrite($file, "</body>\n");
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($json_report_pathname, 'export.json')) {
            $error = __('Unable to add data to JSON file.');
        }
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to HTML file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             * @since 5.4.0 Added the `$json_report_pathname` parameter.
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // Remove the JSON file.
    unlink($json_report_pathname);
    // Remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.3

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: User's email address. */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, '<h1>' . esc_html__('Personal Data Export') . '</h1>');
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        /* translators: Description for the About section in a personal data export. */
        'group_description' => _x('Overview of export report.', 'personal data group description'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.2

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's email address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, '<h1>' . esc_html__('Personal Data Export') . '</h1>');
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 5.1

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's e-mail address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }');
    fwrite($file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }');
    fwrite($file, 'th { padding: 5px; text-align: left; width: 20%; }');
    fwrite($file, 'td { padding: 5px; }');
    fwrite($file, 'tr:nth-child(odd) { background-color: #fafafa; }');
    fwrite($file, '</style>');
    fwrite($file, '<title>');
    fwrite($file, esc_html($title));
    fwrite($file, '</title>');
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, '<h1>' . esc_html__('Personal Data Export') . '</h1>');
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 9.8

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's e-mail address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, "body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }");
    fwrite($file, "table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }");
    fwrite($file, "th { padding: 5px; text-align: left; width: 20%; }");
    fwrite($file, "td { padding: 5px; }");
    fwrite($file, "tr:nth-child(odd) { background-color: #fafafa; }");
    fwrite($file, "</style>");
    fwrite($file, "<title>");
    fwrite($file, esc_html($title));
    fwrite($file, "</title>");
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, "<h1>" . esc_html__('Personal Data Export') . "</h1>");
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 9.7

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, 'Silence is golden.');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's e-mail address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, "body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }");
    fwrite($file, "table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }");
    fwrite($file, "th { padding: 5px; text-align: left; width: 20%; }");
    fwrite($file, "td { padding: 5px; }");
    fwrite($file, "tr:nth-child(odd) { background-color: #fafafa; }");
    fwrite($file, "</style>");
    fwrite($file, "<title>");
    fwrite($file, esc_html($title));
    fwrite($file, "</title>");
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, "<h1>" . esc_html__('Personal Data Export') . "</h1>");
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array('group_label' => __('About'), 'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))));
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: 9.6

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int  $request_id  The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    $result = wp_mkdir_p($exports_dir);
    if (is_wp_error($result)) {
        wp_send_json_error($result->get_error_message());
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, 'Silence is golden.');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's e-mail address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, "body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }");
    fwrite($file, "table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }");
    fwrite($file, "th { padding: 5px; text-align: left; width: 20%; }");
    fwrite($file, "td { padding: 5px; }");
    fwrite($file, "tr:nth-child(odd) { background-color: #fafafa; }");
    fwrite($file, "</style>");
    fwrite($file, "<title>");
    fwrite($file, esc_html($title));
    fwrite($file, "</title>");
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, "<h1>" . esc_html__('Personal Data Export') . "</h1>");
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array('group_label' => __('About'), 'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))));
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param string $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}

WordPress Version: .10

/**
 * Generate the personal data export file.
 *
 * @since 4.9.6
 *
 * @param int $request_id The export request ID.
 */
function wp_privacy_generate_personal_data_export_file($request_id)
{
    if (!class_exists('ZipArchive')) {
        wp_send_json_error(__('Unable to generate export file. ZipArchive not available.'));
    }
    // Get the request data.
    $request = wp_get_user_request_data($request_id);
    if (!$request || 'export_personal_data' !== $request->action_name) {
        wp_send_json_error(__('Invalid request ID when generating export file.'));
    }
    $email_address = $request->email;
    if (!is_email($email_address)) {
        wp_send_json_error(__('Invalid email address when generating export file.'));
    }
    // Create the exports folder if needed.
    $exports_dir = wp_privacy_exports_dir();
    $exports_url = wp_privacy_exports_url();
    if (!wp_mkdir_p($exports_dir)) {
        wp_send_json_error(__('Unable to create export folder.'));
    }
    // Protect export folder from browsing.
    $index_pathname = $exports_dir . 'index.html';
    if (!file_exists($index_pathname)) {
        $file = fopen($index_pathname, 'w');
        if (false === $file) {
            wp_send_json_error(__('Unable to protect export folder from browsing.'));
        }
        fwrite($file, '<!-- Silence is golden. -->');
        fclose($file);
    }
    $stripped_email = str_replace('@', '-at-', $email_address);
    $stripped_email = sanitize_title($stripped_email);
    // slugify the email address
    $obscura = wp_generate_password(32, false, false);
    $file_basename = 'wp-personal-data-file-' . $stripped_email . '-' . $obscura;
    $html_report_filename = $file_basename . '.html';
    $html_report_pathname = wp_normalize_path($exports_dir . $html_report_filename);
    $file = fopen($html_report_pathname, 'w');
    if (false === $file) {
        wp_send_json_error(__('Unable to open export file (HTML report) for writing.'));
    }
    $title = sprintf(
        /* translators: %s: user's e-mail address */
        __('Personal Data Export for %s'),
        $email_address
    );
    // Open HTML.
    fwrite($file, "<!DOCTYPE html>\n");
    fwrite($file, "<html>\n");
    // Head.
    fwrite($file, "<head>\n");
    fwrite($file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n");
    fwrite($file, "<style type='text/css'>");
    fwrite($file, "body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }");
    fwrite($file, "table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }");
    fwrite($file, "th { padding: 5px; text-align: left; width: 20%; }");
    fwrite($file, "td { padding: 5px; }");
    fwrite($file, "tr:nth-child(odd) { background-color: #fafafa; }");
    fwrite($file, "</style>");
    fwrite($file, "<title>");
    fwrite($file, esc_html($title));
    fwrite($file, "</title>");
    fwrite($file, "</head>\n");
    // Body.
    fwrite($file, "<body>\n");
    // Heading.
    fwrite($file, "<h1>" . esc_html__('Personal Data Export') . "</h1>");
    // And now, all the Groups.
    $groups = get_post_meta($request_id, '_export_data_grouped', true);
    // First, build an "About" group on the fly for this report.
    $about_group = array(
        /* translators: Header for the About section in a personal data export. */
        'group_label' => _x('About', 'personal data group label'),
        'items' => array('about-1' => array(array('name' => _x('Report generated for', 'email address'), 'value' => $email_address), array('name' => _x('For site', 'website name'), 'value' => get_bloginfo('name')), array('name' => _x('At URL', 'website URL'), 'value' => get_bloginfo('url')), array('name' => _x('On', 'date/time'), 'value' => current_time('mysql')))),
    );
    // Merge in the special about group.
    $groups = array_merge(array('about' => $about_group), $groups);
    // Now, iterate over every group in $groups and have the formatter render it in HTML.
    foreach ((array) $groups as $group_id => $group_data) {
        fwrite($file, wp_privacy_generate_personal_data_export_group_html($group_data));
    }
    fwrite($file, "</body>\n");
    // Close HTML.
    fwrite($file, "</html>\n");
    fclose($file);
    /*
     * Now, generate the ZIP.
     *
     * If an archive has already been generated, then remove it and reuse the
     * filename, to avoid breaking any URLs that may have been previously sent
     * via email.
     */
    $error = false;
    $archive_url = get_post_meta($request_id, '_export_file_url', true);
    $archive_pathname = get_post_meta($request_id, '_export_file_path', true);
    if (empty($archive_pathname) || empty($archive_url)) {
        $archive_filename = $file_basename . '.zip';
        $archive_pathname = $exports_dir . $archive_filename;
        $archive_url = $exports_url . $archive_filename;
        update_post_meta($request_id, '_export_file_url', $archive_url);
        update_post_meta($request_id, '_export_file_path', wp_normalize_path($archive_pathname));
    }
    if (!empty($archive_pathname) && file_exists($archive_pathname)) {
        wp_delete_file($archive_pathname);
    }
    $zip = new ZipArchive();
    if (true === $zip->open($archive_pathname, ZipArchive::CREATE)) {
        if (!$zip->addFile($html_report_pathname, 'index.html')) {
            $error = __('Unable to add data to export file.');
        }
        $zip->close();
        if (!$error) {
            /**
             * Fires right after all personal data has been written to the export file.
             *
             * @since 4.9.6
             *
             * @param string $archive_pathname     The full path to the export file on the filesystem.
             * @param string $archive_url          The URL of the archive file.
             * @param string $html_report_pathname The full path to the personal data report on the filesystem.
             * @param int    $request_id           The export request ID.
             */
            do_action('wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id);
        }
    } else {
        $error = __('Unable to open export file (archive) for writing.');
    }
    // And remove the HTML file.
    unlink($html_report_pathname);
    if ($error) {
        wp_send_json_error($error);
    }
}