From 869faa0eeea4e80db2eb5835fa3db6838138fba7 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 5 Apr 2026 21:25:11 +0200 Subject: [PATCH 01/14] add _reset_privacy_policy_page_for_post function --- src/wp-includes/post.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index b225d35c48b2a..cbf5b32d3d159 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4027,6 +4027,26 @@ function _reset_front_page_settings_for_post( $post_id ) { unstick_post( $post->ID ); } +/** + * Resets the Privacy Policy page ID option when the Privacy Policy page + * is deleted or trashed, to prevent uncached database queries for a + * non-existent page. + * + * @since 7.1.0 + * @access private + * + * @param int $post_id The ID of the post being deleted or trashed. + */ +function _reset_privacy_policy_page_for_post( $post_id ) { + $post = get_post( $post_id ); + + if ( $post && 'page' === $post->post_type ) { + if ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) { + update_option( 'wp_page_for_privacy_policy', 0 ); + } + } +} + /** * Moves a post or page to the Trash * From e5745b5bebba9cb0a5e1a3db56fbdfacd1865c46 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 5 Apr 2026 21:26:19 +0200 Subject: [PATCH 02/14] add actions --- src/wp-includes/default-filters.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 4b6d9de25fa11..1a821bcae3400 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -577,7 +577,9 @@ add_action( 'init', 'create_initial_post_types', 0 ); // Highest priority. add_action( 'admin_menu', '_add_post_type_submenus' ); add_action( 'before_delete_post', '_reset_front_page_settings_for_post' ); +add_action( 'before_delete_post', '_reset_privacy_policy_page_for_post' ); add_action( 'wp_trash_post', '_reset_front_page_settings_for_post' ); +add_action( 'wp_trash_post', '_reset_privacy_policy_page_for_post' ); add_action( 'change_locale', 'create_initial_post_types' ); // Post Formats. From 6b3e05baa531ab8ffda442cd450b5353f0ba4358 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 5 Apr 2026 21:27:59 +0200 Subject: [PATCH 03/14] add reset option --- src/wp-admin/includes/class-wp-privacy-policy-content.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wp-admin/includes/class-wp-privacy-policy-content.php b/src/wp-admin/includes/class-wp-privacy-policy-content.php index c505df9b81644..c302e80157c2b 100644 --- a/src/wp-admin/includes/class-wp-privacy-policy-content.php +++ b/src/wp-admin/includes/class-wp-privacy-policy-content.php @@ -329,6 +329,12 @@ public static function notice( $post = null ) { $current_screen = get_current_screen(); $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' ); + // If the privacy policy page has been deleted, reset the option and bail. + if ( $policy_page_id && ! get_post( $policy_page_id ) ) { + update_option( 'wp_page_for_privacy_policy', 0 ); + return; + } + if ( 'post' !== $current_screen->base || $policy_page_id !== $post->ID ) { return; } From 3930d1c7a2634a6e6e74240169a4ac69bcd72502 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 5 Apr 2026 21:29:45 +0200 Subject: [PATCH 04/14] Removed the "page is in trash" error --- src/wp-admin/options-privacy.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/wp-admin/options-privacy.php b/src/wp-admin/options-privacy.php index 4205967acb3a8..b4a784a43745e 100644 --- a/src/wp-admin/options-privacy.php +++ b/src/wp-admin/options-privacy.php @@ -127,20 +127,7 @@ static function ( $body_class ) { 'error' ); } else { - if ( 'trash' === $privacy_policy_page->post_status ) { - add_settings_error( - 'page_for_privacy_policy', - 'page_for_privacy_policy', - sprintf( - /* translators: %s: URL to Pages Trash. */ - __( 'The currently selected Privacy Policy page is in the Trash. Please create or select a new Privacy Policy page or restore the current page.' ), - 'edit.php?post_status=trash&post_type=page' - ), - 'error' - ); - } else { - $privacy_policy_page_exists = true; - } + $privacy_policy_page_exists = true; } } From e391d3610636f0b8b31198907a80f95c2cbb9a67 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 08:50:26 +0200 Subject: [PATCH 05/14] add feedback of mukesh Co-authored-by: Mukesh Panchal --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index cbf5b32d3d159..f72789cbcdbb6 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4037,7 +4037,7 @@ function _reset_front_page_settings_for_post( $post_id ) { * * @param int $post_id The ID of the post being deleted or trashed. */ -function _reset_privacy_policy_page_for_post( $post_id ) { +function _reset_privacy_policy_page_for_post( int $post_id ): void { $post = get_post( $post_id ); if ( $post && 'page' === $post->post_type ) { From f4dfb4433826cd5c46582328856906b988bc99fe Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 08:50:52 +0200 Subject: [PATCH 06/14] add feedback of mukesh Co-authored-by: Mukesh Panchal --- src/wp-includes/post.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index f72789cbcdbb6..3e0b3cf2891d5 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4039,11 +4039,13 @@ function _reset_front_page_settings_for_post( $post_id ) { */ function _reset_privacy_policy_page_for_post( int $post_id ): void { $post = get_post( $post_id ); + + if ( ! $post ) { + return; + } - if ( $post && 'page' === $post->post_type ) { - if ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) { - update_option( 'wp_page_for_privacy_policy', 0 ); - } + if ( 'page' === $post->post_type && ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) { + update_option( 'wp_page_for_privacy_policy', 0 ); } } From 555e5c676d09fcb1673012f734fe2dac141b7ac0 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 08:58:31 +0200 Subject: [PATCH 07/14] Fix whitespace --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 3e0b3cf2891d5..2a8ad590907b1 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4039,7 +4039,7 @@ function _reset_front_page_settings_for_post( $post_id ) { */ function _reset_privacy_policy_page_for_post( int $post_id ): void { $post = get_post( $post_id ); - + if ( ! $post ) { return; } From 47d48d646d33b71ded8401d70ffa22f5a8a4295e Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 09:20:52 +0200 Subject: [PATCH 08/14] fix phpstan error --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 2a8ad590907b1..e83257ce0b482 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4044,7 +4044,7 @@ function _reset_privacy_policy_page_for_post( int $post_id ): void { return; } - if ( 'page' === $post->post_type && ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) { + if ( 'page' === $post->post_type && ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) ) { update_option( 'wp_page_for_privacy_policy', 0 ); } } From beb43492feb006a7c9631a442dfd9901818bf21f Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 11:23:02 +0200 Subject: [PATCH 09/14] add westons feedback Co-authored-by: Weston Ruter --- src/wp-includes/post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index e83257ce0b482..1a978d6932e6d 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4044,7 +4044,7 @@ function _reset_privacy_policy_page_for_post( int $post_id ): void { return; } - if ( 'page' === $post->post_type && ( (int) get_option( 'wp_page_for_privacy_policy' ) === (int) $post_id ) ) { + if ( 'page' === get_post_type( $post_id ) && ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post_id ) ) { update_option( 'wp_page_for_privacy_policy', 0 ); } } From fbf18e4ab8ee615798b302c929a55d7905368f12 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 11:57:37 +0200 Subject: [PATCH 10/14] remove redundant code --- src/wp-includes/post.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 1a978d6932e6d..6ac7bafefdcc0 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -4038,12 +4038,6 @@ function _reset_front_page_settings_for_post( $post_id ) { * @param int $post_id The ID of the post being deleted or trashed. */ function _reset_privacy_policy_page_for_post( int $post_id ): void { - $post = get_post( $post_id ); - - if ( ! $post ) { - return; - } - if ( 'page' === get_post_type( $post_id ) && ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post_id ) ) { update_option( 'wp_page_for_privacy_policy', 0 ); } From f8e9ddb3cbe6ddd884658832f05bebf6008568a9 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 18:49:46 +0200 Subject: [PATCH 11/14] add unit tests --- .../wpPrivacyResetPolicyPageForPost.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php new file mode 100644 index 0000000000000..62738896cc396 --- /dev/null +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -0,0 +1,139 @@ +policy_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + update_option( 'wp_page_for_privacy_policy', $this->policy_page_id ); + } + + public function tear_down() { + delete_option( 'wp_page_for_privacy_policy' ); + parent::tear_down(); + } + + /** + * Tests that trashing the Privacy Policy page resets the option to 0. + * + * @ticket 56694 + */ + public function test_trashing_privacy_policy_page_resets_option() { + wp_trash_post( $this->policy_page_id ); + + $this->assertSame( 0, (int) get_option( 'wp_page_for_privacy_policy' ) ); + } + + /** + * Tests that permanently deleting the Privacy Policy page resets the option to 0. + * + * @ticket 56694 + */ + public function test_deleting_privacy_policy_page_resets_option() { + wp_delete_post( $this->policy_page_id, true ); + + $this->assertSame( 0, (int) get_option( 'wp_page_for_privacy_policy' ) ); + } + + /** + * Tests that trashing a different page does not change the option. + * + * @ticket 56694 + */ + public function test_trashing_a_different_page_does_not_reset_option() { + $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + wp_trash_post( $other_page_id ); + + $this->assertSame( + $this->policy_page_id, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'Trashing an unrelated page should not reset wp_page_for_privacy_policy.' + ); + } + + /** + * Tests that deleting a non-page post type does not change the option. + * + * @ticket 56694 + */ + public function test_deleting_non_page_post_type_does_not_reset_option() { + $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) ); + wp_delete_post( $post_id, true ); + + $this->assertSame( + $this->policy_page_id, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'Deleting a non-page post should not reset wp_page_for_privacy_policy.' + ); + } + + /** + * Tests that WP_Privacy_Policy_Content::notice() resets the option to 0 + * when the stored ID points to a page that no longer exists. + * + * @ticket 56694 + * + * @covers WP_Privacy_Policy_Content::notice + */ + public function test_notice_self_heals_when_policy_page_does_not_exist() { + update_option( 'wp_page_for_privacy_policy', 99999 ); + + wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + set_current_screen( 'post' ); + + $post = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ); + WP_Privacy_Policy_Content::notice( $post ); + + $this->assertSame( + 0, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'notice() should reset the option to 0 when the stored page does not exist.' + ); + } + + /** + * Tests that _reset_privacy_policy_page_for_post() does not call + * update_option() when wp_page_for_privacy_policy is already 0. + * + * @ticket 56694 + */ + public function test_no_update_option_when_policy_page_already_zero() { + update_option( 'wp_page_for_privacy_policy', 0 ); + + $call_count = 0; + add_filter( + 'pre_update_option_wp_page_for_privacy_policy', + static function ( $value ) use ( &$call_count ) { + ++$call_count; + return $value; + } + ); + + $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + wp_trash_post( $other_page_id ); + + $this->assertSame( + 0, + $call_count, + 'update_option() should not be called when wp_page_for_privacy_policy is already 0.' + ); + } +} From d398eeb0bd57c4a66ccea9fa32d0074312027fba Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 19:30:31 +0200 Subject: [PATCH 12/14] upgrade failing test --- .../tests/privacy/wpPrivacyResetPolicyPageForPost.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index 62738896cc396..f50eae65523cb 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -96,7 +96,10 @@ public function test_deleting_non_page_post_type_does_not_reset_option() { public function test_notice_self_heals_when_policy_page_does_not_exist() { update_option( 'wp_page_for_privacy_policy', 99999 ); - wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); + $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + $user = new WP_User( $user_id ); + $user->add_cap( 'manage_privacy_options' ); + wp_set_current_user( $user_id ); set_current_screen( 'post' ); $post = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ); From ccfd7f2cc3e4690622c08ff1deac4a24a13e2b40 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 6 Apr 2026 19:59:55 +0200 Subject: [PATCH 13/14] update test --- .../tests/privacy/wpPrivacyResetPolicyPageForPost.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index f50eae65523cb..1e4f0362327bd 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -94,12 +94,13 @@ public function test_deleting_non_page_post_type_does_not_reset_option() { * @covers WP_Privacy_Policy_Content::notice */ public function test_notice_self_heals_when_policy_page_does_not_exist() { + require_once ABSPATH . 'wp-admin/includes/class-wp-privacy-policy-content.php'; + update_option( 'wp_page_for_privacy_policy', 99999 ); $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); - $user = new WP_User( $user_id ); - $user->add_cap( 'manage_privacy_options' ); wp_set_current_user( $user_id ); + wp_get_current_user()->add_cap( 'manage_privacy_options' ); set_current_screen( 'post' ); $post = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ); From 49426c4267e5cb81f39c83e07ff3fb31ca559fcf Mon Sep 17 00:00:00 2001 From: Brian Date: Thu, 9 Apr 2026 17:19:24 +0200 Subject: [PATCH 14/14] fix tests --- .../phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index 1e4f0362327bd..ec2c78dadcfc9 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -100,7 +100,9 @@ public function test_notice_self_heals_when_policy_page_does_not_exist() { $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); wp_set_current_user( $user_id ); - wp_get_current_user()->add_cap( 'manage_privacy_options' ); + if ( is_multisite() ) { + grant_super_admin( $user_id ); + } set_current_screen( 'post' ); $post = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) );