From 7f99ab584365d6db899d7db13c7d3588af8e0911 Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Wed, 5 Feb 2025 15:44:52 +0530 Subject: [PATCH 1/3] Fix duplicate slugs between posts and pages and added related tests --- src/wp-includes/post.php | 12 +- tests/phpunit/tests/post/wpUniquePostSlug.php | 124 ++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index a78e97b6c1dd9..7037da0950694 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5447,7 +5447,11 @@ function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_p * Page slugs must be unique within their own trees. Pages are in a separate * namespace than posts so page slugs are allowed to overlap post slugs. */ - $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + if ( 'page' === $post_type ) { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'post', 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + } else { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + } $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_id, $post_parent ) ); /** @@ -5477,7 +5481,11 @@ function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_p } } else { // Post slugs must be unique across all posts. - $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; + if ( 'post' === $post_type ) { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN( %s, 'page' ) AND ID != %d LIMIT 1"; + } else { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; + } $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_id ) ); $post = get_post( $post_id ); diff --git a/tests/phpunit/tests/post/wpUniquePostSlug.php b/tests/phpunit/tests/post/wpUniquePostSlug.php index f242159987e3f..001ab8e1a4a24 100644 --- a/tests/phpunit/tests/post/wpUniquePostSlug.php +++ b/tests/phpunit/tests/post/wpUniquePostSlug.php @@ -380,4 +380,128 @@ public function test_embed_slug_should_be_suffixed_for_attachments() { $found = wp_unique_post_slug( 'embed', $p, 'publish', 'attachment', 0 ); $this->assertSame( 'embed-2', $found ); } + + /** + * Test that a post cannot use an existing page's slug. + * + * @ticket 13459 + */ + public function test_unique_slug_page_before_post() { + $this->set_permalink_structure( '/%postname%/' ); + + $page = self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_title' => 'Test Page', + 'post_name' => 'test-slug', + ) + ); + + $post = self::factory()->post->create( + array( + 'post_type' => 'post', + 'post_title' => 'Test Post', + 'post_name' => 'test-slug', + ) + ); + + $page_obj = get_post( $page ); + $post_obj = get_post( $post ); + + $this->assertSame( 'test-slug', $page_obj->post_name ); + $this->assertSame( 'test-slug-2', $post_obj->post_name ); + } + + /** + * Test that a page cannot use an existing post's slug. + * + * @ticket 13459 + */ + public function test_unique_slug_post_before_page() { + $this->set_permalink_structure( '/%postname%/' ); + + $post = self::factory()->post->create( + array( + 'post_type' => 'post', + 'post_title' => 'Test Post', + 'post_name' => 'test-slug', + ) + ); + + $page = self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_title' => 'Test Page', + 'post_name' => 'test-slug', + ) + ); + + $post_obj = get_post( $post ); + $page_obj = get_post( $page ); + + $this->assertSame( 'test-slug', $post_obj->post_name ); + $this->assertSame( 'test-slug-2', $page_obj->post_name ); + } + + /** + * Test that two posts cannot share the same slug. + * + * @ticket 13459 + */ + public function test_unique_slug_post_before_post() { + $this->set_permalink_structure( '/%postname%/' ); + + $post1 = self::factory()->post->create( + array( + 'post_type' => 'post', + 'post_title' => 'First Post', + 'post_name' => 'test-slug', + ) + ); + + $post2 = self::factory()->post->create( + array( + 'post_type' => 'post', + 'post_title' => 'Second Post', + 'post_name' => 'test-slug', + ) + ); + + $post1_obj = get_post( $post1 ); + $post2_obj = get_post( $post2 ); + + $this->assertSame( 'test-slug', $post1_obj->post_name ); + $this->assertSame( 'test-slug-2', $post2_obj->post_name ); + } + + /** + * Test that two pages cannot share the same slug. + * + * @ticket 13459 + */ + public function test_unique_slug_page_before_page() { + $this->set_permalink_structure( '/%postname%/' ); + + $page1 = self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_title' => 'First Page', + 'post_name' => 'test-slug', + ) + ); + + $page2 = self::factory()->post->create( + array( + 'post_type' => 'page', + 'post_title' => 'Second Page', + 'post_name' => 'test-slug', + ) + ); + + $page1_obj = get_post( $page1 ); + $page2_obj = get_post( $page2 ); + + $this->assertSame( 'test-slug', $page1_obj->post_name ); + $this->assertSame( 'test-slug-2', $page2_obj->post_name ); + } } From a91f5aff8197bc773dbc21621fdd1b85bc4be929 Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Thu, 6 Feb 2025 10:54:12 +0530 Subject: [PATCH 2/3] Update tests to use dataProvider --- tests/phpunit/tests/post/wpUniquePostSlug.php | 156 +++++++----------- 1 file changed, 59 insertions(+), 97 deletions(-) diff --git a/tests/phpunit/tests/post/wpUniquePostSlug.php b/tests/phpunit/tests/post/wpUniquePostSlug.php index 001ab8e1a4a24..59f79415d6f9d 100644 --- a/tests/phpunit/tests/post/wpUniquePostSlug.php +++ b/tests/phpunit/tests/post/wpUniquePostSlug.php @@ -382,126 +382,88 @@ public function test_embed_slug_should_be_suffixed_for_attachments() { } /** - * Test that a post cannot use an existing page's slug. + * Data provider for testing unique slug constraints. * - * @ticket 13459 + * @return array[] Test data. */ - public function test_unique_slug_page_before_post() { - $this->set_permalink_structure( '/%postname%/' ); - - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'Test Page', - 'post_name' => 'test-slug', - ) - ); - - $post = self::factory()->post->create( - array( - 'post_type' => 'post', - 'post_title' => 'Test Post', - 'post_name' => 'test-slug', - ) + public static function unique_slug_test_cases() { + return array( + 'page_before_post' => array( + 'first' => array( + 'type' => 'page', + 'title' => 'Test Page', + ), + 'second' => array( + 'type' => 'post', + 'title' => 'Test Post', + ), + ), + 'post_before_page' => array( + 'first' => array( + 'type' => 'post', + 'title' => 'Test Post', + ), + 'second' => array( + 'type' => 'page', + 'title' => 'Test Page', + ), + ), + 'post_before_post' => array( + 'first' => array( + 'type' => 'post', + 'title' => 'First Post', + ), + 'second' => array( + 'type' => 'post', + 'title' => 'Second Post', + ), + ), + 'page_before_page' => array( + 'first' => array( + 'type' => 'page', + 'title' => 'First Page', + ), + 'second' => array( + 'type' => 'page', + 'title' => 'Second Page', + ), + ), ); - - $page_obj = get_post( $page ); - $post_obj = get_post( $post ); - - $this->assertSame( 'test-slug', $page_obj->post_name ); - $this->assertSame( 'test-slug-2', $post_obj->post_name ); } /** - * Test that a page cannot use an existing post's slug. + * Test that posts and pages maintain unique slugs. * * @ticket 13459 - */ - public function test_unique_slug_post_before_page() { - $this->set_permalink_structure( '/%postname%/' ); - - $post = self::factory()->post->create( - array( - 'post_type' => 'post', - 'post_title' => 'Test Post', - 'post_name' => 'test-slug', - ) - ); - - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'Test Page', - 'post_name' => 'test-slug', - ) - ); - - $post_obj = get_post( $post ); - $page_obj = get_post( $page ); - - $this->assertSame( 'test-slug', $post_obj->post_name ); - $this->assertSame( 'test-slug-2', $page_obj->post_name ); - } - - /** - * Test that two posts cannot share the same slug. * - * @ticket 13459 - */ - public function test_unique_slug_post_before_post() { - $this->set_permalink_structure( '/%postname%/' ); - - $post1 = self::factory()->post->create( - array( - 'post_type' => 'post', - 'post_title' => 'First Post', - 'post_name' => 'test-slug', - ) - ); - - $post2 = self::factory()->post->create( - array( - 'post_type' => 'post', - 'post_title' => 'Second Post', - 'post_name' => 'test-slug', - ) - ); - - $post1_obj = get_post( $post1 ); - $post2_obj = get_post( $post2 ); - - $this->assertSame( 'test-slug', $post1_obj->post_name ); - $this->assertSame( 'test-slug-2', $post2_obj->post_name ); - } - - /** - * Test that two pages cannot share the same slug. + * @dataProvider unique_slug_test_cases * - * @ticket 13459 + * @param array $first_item First post/page to create. + * @param array $second_item Second post/page to create. */ - public function test_unique_slug_page_before_page() { + public function test_unique_slugs( $first_item, $second_item ) { $this->set_permalink_structure( '/%postname%/' ); - $page1 = self::factory()->post->create( + $first = self::factory()->post->create( array( - 'post_type' => 'page', - 'post_title' => 'First Page', + 'post_type' => $first_item['type'], + 'post_title' => $first_item['title'], 'post_name' => 'test-slug', ) ); - $page2 = self::factory()->post->create( + $second = self::factory()->post->create( array( - 'post_type' => 'page', - 'post_title' => 'Second Page', + 'post_type' => $second_item['type'], + 'post_title' => $second_item['title'], 'post_name' => 'test-slug', ) ); - $page1_obj = get_post( $page1 ); - $page2_obj = get_post( $page2 ); + $first_obj = get_post( $first ); + $second_obj = get_post( $second ); - $this->assertSame( 'test-slug', $page1_obj->post_name ); - $this->assertSame( 'test-slug-2', $page2_obj->post_name ); + $this->assertSame( 'test-slug', $first_obj->post_name ); + $this->assertSame( 'test-slug-2', $second_obj->post_name ); } } From ded6eff9b2edb2e4680f4b80a1259714171164c6 Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Fri, 7 Feb 2025 12:32:51 +0530 Subject: [PATCH 3/3] Update checking condition and tests: --- src/wp-includes/post.php | 14 +++- tests/phpunit/tests/post/wpUniquePostSlug.php | 74 +++++++++---------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 7037da0950694..243e7f5ce76fa 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5411,6 +5411,8 @@ function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_p $feeds = array(); } + $permalink_structure = get_option( 'permalink_structure' ); + if ( 'attachment' === $post_type ) { // Attachment slugs must be unique across all types. $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1"; @@ -5448,7 +5450,11 @@ function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_p * namespace than posts so page slugs are allowed to overlap post slugs. */ if ( 'page' === $post_type ) { - $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'post', 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + if ( 0 === $post_parent && '/%postname%/' === $permalink_structure ) { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'post', 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + } else { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; + } } else { $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; } @@ -5482,7 +5488,11 @@ function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_p } else { // Post slugs must be unique across all posts. if ( 'post' === $post_type ) { - $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN( %s, 'page' ) AND ID != %d LIMIT 1"; + if ( '/%postname%/' === $permalink_structure ) { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ((post_type = %s) OR (post_type = 'page' AND post_parent = 0)) AND ID != %d LIMIT 1"; + } else { + $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; + } } else { $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; } diff --git a/tests/phpunit/tests/post/wpUniquePostSlug.php b/tests/phpunit/tests/post/wpUniquePostSlug.php index 59f79415d6f9d..9dca92b740270 100644 --- a/tests/phpunit/tests/post/wpUniquePostSlug.php +++ b/tests/phpunit/tests/post/wpUniquePostSlug.php @@ -381,12 +381,48 @@ public function test_embed_slug_should_be_suffixed_for_attachments() { $this->assertSame( 'embed-2', $found ); } + /** + * Test that posts and pages maintain unique slugs. + * + * @ticket 13459 + * + * @dataProvider data_unique_slugs + * + * @param array $first_item First post/page to create. + * @param array $second_item Second post/page to create. + */ + public function test_unique_slugs( $first_item, $second_item ) { + $this->set_permalink_structure( '/%postname%/' ); + + $first = self::factory()->post->create( + array( + 'post_type' => $first_item['type'], + 'post_title' => $first_item['title'], + 'post_name' => 'test-slug', + ) + ); + + $second = self::factory()->post->create( + array( + 'post_type' => $second_item['type'], + 'post_title' => $second_item['title'], + 'post_name' => 'test-slug', + ) + ); + + $first_obj = get_post( $first ); + $second_obj = get_post( $second ); + + $this->assertSame( 'test-slug', $first_obj->post_name ); + $this->assertSame( 'test-slug-2', $second_obj->post_name ); + } + /** * Data provider for testing unique slug constraints. * * @return array[] Test data. */ - public static function unique_slug_test_cases() { + public static function data_unique_slugs() { return array( 'page_before_post' => array( 'first' => array( @@ -430,40 +466,4 @@ public static function unique_slug_test_cases() { ), ); } - - /** - * Test that posts and pages maintain unique slugs. - * - * @ticket 13459 - * - * @dataProvider unique_slug_test_cases - * - * @param array $first_item First post/page to create. - * @param array $second_item Second post/page to create. - */ - public function test_unique_slugs( $first_item, $second_item ) { - $this->set_permalink_structure( '/%postname%/' ); - - $first = self::factory()->post->create( - array( - 'post_type' => $first_item['type'], - 'post_title' => $first_item['title'], - 'post_name' => 'test-slug', - ) - ); - - $second = self::factory()->post->create( - array( - 'post_type' => $second_item['type'], - 'post_title' => $second_item['title'], - 'post_name' => 'test-slug', - ) - ); - - $first_obj = get_post( $first ); - $second_obj = get_post( $second ); - - $this->assertSame( 'test-slug', $first_obj->post_name ); - $this->assertSame( 'test-slug-2', $second_obj->post_name ); - } }