diff --git a/wp-admin/includes/file.php b/wp-admin/includes/file.php index 21a44a6135..7fae227234 100644 --- a/wp-admin/includes/file.php +++ b/wp-admin/includes/file.php @@ -1999,9 +1999,6 @@ function wp_privacy_generate_personal_data_export_group_html( $group_data ) { * @param int $request_id The export request ID. */ function wp_privacy_generate_personal_data_export_file( $request_id ) { - // Maybe make this a cron job instead. - wp_privacy_delete_old_export_files(); - if ( ! class_exists( 'ZipArchive' ) ) { wp_send_json_error( __( 'Unable to generate export file. ZipArchive not available.' ) ); } @@ -2162,13 +2159,18 @@ function wp_privacy_send_personal_data_export_email( $request_id ) { return new WP_Error( 'invalid', __( 'Invalid request ID when sending personal data export email.' ) ); } -/* translators: Do not translate LINK, EMAIL, SITENAME, SITEURL: those are placeholders. */ + /** This filter is documented in wp-admin/includes/file.php */ + $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); + $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); + +/* translators: Do not translate EXPIRATION, LINK, EMAIL, SITENAME, SITEURL: those are placeholders. */ $email_text = __( 'Howdy, Your request for an export of personal data has been completed. You may -download your personal data by clicking on the link below. This link is -good for the next 3 days. +download your personal data by clicking on the link below. For privacy +and security, we will automatically delete the file on ###EXPIRATION###, +so please download it before then. ###LINK### @@ -2183,6 +2185,7 @@ All at ###SITENAME### * Filters the text of the email sent with a personal data export file. * * The following strings have a special meaning and will get replaced dynamically: + * ###EXPIRATION### The date when the URL will be automatically deleted. * ###LINK### URL of the personal data export file for the user. * ###EMAIL### The email we are sending to. * ###SITENAME### The name of the site. @@ -2200,6 +2203,7 @@ All at ###SITENAME### $site_name = is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' ); $site_url = network_home_url(); + $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content ); $content = str_replace( '###EMAIL###', $email_address, $content ); $content = str_replace( '###SITENAME###', wp_specialchars_decode( $site_name, ENT_QUOTES ), $content ); @@ -2344,22 +2348,3 @@ function wp_privacy_process_personal_data_export_page( $response, $exporter_inde return $response; } - -/** - * Cleans up export files older than three days old. - * - * @since 4.9.6 - */ -function wp_privacy_delete_old_export_files() { - $upload_dir = wp_upload_dir(); - $exports_dir = trailingslashit( $upload_dir['basedir'] . '/exports' ); - $export_files = list_files( $exports_dir ); - - foreach( (array) $export_files as $export_file ) { - $file_age_in_seconds = time() - filemtime( $export_file ); - - if ( 3 * DAY_IN_SECONDS < $file_age_in_seconds ) { - @unlink( $export_file ); - } - } -} diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php index dcdebacb1d..a3c68eed86 100644 --- a/wp-includes/default-filters.php +++ b/wp-includes/default-filters.php @@ -352,6 +352,8 @@ add_action( 'user_request_action_confirmed', '_wp_privacy_account_request_confir add_filter( 'user_request_action_confirmed_message', '_wp_privacy_account_request_confirmed_message', 10, 2 ); add_filter( 'wp_privacy_personal_data_exporters', 'wp_register_comment_personal_data_exporter' ); add_filter( 'wp_privacy_personal_data_erasers', 'wp_register_comment_personal_data_eraser' ); +add_action( 'init', 'wp_schedule_delete_old_privacy_export_files' ); +add_action( 'wp_privacy_delete_old_export_files', 'wp_privacy_delete_old_export_files' ); // Cron tasks add_action( 'wp_scheduled_delete', 'wp_scheduled_delete' ); diff --git a/wp-includes/functions.php b/wp-includes/functions.php index de978b3b5d..647e0db2d0 100644 --- a/wp-includes/functions.php +++ b/wp-includes/functions.php @@ -6257,3 +6257,51 @@ function wp_privacy_anonymize_data( $type, $data = '' ) { function _wp_privacy_active_plugins_change() { update_option( '_wp_privacy_text_change_check', 'check' ); } + +/** + * Schedule a `WP_Cron` job to delete expired export files. + * + * @since 4.9.6 + */ +function wp_schedule_delete_old_privacy_export_files() { + if ( ! wp_next_scheduled( 'wp_privacy_delete_old_export_files' ) ) { + wp_schedule_event( time(), 'hourly', 'wp_privacy_delete_old_export_files' ); + } +} + +/** + * Cleans up export files older than three days old. + * + * The export files are stored in `wp-content/uploads`, and are therefore publicly + * accessible. A CSPRN is appended to the filename to mitigate the risk of an + * unauthorized person downloading the file, but it is still possible. Deleting + * the file after the data subject has had a chance to delete it adds an additional + * layer of protection. + * + * @since 4.9.6 + */ +function wp_privacy_delete_old_export_files() { + $upload_dir = wp_upload_dir(); + $exports_dir = trailingslashit( $upload_dir['basedir'] . '/exports' ); + $export_files = list_files( $exports_dir, 100, array( 'index.html' ) ); + + /** + * Filters the lifetime, in seconds, of a personal data export file. + * + * By default, the lifetime is 3 days. Once the file reaches that age, it will automatically + * be deleted by a cron job. + * + * @since 4.9.6 + * + * @param int $expiration The expiration age of the export, in seconds. + */ + $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); + + foreach ( (array) $export_files as $export_file ) { + $file_age_in_seconds = time() - filemtime( $export_file ); + + if ( $expiration < $file_age_in_seconds ) { + unlink( $export_file ); + } + } +} diff --git a/wp-includes/version.php b/wp-includes/version.php index 21d4941ee2..052178f72f 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '5.0-alpha-43045'; +$wp_version = '5.0-alpha-43046'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.