wp_privacy_generate_personal_data_export_file()
云策文档标注
概述
wp_privacy_generate_personal_data_export_file() 是 WordPress 中用于生成个人数据导出文件的函数,它处理导出请求,创建 JSON 和 HTML 格式的报告,并打包为 ZIP 文件。该函数包含参数验证、文件生成和错误处理机制。
关键要点
- 函数参数:接受一个必需的整数参数 $request_id,表示导出请求的 ID。
- 依赖检查:需要 ZipArchive 类支持,否则返回 JSON 错误。
- 请求验证:通过 wp_get_user_request() 验证请求 ID 和操作类型是否为 'export_personal_data'。
- 邮箱验证:使用 is_email() 检查请求中的邮箱地址是否有效。
- 导出目录管理:使用 wp_privacy_exports_dir() 和 wp_privacy_exports_url() 创建和保护导出文件夹。
- 数据分组处理:从 post meta 获取分组数据,并合并一个特殊的“关于”组,然后转换为 JSON 格式。
- 文件生成:分别创建 JSON 和 HTML 报告文件,并写入格式化数据。
- ZIP 打包:使用 ZipArchive 将报告文件打包,处理文件名和路径的元数据更新。
- 钩子触发:在文件创建后触发 wp_privacy_personal_data_export_file_created 动作钩子。
- 错误处理:在关键步骤中,如文件操作失败,使用 wp_send_json_error() 返回错误信息。
代码示例
// 示例调用函数生成个人数据导出文件
$request_id = 123; // 假设的导出请求 ID
wp_privacy_generate_personal_data_export_file( $request_id );注意事项
- 确保服务器环境支持 ZipArchive 扩展,否则函数会失败。
- 函数内部使用 wp_send_json_error() 处理错误,适合在 Ajax 上下文中调用。
- 导出文件存储在 wp_privacy_exports_dir() 指定的目录中,需确保目录可写。
- 函数会清理临时生成的 JSON 和 HTML 文件,只保留最终的 ZIP 文件。
- 自 WordPress 4.9.6 版本引入,兼容性需考虑。
原文内容
Generate the personal data export file.
Parameters
$request_idintrequired-
The export request ID.
Source
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, " _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, "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 );
}
}
Hooks
- do_action( ‘wp_privacy_personal_data_export_file_created’, string $archive_pathname, string $archive_url, string $html_report_pathname, int $request_id, string $json_report_pathname )
-
Fires right after all personal data has been written to the export file.
Changelog
| Version | Description |
|---|---|
| 4.9.6 | Introduced. |