Make WordPress Core

Opened 5 weeks ago

#61996 new defect (bug)

Slug Conflict When a Published and Draft Page Share post_name

Reported by: brookedot's profile brookedot Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Posts, Post Types Keywords:
Focuses: Cc:

Description

It is possible (although it takes some work) to have a published page and a draft page that shares the same post_name. When this occurs if the draft page has a lower ID then it is retrieved by get_page_by_path in place of the published page. This is due at least in part to get_page_by_path not checking the page status and .

  1. Ensure permalinks are anything other than Plain
  2. Publish three nested pages, the first page here is needed to avoid a slug conflict when you later create pages with the same name. For example, to prevent parent from becoming parent-2. The posts must be published so they can be selected as parent pages. Consider setting the page content to "this is draft" so it's easy to tell these apart later.
  • Placeholder (slug placeholder)
    • Parent (slug /placehoder/parent
      • Child ( slug placeholder/parent/child
  1. Using the Bulk Action, set all three newly created published posts to draft. There may be other ways to do this, as well.
  1. Publish two addition new pages with the same slugs as parent and child
  • Parent ( slug parent )
    • Child (slug parent/child)
  1. Using Quick Edit remove the parent from the draft page parent ( /placeholder/parent) by setting its parent page to `Main (no parent) keeping it as a draft.

You should now have parent and parent/child as drafts alongside your other two published pages.

  1. Ensure you don't have a -2 in either parent-2 or child-2
  1. If all goes well, when you visit parent/child you will now see a 404 instead of the pages you published in step 4.

There is a screen recording of these steps here:
https://cloudup.com/cxkwjscxDfB

They can be replicated in Multisite, Single site, and WordPress playground.

Some insight into what is happening is when you return the array used to get_page_by_path. For WordPress 6.6.1 that is https://github.com/WordPress/WordPress/blob/6.6.1/wp-includes/post.php#L5881

Using var_dump to return the array:

<?php
        $sql                 = "
                SELECT ID, post_name, post_parent, post_type
                FROM $wpdb->posts
                WHERE post_name IN ($in_string)
                AND post_type IN ($post_type_in_string)
        ";

        $pages = $wpdb->get_results( $sql, ARRAY_A );

        $revparts = array_reverse( $parts );
        var_dump( $pages );

This returns post ID 63 first which is our draft child page thus a 404 on the front end. ID 69 is the published child page which is never displayed on the front end as ID 63 is returned first.

Here is the output of the above var_dump()

<?php
array (size=4)
  0 => 
    array (size=4)
      'ID' => string '63' (length=2)
      'post_name' => string 'child' (length=5)
      'post_parent' => string '61' (length=2)
      'post_type' => string 'page' (length=4)
  1 => 
    array (size=4)
      'ID' => string '69' (length=2)
      'post_name' => string 'child' (length=5)
      'post_parent' => string '65' (length=2)
      'post_type' => string 'page' (length=4)
  2 => 
    array (size=4)
      'ID' => string '61' (length=2)
      'post_name' => string 'parent' (length=6)
      'post_parent' => string '0' (length=1)
      'post_type' => string 'page' (length=4)
  3 => 
    array (size=4)
      'ID' => string '65' (length=2)
      'post_name' => string 'parent' (length=6)
      'post_parent' => string '0' (length=1)
      'post_type' => string 'page' (length=4)

A few other related trac issues were found but nothing that matched this exactly. Please update the Component to match the closest, I almost set this as permalinks

Related to #13459 (but not a duplicate as this is the same post type)

Props @trepmal who was instrumental in debugging and reporting this bug.

Change History (0)

Note: See TracTickets for help on using tickets.