post_type ) {
		return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) );
	}
	$result = wp_send_user_request( $request_id );
	if ( is_wp_error( $result ) ) {
		return $result;
	} elseif ( ! $result ) {
		return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation for personal data request.' ) );
	}
	return true;
}
/**
 * Marks a request as completed by the admin and logs the current timestamp.
 *
 * @since 4.9.6
 * @access private
 *
 * @param int $request_id Request ID.
 * @return int|WP_Error Request ID on success, or a WP_Error on failure.
 */
function _wp_privacy_completed_request( $request_id ) {
	// Get the request.
	$request_id = absint( $request_id );
	$request    = wp_get_user_request( $request_id );
	if ( ! $request ) {
		return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) );
	}
	update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() );
	$result = wp_update_post(
		array(
			'ID'          => $request_id,
			'post_status' => 'request-completed',
		)
	);
	return $result;
}
/**
 * Handle list table actions.
 *
 * @since 4.9.6
 * @access private
 */
function _wp_personal_data_handle_actions() {
	if ( isset( $_POST['privacy_action_email_retry'] ) ) {
		check_admin_referer( 'bulk-privacy_requests' );
		$request_id = absint( current( array_keys( (array) wp_unslash( $_POST['privacy_action_email_retry'] ) ) ) );
		$result     = _wp_privacy_resend_request( $request_id );
		if ( is_wp_error( $result ) ) {
			add_settings_error(
				'privacy_action_email_retry',
				'privacy_action_email_retry',
				$result->get_error_message(),
				'error'
			);
		} else {
			add_settings_error(
				'privacy_action_email_retry',
				'privacy_action_email_retry',
				__( 'Confirmation request sent again successfully.' ),
				'success'
			);
		}
	} elseif ( isset( $_POST['action'] ) ) {
		$action = ! empty( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : '';
		switch ( $action ) {
			case 'add_export_personal_data_request':
			case 'add_remove_personal_data_request':
				check_admin_referer( 'personal-data-request' );
				if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) {
					add_settings_error(
						'action_type',
						'action_type',
						__( 'Invalid personal data action.' ),
						'error'
					);
				}
				$action_type               = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) );
				$username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) );
				$email_address             = '';
				$status                    = 'pending';
				if ( ! isset( $_POST['send_confirmation_email'] ) ) {
					$status = 'confirmed';
				}
				if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) {
					add_settings_error(
						'action_type',
						'action_type',
						__( 'Invalid personal data action.' ),
						'error'
					);
				}
				if ( ! is_email( $username_or_email_address ) ) {
					$user = get_user_by( 'login', $username_or_email_address );
					if ( ! $user instanceof WP_User ) {
						add_settings_error(
							'username_or_email_for_privacy_request',
							'username_or_email_for_privacy_request',
							__( 'Unable to add this request. A valid email address or username must be supplied.' ),
							'error'
						);
					} else {
						$email_address = $user->user_email;
					}
				} else {
					$email_address = $username_or_email_address;
				}
				if ( empty( $email_address ) ) {
					break;
				}
				$request_id = wp_create_user_request( $email_address, $action_type, array(), $status );
				$message    = '';
				if ( is_wp_error( $request_id ) ) {
					$message = $request_id->get_error_message();
				} elseif ( ! $request_id ) {
					$message = __( 'Unable to initiate confirmation request.' );
				}
				if ( $message ) {
					add_settings_error(
						'username_or_email_for_privacy_request',
						'username_or_email_for_privacy_request',
						$message,
						'error'
					);
					break;
				}
				if ( 'pending' === $status ) {
					wp_send_user_request( $request_id );
					$message = __( 'Confirmation request initiated successfully.' );
				} elseif ( 'confirmed' === $status ) {
					$message = __( 'Request added successfully.' );
				}
				if ( $message ) {
					add_settings_error(
						'username_or_email_for_privacy_request',
						'username_or_email_for_privacy_request',
						$message,
						'success'
					);
					break;
				}
		}
	}
}
/**
 * Cleans up failed and expired requests before displaying the list table.
 *
 * @since 4.9.6
 * @access private
 */
function _wp_personal_data_cleanup_requests() {
	/** This filter is documented in wp-includes/user.php */
	$expires = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS );
	$requests_query = new WP_Query(
		array(
			'post_type'      => 'user_request',
			'posts_per_page' => -1,
			'post_status'    => 'request-pending',
			'fields'         => 'ids',
			'date_query'     => array(
				array(
					'column' => 'post_modified_gmt',
					'before' => $expires . ' seconds ago',
				),
			),
		)
	);
	$request_ids = $requests_query->posts;
	foreach ( $request_ids as $request_id ) {
		wp_update_post(
			array(
				'ID'            => $request_id,
				'post_status'   => 'request-failed',
				'post_password' => '',
			)
		);
	}
}
/**
 * Generate a single group for the personal data export report.
 *
 * @since 4.9.6
 * @since 5.4.0 Added the `$group_id` and `$groups_count` parameters.
 *
 * @param array  $group_data {
 *     The group data to render.
 *
 *     @type string $group_label  The user-facing heading for the group, e.g. 'Comments'.
 *     @type array  $items        {
 *         An array of group items.
 *
 *         @type array  $group_item_data  {
 *             An array of name-value pairs for the item.
 *
 *             @type string $name   The user-facing name of an item name-value pair, e.g. 'IP Address'.
 *             @type string $value  The user-facing value of an item data pair, e.g. '50.60.70.0'.
 *         }
 *     }
 * }
 * @param string $group_id     The group identifier.
 * @param int    $groups_count The number of all groups
 * @return string The HTML for this group and its items.
 */
function wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id = '', $groups_count = 1 ) {
	$group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id );
	$group_html  = '
';
	$group_html .= esc_html( $group_data['group_label'] );
	$items_count = count( (array) $group_data['items'] );
	if ( $items_count > 1 ) {
		$group_html .= sprintf( ' (%d)', $items_count );
	}
	$group_html .= '
';
	if ( ! empty( $group_data['group_description'] ) ) {
		$group_html .= '' . esc_html( $group_data['group_description'] ) . '
';
	}
	$group_html .= '';
	foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) {
		$group_html .= '
';
		$group_html .= '';
		foreach ( (array) $group_item_data as $group_item_datum ) {
			$value = $group_item_datum['value'];
			// If it looks like a link, make it a link.
			if ( ! str_contains( $value, ' ' ) && ( str_starts_with( $value, 'http://' ) || str_starts_with( $value, 'https://' ) ) ) {
				$value = '' . esc_html( $value ) . '';
			}
			$group_html .= '';
			$group_html .= '| ' . esc_html( $group_item_datum['name'] ) . '';
			$group_html .= ' | ' . wp_kses( $value, 'personal_data_export' ) . '';
			$group_html .= ' | 
';
		}
		$group_html .= '';
		$group_html .= '
';
	}
	if ( $groups_count > 1 ) {
		$group_html .= '
';
	}
	$group_html .= '
_export_data_grouped' ),
				'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, "\n" );
	fwrite( $file, "\n" );
	fwrite( $file, "\n" );
	fwrite( $file, "' );
	fwrite( $file, '' );
	fwrite( $file, esc_html( $title ) );
	fwrite( $file, '' );
	fwrite( $file, "\n" );
	fwrite( $file, "\n" );
	fwrite( $file, '' . esc_html__( 'Personal Data Export' ) . '
' );
	// Create TOC.
	if ( $groups_count > 1 ) {
		fwrite( $file, '' );
		fwrite( $file, '
' . esc_html__( 'Table of Contents' ) . '
' );
		fwrite( $file, '
' );
		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( ' (%d)', $group_items_count );
			}
			fwrite( $file, '- ' );
			fwrite( $file, '' . $group_label . '' );
			fwrite( $file, '' );
		}
		fwrite( $file, '
' );
		fwrite( $file, '