diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php
index 9db1065c77..815c539a4a 100644
--- a/src/wp-includes/link-template.php
+++ b/src/wp-includes/link-template.php
@@ -4282,10 +4282,74 @@ function get_parent_theme_file_path( $file = '' ) {
* @return string The URL to the privacy policy page. Empty string if it doesn't exist.
*/
function get_privacy_policy_url() {
+ $url = '';
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
if ( ! empty( $policy_page_id ) && get_post_status( $policy_page_id ) === 'publish' ) {
- return get_permalink( $policy_page_id );
+ $url = (string) get_permalink( $policy_page_id );
+ }
+
+ /**
+ * Filters the URL of the privacy policy page.
+ *
+ * @since 4.9.6
+ *
+ * @param string $url The URL to the privacy policy page. Empty string
+ * if it doesn't exist.
+ * @param int $policy_page_id The ID of privacy policy page.
+ */
+ return apply_filters( 'privacy_policy_url', $url, $policy_page_id );
+}
+
+/**
+ * Displays the privacy policy link with formatting, when applicable.
+ *
+ * @since 4.9.6
+ *
+ * @param string $before Optional. Display before privacy policy link. Default empty.
+ * @param string $after Optional. Display after privacy policy link. Default empty.
+ */
+function the_privacy_policy_link( $before = '', $after = '' ) {
+ echo get_the_privacy_policy_link( $before, $after );
+}
+
+/**
+ * Returns the privacy policy link with formatting, when applicable.
+ *
+ * @since 4.9.6
+ *
+ * @param string $before Optional. Display before privacy policy link. Default empty.
+ * @param string $after Optional. Display after privacy policy link. Default empty.
+ *
+ * @return string Markup for the link and surrounding elements. Empty string if it
+ * doesn't exist.
+ */
+function get_the_privacy_policy_link( $before = '', $after = '' ) {
+ $link = '';
+ $privacy_policy_url = get_privacy_policy_url();
+
+ if ( $privacy_policy_url ) {
+ $link = sprintf(
+ '%s',
+ esc_url( $privacy_policy_url ),
+ __( 'Privacy Policy' )
+ );
+ }
+
+ /**
+ * Filters the privacy policy link.
+ *
+ * @since 4.9.6
+ *
+ * @param string $link The privacy policy link. Empty string if it
+ * doesn't exist.
+ * @param string $privacy_policy_url The URL of the privacy policy. Empty string
+ * if it doesn't exist.
+ */
+ $link = apply_filters( 'the_privacy_policy_link', $link, $privacy_policy_url );
+
+ if ( $link ) {
+ return $before . $link . $after;
}
return '';
diff --git a/tests/phpunit/tests/link/getThePrivacyPolicyLink.php b/tests/phpunit/tests/link/getThePrivacyPolicyLink.php
new file mode 100644
index 0000000000..14dd1a9ea0
--- /dev/null
+++ b/tests/phpunit/tests/link/getThePrivacyPolicyLink.php
@@ -0,0 +1,143 @@
+post->create(
+ array(
+ 'post_type' => 'page',
+ 'post_title' => WP_TESTS_DOMAIN . ' Privacy Policy',
+ )
+ );
+
+ // `esc_url()` is added for consistency with `get_the_privacy_policy_link()`.
+ self::$privacy_policy_url = esc_url( get_permalink( self::$privacy_policy_page_id ) );
+
+ self::$before = '';
+ self::$after = '';
+ }
+
+ /**
+ * The function should return a valid link if a privacy policy page has been
+ * created and set as the `wp_page_for_privacy_policy`.
+ */
+ public function test_get_the_privacy_policy_link_should_return_valid_link_when_privacy_page_set() {
+ update_option( 'wp_page_for_privacy_policy', self::$privacy_policy_page_id );
+
+ $actual_link = get_the_privacy_policy_link();
+
+ $this->assertStringStartsWith( 'assertContains( self::$privacy_policy_url, $actual_link );
+ $this->assertStringEndsWith( '', $actual_link );
+ }
+
+ /**
+ * The function should prepend the supplied `$before` markup and append the
+ * supplied `$after` markup when the `wp_page_for_privacy_policy` is configured.
+ */
+ public function test_get_the_privacy_policy_link_should_prepend_and_append_supplied_markup_when_privacy_page_set() {
+ update_option( 'wp_page_for_privacy_policy', self::$privacy_policy_page_id );
+
+ $actual_link = get_the_privacy_policy_link( self::$before, self::$after );
+
+ $this->assertStringStartsWith( self::$before . 'assertContains( self::$privacy_policy_url, $actual_link );
+ $this->assertStringEndsWith( '' . self::$after, $actual_link );
+ }
+
+ /**
+ * The function should _not_ prepend the supplied `$before` markup and append
+ * the supplied `$after` markup when the `wp_page_for_privacy_policy` is _not_ configured.
+ */
+ public function test_get_the_privacy_policy_link_should_not_prepend_and_append_supplied_markup_when_privacy_page_not_set() {
+ $actual_link = get_the_privacy_policy_link( self::$before, self::$after );
+
+ $this->assertSame( '', $actual_link );
+ }
+
+ /**
+ * The function should return an empty string when `wp_page_for_privacy_policy` is _not_ configured.
+ */
+ public function test_get_the_privacy_policy_link_should_return_empty_string_when_privacy_page_not_set() {
+ $this->assertSame( '', get_the_privacy_policy_link() );
+ }
+
+ /**
+ * The output of the get_the_privacy_policy_link() function should be filterable with the 'privacy_policy_link' filter.
+ */
+ public function test_get_the_privacy_policy_link_should_be_filterable() {
+ update_option( 'wp_page_for_privacy_policy', self::$privacy_policy_page_id );
+ $expected_url = get_privacy_policy_url();
+
+ $this->assertNotEmpty( $expected_url );
+
+ add_filter( 'the_privacy_policy_link', array( $this, 'modify_link_markup' ), 10, 2 );
+ $this->assertSame( 'Policy: ' . $expected_url, get_the_privacy_policy_link() );
+ remove_filter( 'the_privacy_policy_link', array( $this, 'modify_link_markup' ), 10 );
+ }
+
+ /**
+ * Return modified `the_privacy_policy_link` content in order to test the filter.
+ *
+ * @param string $link The privacy policy link. Empty string if it
+ * doesn't exist.
+ * @param string $privacy_policy_url The URL of the privacy policy. Empty string
+ * if it doesn't exist.
+ * @return string
+ */
+ public static function modify_link_markup( $link, $privacy_policy_url ) {
+ return 'Policy: ' . $privacy_policy_url;
+ }
+}
diff --git a/tests/phpunit/tests/url/getPrivacyPolicyUrl.php b/tests/phpunit/tests/url/getPrivacyPolicyUrl.php
new file mode 100644
index 0000000000..b6d6a23ba3
--- /dev/null
+++ b/tests/phpunit/tests/url/getPrivacyPolicyUrl.php
@@ -0,0 +1,106 @@
+post->create(
+ array(
+ 'post_type' => 'page',
+ 'post_title' => WP_TESTS_DOMAIN . ' Privacy Policy',
+ )
+ );
+
+ self::$privacy_policy_url = get_permalink( self::$privacy_policy_page_id );
+ }
+
+ /**
+ * The function should return an empty string when `wp_page_for_privacy_policy` is _not_ set.
+ */
+ public function test_get_privacy_policy_url_should_return_empty_string_when_policy_page_not_set() {
+ $this->assertSame( '', get_privacy_policy_url() );
+ }
+
+ /**
+ * The function should return the privacy policy URL when `wp_page_for_privacy_policy` is set.
+ */
+ public function test_get_privacy_policy_url_should_return_valid_url_when_policy_page_set() {
+ update_option( 'wp_page_for_privacy_policy', self::$privacy_policy_page_id );
+
+ $this->assertSame( self::$privacy_policy_url, get_privacy_policy_url() );
+ }
+
+ /**
+ * The function should return an empty string when `wp_page_for_privacy_policy` is _not_ set.
+ */
+ public function test_get_privacy_policy_url_should_return_empty_when_privacy_policy_page_not_set() {
+ $this->assertSame( '', get_privacy_policy_url() );
+ }
+
+ /**
+ * The function should return an empty string for an invalid `wp_page_for_privacy_policy` value.
+ */
+ public function test_get_privacy_policy_url_should_return_empty_for_non_existing_page() {
+ update_option( 'wp_page_for_privacy_policy', PHP_INT_MAX );
+
+ $this->assertSame( '', get_privacy_policy_url() );
+ }
+
+ /**
+ * The output of `get_privacy_policy_url()` should be filterable with the 'privacy_policy_url' filter.
+ */
+ public function test_get_privacy_policy_url_should_be_filterable() {
+ update_option( 'wp_page_for_privacy_policy', self::$privacy_policy_page_id );
+
+ add_filter( 'privacy_policy_url', array( $this, 'modify_policy_url' ), 10, 2 );
+ $this->assertSame( 'Page ID: ' . self::$privacy_policy_page_id, get_privacy_policy_url() );
+ remove_filter( 'privacy_policy_url', array( $this, 'modify_policy_url' ), 10 );
+ }
+
+ /**
+ * Return modified `privacy_policy_url` content in order to test the filter.
+ *
+ * @param string $url The URL to the privacy policy page. Empty string
+ * if it doesn't exist.
+ * @param int $policy_page_id The ID of privacy policy page.
+ * @return string
+ */
+ public static function modify_policy_url( $url, $policy_page_id ) {
+ return 'Page ID: ' . $policy_page_id;
+ }
+}