From 869faa0eeea4e80db2eb5835fa3db6838138fba7 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 5 Apr 2026 21:25:11 +0200 Subject: [PATCH 01/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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' ) ); From 4477dd6de1c8ef7977603b42f44a7a8d0cfe336b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 2 Jun 2026 10:46:07 +0200 Subject: [PATCH 15/25] Add void return types --- .../privacy/wpPrivacyResetPolicyPageForPost.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index ec2c78dadcfc9..a954d4ba7133b 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -19,14 +19,14 @@ class Tests_Privacy_WpPrivacyResetPolicyPageForPost extends WP_UnitTestCase { */ private $policy_page_id; - public function set_up() { + public function set_up(): void { parent::set_up(); $this->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() { + public function tear_down(): void { delete_option( 'wp_page_for_privacy_policy' ); parent::tear_down(); } @@ -36,7 +36,7 @@ public function tear_down() { * * @ticket 56694 */ - public function test_trashing_privacy_policy_page_resets_option() { + public function test_trashing_privacy_policy_page_resets_option(): void { wp_trash_post( $this->policy_page_id ); $this->assertSame( 0, (int) get_option( 'wp_page_for_privacy_policy' ) ); @@ -47,7 +47,7 @@ public function test_trashing_privacy_policy_page_resets_option() { * * @ticket 56694 */ - public function test_deleting_privacy_policy_page_resets_option() { + public function test_deleting_privacy_policy_page_resets_option(): void { wp_delete_post( $this->policy_page_id, true ); $this->assertSame( 0, (int) get_option( 'wp_page_for_privacy_policy' ) ); @@ -58,7 +58,7 @@ public function test_deleting_privacy_policy_page_resets_option() { * * @ticket 56694 */ - public function test_trashing_a_different_page_does_not_reset_option() { + public function test_trashing_a_different_page_does_not_reset_option(): void { $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); wp_trash_post( $other_page_id ); @@ -74,7 +74,7 @@ public function test_trashing_a_different_page_does_not_reset_option() { * * @ticket 56694 */ - public function test_deleting_non_page_post_type_does_not_reset_option() { + public function test_deleting_non_page_post_type_does_not_reset_option(): void { $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) ); wp_delete_post( $post_id, true ); @@ -93,7 +93,7 @@ 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() { + public function test_notice_self_heals_when_policy_page_does_not_exist(): void { require_once ABSPATH . 'wp-admin/includes/class-wp-privacy-policy-content.php'; update_option( 'wp_page_for_privacy_policy', 99999 ); @@ -121,7 +121,7 @@ public function test_notice_self_heals_when_policy_page_does_not_exist() { * * @ticket 56694 */ - public function test_no_update_option_when_policy_page_already_zero() { + public function test_no_update_option_when_policy_page_already_zero(): void { update_option( 'wp_page_for_privacy_policy', 0 ); $call_count = 0; From 1ba5c0ff6ceedc7d9d474d856159ee42252cfea1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 2 Jun 2026 10:48:00 +0200 Subject: [PATCH 16/25] Add assertion for factory output --- .../tests/privacy/wpPrivacyResetPolicyPageForPost.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index a954d4ba7133b..f51e2e2ba6e5b 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -22,7 +22,9 @@ class Tests_Privacy_WpPrivacyResetPolicyPageForPost extends WP_UnitTestCase { public function set_up(): void { parent::set_up(); - $this->policy_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + $page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + assert( is_int( $page_id ) ); + $this->policy_page_id = $page_id; update_option( 'wp_page_for_privacy_policy', $this->policy_page_id ); } @@ -60,6 +62,7 @@ public function test_deleting_privacy_policy_page_resets_option(): void { */ public function test_trashing_a_different_page_does_not_reset_option(): void { $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + $this->assertIsInt( $other_page_id ); wp_trash_post( $other_page_id ); $this->assertSame( @@ -76,6 +79,7 @@ public function test_trashing_a_different_page_does_not_reset_option(): void { */ public function test_deleting_non_page_post_type_does_not_reset_option(): void { $post_id = self::factory()->post->create( array( 'post_type' => 'post' ) ); + $this->assertIsInt( $post_id ); wp_delete_post( $post_id, true ); $this->assertSame( @@ -99,6 +103,7 @@ public function test_notice_self_heals_when_policy_page_does_not_exist(): void { update_option( 'wp_page_for_privacy_policy', 99999 ); $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); + $this->assertIsInt( $user_id ); wp_set_current_user( $user_id ); if ( is_multisite() ) { grant_super_admin( $user_id ); @@ -106,6 +111,7 @@ public function test_notice_self_heals_when_policy_page_does_not_exist(): void { set_current_screen( 'post' ); $post = self::factory()->post->create_and_get( array( 'post_type' => 'page' ) ); + $this->assertInstanceOf( WP_Post::class, $post ); WP_Privacy_Policy_Content::notice( $post ); $this->assertSame( @@ -134,6 +140,7 @@ static function ( $value ) use ( &$call_count ) { ); $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + $this->assertIsInt( $other_page_id ); wp_trash_post( $other_page_id ); $this->assertSame( From bd4b5f18d8b1cfde85244d479d83fddcf00db97f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 2 Jun 2026 11:01:06 +0200 Subject: [PATCH 17/25] Use native property hint --- .../phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index f51e2e2ba6e5b..810068de4af08 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -14,10 +14,8 @@ class Tests_Privacy_WpPrivacyResetPolicyPageForPost extends WP_UnitTestCase { /** * ID of the page set as the Privacy Policy page. - * - * @var int */ - private $policy_page_id; + private int $policy_page_id; public function set_up(): void { parent::set_up(); From 05722734327bb4edc0a5cb7fb97e4b63fbdf8506 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 2 Jun 2026 11:01:44 +0200 Subject: [PATCH 18/25] Remove unnecessary tags from unit test class --- .../phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index 810068de4af08..e019d8c9d1d56 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -3,10 +3,6 @@ * Tests for _reset_privacy_policy_page_for_post() and the self-healing * check in WP_Privacy_Policy_Content::notice(). * - * @package WordPress - * @subpackage UnitTests - * @since 7.1.0 - * * @group privacy * * @covers ::_reset_privacy_policy_page_for_post From b60e3ee63380a0e25a9d6e67142c5b64299e077c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 2 Jun 2026 11:25:30 +0200 Subject: [PATCH 19/25] Revert "Remove unnecessary tags from unit test class" This reverts commit 05722734327bb4edc0a5cb7fb97e4b63fbdf8506. --- .../phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index e019d8c9d1d56..810068de4af08 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -3,6 +3,10 @@ * Tests for _reset_privacy_policy_page_for_post() and the self-healing * check in WP_Privacy_Policy_Content::notice(). * + * @package WordPress + * @subpackage UnitTests + * @since 7.1.0 + * * @group privacy * * @covers ::_reset_privacy_policy_page_for_post From 20afd2c7579ab96db9ccdb8441f8e57fcbb1d903 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 2 Jun 2026 12:58:48 +0200 Subject: [PATCH 20/25] wait until trash operation was successful Co-authored-by: Weston Ruter --- src/wp-includes/default-filters.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index d9e5da27ca006..452c689d193e6 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -585,7 +585,7 @@ 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( 'trashed_post', '_reset_privacy_policy_page_for_post' ); add_action( 'change_locale', 'create_initial_post_types' ); // Post Formats. From a9dab6058e2be031474ab1d5199a3b9f57aca5da Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 16 Jun 2026 18:24:17 +0200 Subject: [PATCH 21/25] reset wp_page_for_privacy_policy on permanent deletion only, not on trash --- src/wp-admin/options-privacy.php | 15 ++++++++++++++- src/wp-includes/default-filters.php | 1 - .../privacy/wpPrivacyResetPolicyPageForPost.php | 11 ++++++++--- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/wp-admin/options-privacy.php b/src/wp-admin/options-privacy.php index b4a784a43745e..4205967acb3a8 100644 --- a/src/wp-admin/options-privacy.php +++ b/src/wp-admin/options-privacy.php @@ -127,7 +127,20 @@ static function ( $body_class ) { 'error' ); } else { - $privacy_policy_page_exists = true; + 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; + } } } diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 452c689d193e6..e2dcbfa20e7c5 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -585,7 +585,6 @@ 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( 'trashed_post', '_reset_privacy_policy_page_for_post' ); add_action( 'change_locale', 'create_initial_post_types' ); // Post Formats. diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index 810068de4af08..aec4659e78b68 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -32,14 +32,19 @@ public function tear_down(): void { } /** - * Tests that trashing the Privacy Policy page resets the option to 0. + * Tests that trashing the Privacy Policy page does NOT reset the option, + * so that restoring from trash preserves the assignment. * * @ticket 56694 */ - public function test_trashing_privacy_policy_page_resets_option(): void { + public function test_trashing_privacy_policy_page_does_not_reset_option(): void { wp_trash_post( $this->policy_page_id ); - $this->assertSame( 0, (int) get_option( 'wp_page_for_privacy_policy' ) ); + $this->assertSame( + $this->policy_page_id, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'Trashing the Privacy Policy page should not reset wp_page_for_privacy_policy.' + ); } /** From 12e931935d556bda75a7c634264ca650dcbcdb6e Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 16 Jun 2026 18:28:20 +0200 Subject: [PATCH 22/25] update docblock and testcase --- src/wp-includes/post.php | 4 ++-- .../phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 309e54ea91906..fc3fc0788b329 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -3993,13 +3993,13 @@ function _reset_front_page_settings_for_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 + * is permanently deleted, 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. + * @param int $post_id The ID of the post being deleted. */ function _reset_privacy_policy_page_for_post( int $post_id ): void { if ( 'page' === get_post_type( $post_id ) && ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post_id ) ) { diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index aec4659e78b68..b5e39fda5808e 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -144,7 +144,7 @@ static function ( $value ) use ( &$call_count ) { $other_page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); $this->assertIsInt( $other_page_id ); - wp_trash_post( $other_page_id ); + wp_delete_post( $other_page_id, true ); $this->assertSame( 0, From ada29154b3741b9603fe711d55fede8ad2d0eeb2 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 16 Jun 2026 18:48:19 +0200 Subject: [PATCH 23/25] add test cases --- .../wpPrivacyResetPolicyPageForPost.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index b5e39fda5808e..8621f2a22b6aa 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -152,4 +152,41 @@ static function ( $value ) use ( &$call_count ) { 'update_option() should not be called when wp_page_for_privacy_policy is already 0.' ); } + + /** + * Tests that untrashing the Privacy Policy page preserves the option, + * confirming the trash/restore cycle keeps the assignment intact. + * + * @ticket 56694 + */ + public function test_untrashing_privacy_policy_page_preserves_option(): void { + wp_trash_post( $this->policy_page_id ); + wp_untrash_post( $this->policy_page_id ); + + $this->assertSame( + $this->policy_page_id, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'Untrashing the Privacy Policy page should preserve wp_page_for_privacy_policy.' + ); + } + + /** + * Tests that when trash is disabled (EMPTY_TRASH_DAYS=0), wp_trash_post() + * permanently deletes the page and the option is reset. + * + * @ticket 56694 + */ + public function test_trashing_resets_option_when_trash_is_disabled(): void { + add_filter( 'pre_option_empty_trash_days', '__return_zero' ); + + wp_trash_post( $this->policy_page_id ); + + remove_filter( 'pre_option_empty_trash_days', '__return_zero' ); + + $this->assertSame( + 0, + (int) get_option( 'wp_page_for_privacy_policy' ), + 'When trash is disabled, wp_trash_post() permanently deletes and should reset wp_page_for_privacy_policy.' + ); + } } From 4eee3f59e97c676927681ef372fc8ef423a888f3 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 16 Jun 2026 19:09:09 +0200 Subject: [PATCH 24/25] update tests --- .../wpPrivacyResetPolicyPageForPost.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index 8621f2a22b6aa..fe10f9c48b08e 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -170,23 +170,4 @@ public function test_untrashing_privacy_policy_page_preserves_option(): void { ); } - /** - * Tests that when trash is disabled (EMPTY_TRASH_DAYS=0), wp_trash_post() - * permanently deletes the page and the option is reset. - * - * @ticket 56694 - */ - public function test_trashing_resets_option_when_trash_is_disabled(): void { - add_filter( 'pre_option_empty_trash_days', '__return_zero' ); - - wp_trash_post( $this->policy_page_id ); - - remove_filter( 'pre_option_empty_trash_days', '__return_zero' ); - - $this->assertSame( - 0, - (int) get_option( 'wp_page_for_privacy_policy' ), - 'When trash is disabled, wp_trash_post() permanently deletes and should reset wp_page_for_privacy_policy.' - ); - } } From c0dd628de11e6ff7e9909e00f50c137d75bf1d7e Mon Sep 17 00:00:00 2001 From: Brian Date: Wed, 17 Jun 2026 07:37:35 +0200 Subject: [PATCH 25/25] fix The closing brace for the class must go on the next line after the body --- tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php index fe10f9c48b08e..50ce04cb1bd44 100644 --- a/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php +++ b/tests/phpunit/tests/privacy/wpPrivacyResetPolicyPageForPost.php @@ -169,5 +169,4 @@ public function test_untrashing_privacy_policy_page_preserves_option(): void { 'Untrashing the Privacy Policy page should preserve wp_page_for_privacy_policy.' ); } - }