Index: src/wp-admin/includes/file.php
===================================================================
--- src/wp-admin/includes/file.php	(revision 43434)
+++ src/wp-admin/includes/file.php	(working copy)
@@ -2183,14 +2183,19 @@
 }
 
 /**
- * Send an email to the user with a link to the personal data export file
+ * Send an email to the user with a link to the personal data export file.
  *
+ * The link to the export file is only included in the email content when there
+ * is personal data to export.
+ *
  * @since 4.9.6
+ * @since 4.9.8 Added the $has_export_data parameter.
  *
- * @param int $request_id The request ID for this personal data export.
+ * @param int  $request_id      The request ID for this personal data export.
+ * @param bool $has_export_data Whether personal data exists to export.
  * @return true|WP_Error True on success or `WP_Error` on failure.
  */
-function wp_privacy_send_personal_data_export_email( $request_id ) {
+function wp_privacy_send_personal_data_export_email( $request_id, $has_export_data = true ) {
 	// Get the request data.
 	$request = wp_get_user_request_data( $request_id );
 
@@ -2202,9 +2207,9 @@
 	$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, SITENAME, SITEURL: those are placeholders. */
-$email_text = __(
-'Howdy,
+	if ( $has_export_data ) {
+		/* translators: Do not translate EXPIRATION, LINK, 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. For privacy
@@ -2215,33 +2220,61 @@
 
 Regards,
 All at ###SITENAME###
-###SITEURL###'
-);
+###SITEURL###' );
 
-	/**
-	 * 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.
-	 * ###SITENAME###           The name of the site.
-	 * ###SITEURL###            The URL to the site.
-	 *
-	 * @since 4.9.6
-	 *
-	 * @param string $email_text     Text in the email.
-	 * @param int    $request_id     The request ID for this personal data export.
-	 */
-	$content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id );
+		/**
+		 * 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.
+		 * ###SITENAME###           The name of the site.
+		 * ###SITEURL###            The URL to the site.
+		 *
+		 * @since 4.9.6
+		 * @since 4.9.8 $has_export_data argument added.
+		 *
+		 * @param string $email_text      Text in the email.
+		 * @param int    $request_id      The request ID for this personal data export.
+		 * @param bool   $has_export_data Whether personal data exists to export.
+		 */
+		$content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id, $has_export_data );
+	} else {
+		/* translators: Do not translate EXPIRATION, SITENAME, SITEURL: those are placeholders. */
+		$email_text = __( 'Howdy,
 
-	$email_address = $request->email;
+Your request for an export of personal data has been completed.
+
+###SITENAME### has identified no personal data associated with this email address.
+
+Regards,
+All at ###SITENAME###
+###SITEURL###' );
+
+		/**
+		 * Filters the text of the email sent when no personal data exists to export.
+		 *
+		 * The following strings have a special meaning and will get replaced dynamically:
+		 * ###EXPIRATION###         The date when the URL will be automatically deleted.
+		 * ###SITENAME###           The name of the site.
+		 * ###SITEURL###            The URL to the site.
+		 *
+		 * @since 4.9.8
+		 *
+		 * @param string $email_text      Text in the email.
+		 * @param int    $request_id      The request ID for this personal data export.
+		 * @param bool   $has_export_data Whether personal data exists to export.
+		 */
+		$content = apply_filters( 'wp_privacy_personal_data_email_content_no_data', $email_text, $request_id, $has_export_data );
+	}
+
+	$email_address   = $request->email;
 	$export_file_url = get_post_meta( $request_id, '_export_file_url', true );
-	$site_name = is_multisite() ? get_site_option( 'site_name' ) : get_option( 'blogname' );
-	$site_url = network_home_url();
+	$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 );
 	$content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content );
 
@@ -2248,6 +2281,7 @@
 	$mail_success = wp_mail(
 		$email_address,
 		sprintf(
+			/* translators: %s Site name. */
 			__( '[%s] Personal Data Export' ),
 			wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES )
 		),
@@ -2353,25 +2387,41 @@
 	delete_post_meta( $request_id, '_export_data_raw' );
 	update_post_meta( $request_id, '_export_data_grouped', $groups );
 
-	/**
-	 * Generate the export file from the collected, grouped personal data.
-	 *
-	 * @since 4.9.6
-	 *
-	 * @param int $request_id The export request ID.
-	 */
-	do_action( 'wp_privacy_personal_data_export_file', $request_id );
+	$has_export_data = ! empty( $groups );
 
+	if ( $has_export_data ) {
+		/**
+		 * Generate the export file from the collected, grouped personal data.
+		 *
+		 * @since 4.9.6
+		 * @since 4.9.7 Added the $request parameter.
+		 *
+		 * @param int             $request_id The export request ID.
+		 * @param WP_User_Request $request    The export request.
+		 */
+		do_action( 'wp_privacy_personal_data_export_file', $request_id, $request );
+	} else {
+		/**
+		 * Fires when no personal data is found for exporting.
+		 *
+		 * @since 4.9.7
+		 *
+		 * @param int             $request_id The export request ID.
+		 * @param WP_User_Request $request    The export request.
+		 */
+		do_action( 'wp_privacy_personal_data_export_no_data', $request_id, $request );
+	}
+
 	// Clear the grouped data now that it is no longer needed.
 	delete_post_meta( $request_id, '_export_data_grouped' );
 
 	// If the destination is email, send it now.
 	if ( $send_as_email ) {
-		$mail_success = wp_privacy_send_personal_data_export_email( $request_id );
+		$mail_success = wp_privacy_send_personal_data_export_email( $request_id, $has_export_data );
 		if ( is_wp_error( $mail_success ) ) {
 			wp_send_json_error( $mail_success->get_error_message() );
 		}
-	} else {
+	} elseif ( $has_export_data ) {
 		// Modify the response to include the URL of the export file so the browser can fetch it.
 		$export_file_url = get_post_meta( $request_id, '_export_file_url', true );
 		if ( ! empty( $export_file_url ) ) {
