WordPress.org

Make WordPress Core

Ticket #5272: 5272.diff

File 5272.diff, 48.7 KB (added by peterwilsoncc, 8 months ago)
  • src/wp-includes/canonical.php

    diff --git a/src/wp-includes/canonical.php b/src/wp-includes/canonical.php
    index 617bcb352d..f7c5caa889 100644
    a b function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    7777
    7878        $redirect     = $original;
    7979        $redirect_url = false;
     80        $redirect_obj = false;
    8081
    8182        // Notice fixing.
    8283        if ( ! isset( $redirect['path'] ) ) {
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    102103
    103104        if ( is_feed() && $post_id ) {
    104105                $redirect_url = get_post_comments_feed_link( $post_id, get_query_var( 'feed' ) );
     106                $redirect_obj = get_post( $post_id );
    105107
    106108                if ( $redirect_url ) {
    107109                        $redirect['query'] = _remove_qs_args_if_not_in_url(
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    126128                        }
    127129
    128130                        $redirect_url = get_permalink( $post_id );
     131                        $redirect_obj = get_post( $post_id );
    129132
    130133                        if ( $redirect_url ) {
    131134                                $redirect['query'] = _remove_qs_args_if_not_in_url(
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    150153
    151154                        if ( $post_type_obj && $post_type_obj->public && 'auto-draft' !== $redirect_post->post_status ) {
    152155                                $redirect_url = get_permalink( $redirect_post );
     156                                $redirect_obj = get_post( $redirect_post );
    153157
    154158                                $redirect['query'] = _remove_qs_args_if_not_in_url(
    155159                                        $redirect['query'],
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    197201
    198202                        if ( $post_id ) {
    199203                                $redirect_url = get_permalink( $post_id );
     204                                $redirect_obj = get_post( $post_id );
    200205
    201206                                $redirect['path']  = rtrim( $redirect['path'], (int) get_query_var( 'page' ) . '/' );
    202207                                $redirect['query'] = remove_query_arg( 'page', $redirect['query'] );
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    223228                ) {
    224229                        if ( ! empty( $_GET['attachment_id'] ) ) {
    225230                                $redirect_url = get_attachment_link( get_query_var( 'attachment_id' ) );
     231                                $redirect_obj = get_post( get_query_var( 'attachment_id' ) );
    226232
    227233                                if ( $redirect_url ) {
    228234                                        $redirect['query'] = remove_query_arg( 'attachment_id', $redirect['query'] );
    229235                                }
    230236                        } else {
    231237                                $redirect_url = get_attachment_link();
     238                                $redirect_obj = get_post();
    232239                        }
    233240                } elseif ( is_single() && ! empty( $_GET['p'] ) && ! $redirect_url ) {
    234241                        $redirect_url = get_permalink( get_query_var( 'p' ) );
     242                        $redirect_obj = get_post( get_query_var( 'p' ) );
    235243
    236244                        if ( $redirect_url ) {
    237245                                $redirect['query'] = remove_query_arg( array( 'p', 'post_type' ), $redirect['query'] );
    238246                        }
    239247                } elseif ( is_single() && ! empty( $_GET['name'] ) && ! $redirect_url ) {
    240248                        $redirect_url = get_permalink( $wp_query->get_queried_object_id() );
     249                        $redirect_obj = get_post( $wp_query->get_queried_object_id() );
    241250
    242251                        if ( $redirect_url ) {
    243252                                $redirect['query'] = remove_query_arg( 'name', $redirect['query'] );
    244253                        }
    245254                } elseif ( is_page() && ! empty( $_GET['page_id'] ) && ! $redirect_url ) {
    246255                        $redirect_url = get_permalink( get_query_var( 'page_id' ) );
     256                        $redirect_obj = get_post( get_query_var( 'page_id' ) );
    247257
    248258                        if ( $redirect_url ) {
    249259                                $redirect['query'] = remove_query_arg( 'page_id', $redirect['query'] );
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    256266                        && 'page' === get_option( 'show_on_front' ) && get_query_var( 'page_id' ) === (int) get_option( 'page_for_posts' )
    257267                ) {
    258268                        $redirect_url = get_permalink( get_option( 'page_for_posts' ) );
     269                        $redirect_obj = get_post( get_option( 'page_for_posts' ) );
    259270
    260271                        if ( $redirect_url ) {
    261272                                $redirect['query'] = remove_query_arg( 'page_id', $redirect['query'] );
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    310321                                && $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE $wpdb->posts.post_author = %d AND $wpdb->posts.post_status = 'publish' LIMIT 1", $author->ID ) )
    311322                        ) {
    312323                                $redirect_url = get_author_posts_url( $author->ID, $author->user_nicename );
     324                                $redirect_obj = $author;
    313325
    314326                                if ( $redirect_url ) {
    315327                                        $redirect['query'] = remove_query_arg( 'author', $redirect['query'] );
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    385397                                        || ! has_term( $category->term_id, 'category', $wp_query->get_queried_object_id() )
    386398                                ) {
    387399                                        $redirect_url = get_permalink( $wp_query->get_queried_object_id() );
     400                                        $redirect_obj = get_post( $wp_query->get_queried_object_id() );
    388401                                }
    389402                        }
    390403                }
    function redirect_canonical( $requested_url = null, $do_redirect = true ) { 
    395408
    396409                        if ( ! $redirect_url ) {
    397410                                $redirect_url = get_permalink( get_queried_object_id() );
     411                                $redirect_obj = get_post( get_queried_object_id() );
    398412                        }
    399413
    400414                        if ( $page > 1 ) {
    function lowercase_octets( $matches ) { 
    740754                $requested_url = preg_replace_callback( '|%[a-fA-F0-9][a-fA-F0-9]|', 'lowercase_octets', $requested_url );
    741755        }
    742756
     757        if ( $redirect_obj instanceof WP_Post ) {
     758                $post_status_obj = get_post_status_object( get_post_status( $redirect_obj ) );
     759                /*
     760                 * Unset the redirect object and URL if they are not readable by the user.
     761                 * This condition is a little confusing as the condition needs to pass if
     762                 * the post is not readable by the user. That's why there are ! (not) conditions
     763                 * rather throughout.
     764                 */
     765                if (
     766                        // Private post statuses only redirect if the user can read them.
     767                        ! (
     768                                $post_status_obj->private &&
     769                                current_user_can( 'read_post', $redirect_obj->ID )
     770                        ) &&
     771                        // For other posts, only redirect if publicly viewable.
     772                        ! is_post_publicly_viewable( $redirect_obj )
     773                ) {
     774                        $redirect_obj = false;
     775                        $redirect_url = false;
     776                }
     777        }
     778
    743779        /**
    744780         * Filters the canonical redirect URL.
    745781         *
  • src/wp-includes/capabilities.php

    diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php
    index 22eff2b1e3..ef61d3e0cb 100644
    a b function map_meta_cap( $cap, $user_id, ...$args ) { 
    239239                                break;
    240240                        }
    241241
    242                         $status_obj = get_post_status_object( $post->post_status );
     242                        $status_obj = get_post_status_object( get_post_status( $post ) );
    243243                        if ( ! $status_obj ) {
    244244                                /* translators: 1: Post status, 2: Capability name. */
    245                                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), $post->post_status, $cap ), '5.4.0' );
     245                                _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), get_post_status( $post ), $cap ), '5.4.0' );
    246246                                $caps[] = 'edit_others_posts';
    247247                                break;
    248248                        }
  • src/wp-includes/link-template.php

    diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php
    index 28e6f98ec7..b5e5064d17 100644
    a b function permalink_anchor( $mode = 'id' ) { 
    8989        }
    9090}
    9191
     92/**
     93 * Determine whether post should always use an ugly permalink structure.
     94 *
     95 * @since 5.7.0
     96 *
     97 * @param WP_Post|int|null $post   Optional. Post ID or post object. Defaults to global $post.
     98 * @param bool             $sample Optional. Whether to force consideration based on sample links.
     99 * @return bool Whether to use an ugly permalink structure.
     100 */
     101function wp_force_ugly_post_permalink( $post = null, $sample = null ) {
     102        if (
     103                null === $sample &&
     104                is_object( $post ) &&
     105                isset( $post->filter ) &&
     106                'sample' === $post->filter
     107        ) {
     108                $sample = true;
     109        } else {
     110                $post   = get_post( $post );
     111                $sample = null !== $sample ? $sample : false;
     112        }
     113
     114        if ( ! $post ) {
     115                return true;
     116        }
     117
     118        $post_status_obj = get_post_status_object( get_post_status( $post ) );
     119        $post_type_obj   = get_post_type_object( get_post_type( $post ) );
     120
     121        if ( ! $post_status_obj || ! $post_type_obj ) {
     122                return true;
     123        }
     124
     125        if (
     126                // Publicly viewable links never have ugly permalinks.
     127                is_post_status_viewable( $post_status_obj ) ||
     128                (
     129                        // Private posts don't have ugly links if the user can read them.
     130                        $post_status_obj->private &&
     131                        current_user_can( 'read_post', $post->ID )
     132                ) ||
     133                // Protected posts don't have ugly links if getting a sample URL.
     134                ( $post_status_obj->protected && $sample )
     135        ) {
     136                return false;
     137        }
     138
     139        return true;
     140}
     141
    92142/**
    93143 * Retrieves the full permalink for the current post or post ID.
    94144 *
    function get_permalink( $post = 0, $leavename = false ) { 
    166216
    167217        if (
    168218                $permalink &&
    169                 ! in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft', 'future', 'trash' ), true )
     219                ! wp_force_ugly_post_permalink( $post )
    170220        ) {
    171221
    172222                $category = '';
    function get_post_permalink( $id = 0, $leavename = false, $sample = false ) { 
    277327
    278328        $slug = $post->post_name;
    279329
    280         $draft_or_pending = get_post_status( $post ) && in_array( get_post_status( $post ), array( 'draft', 'pending', 'auto-draft', 'future' ), true );
     330        $force_ugly_link = wp_force_ugly_post_permalink( $post );
    281331
    282332        $post_type = get_post_type_object( $post->post_type );
    283333
    function get_post_permalink( $id = 0, $leavename = false, $sample = false ) { 
    285335                $slug = get_page_uri( $post );
    286336        }
    287337
    288         if ( ! empty( $post_link ) && ( ! $draft_or_pending || $sample ) ) {
     338        if ( ! empty( $post_link ) && ( ! $force_ugly_link || $sample ) ) {
    289339                if ( ! $leavename ) {
    290340                        $post_link = str_replace( "%$post->post_type%", $slug, $post_link );
    291341                }
    292342                $post_link = home_url( user_trailingslashit( $post_link ) );
    293343        } else {
    294                 if ( $post_type->query_var && ( isset( $post->post_status ) && ! $draft_or_pending ) ) {
     344                if ( $post_type->query_var && ( isset( $post->post_status ) && ! $force_ugly_link ) ) {
    295345                        $post_link = add_query_arg( $post_type->query_var, $slug, '' );
    296346                } else {
    297347                        $post_link = add_query_arg(
    function _get_page_link( $post = false, $leavename = false, $sample = false ) { 
    373423
    374424        $post = get_post( $post );
    375425
    376         $draft_or_pending = in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ), true );
     426        $force_ugly_link = wp_force_ugly_post_permalink( $post );
    377427
    378428        $link = $wp_rewrite->get_page_permastruct();
    379429
    380         if ( ! empty( $link ) && ( ( isset( $post->post_status ) && ! $draft_or_pending ) || $sample ) ) {
     430        if ( ! empty( $link ) && ( ( isset( $post->post_status ) && ! $force_ugly_link ) || $sample ) ) {
    381431                if ( ! $leavename ) {
    382432                        $link = str_replace( '%pagename%', get_page_uri( $post ), $link );
    383433                }
    function get_attachment_link( $post = null, $leavename = false ) { 
    417467
    418468        $link = false;
    419469
    420         $post   = get_post( $post );
    421         $parent = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false;
    422         if ( $parent && ! in_array( $parent->post_type, get_post_types(), true ) ) {
    423                 $parent = false;
     470        $post            = get_post( $post );
     471        $force_ugly_link = wp_force_ugly_post_permalink( $post );
     472        $parent_id       = $post->post_parent;
     473        $parent          = $parent_id ? get_post( $parent_id ) : false;
     474        $parent_valid    = true; // Default for no parent.
     475        if (
     476                $parent_id &&
     477                (
     478                        $post->post_parent === $post->ID ||
     479                        ! $parent ||
     480                        ! is_post_type_viewable( get_post_type( $parent ) )
     481                )
     482        ) {
     483                // Post is either its own parent or parent post unavailable.
     484                $parent_valid = false;
    424485        }
    425486
    426         if ( $wp_rewrite->using_permalinks() && $parent ) {
     487        if ( $force_ugly_link || ! $parent_valid ) {
     488                $link = false;
     489        } elseif ( $wp_rewrite->using_permalinks() && $parent ) {
    427490                if ( 'page' === $parent->post_type ) {
    428491                        $parentlink = _get_page_link( $post->post_parent ); // Ignores page_on_front.
    429492                } else {
  • src/wp-includes/post.php

    diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php
    index f04900aed3..a2be7ce8ae 100644
    a b function is_post_type_viewable( $post_type ) { 
    20162016                }
    20172017        }
    20182018
     2019        if ( ! is_object( $post_type ) ) {
     2020                return false;
     2021        }
     2022
    20192023        return $post_type->publicly_queryable || ( $post_type->_builtin && $post_type->public );
    20202024}
    20212025
     2026/**
     2027 * Determine whether a post status is considered "viewable".
     2028 *
     2029 * For built-in post statuses such as publish and private, the 'public' value will be evaluted.
     2030 * For all others, the 'publicly_queryable' value will be used.
     2031 *
     2032 * @since 5.7.0
     2033 *
     2034 * @param string|stdClass $post_status Post status name or object.
     2035 * @return bool Whether the post status should be considered viewable.
     2036 */
     2037function is_post_status_viewable( $post_status ) {
     2038        if ( is_scalar( $post_status ) ) {
     2039                $post_status = get_post_status_object( $post_status );
     2040                if ( ! $post_status ) {
     2041                        return false;
     2042                }
     2043        }
     2044
     2045        if (
     2046                ! is_object( $post_status ) ||
     2047                $post_status->internal ||
     2048                $post_status->protected
     2049        ) {
     2050                return false;
     2051        }
     2052
     2053        return $post_status->publicly_queryable || ( $post_status->_builtin && $post_status->public );
     2054}
     2055
     2056/**
     2057 * Determine whether a post is publicly viewable.
     2058 *
     2059 * Posts are considered publicly viewable if both the post status and post type
     2060 * are viewable.
     2061 *
     2062 * @since 5.7.0
     2063 *
     2064 * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post.
     2065 * @return bool Whether the post is publicly viewable.
     2066 */
     2067function is_post_publicly_viewable( $post = null ) {
     2068        $post = get_post( $post );
     2069
     2070        if ( ! $post ) {
     2071                return false;
     2072        }
     2073
     2074        $post_type   = get_post_type( $post );
     2075        $post_status = get_post_status( $post );
     2076
     2077        return is_post_type_viewable( $post_type ) && is_post_status_viewable( $post_status );
     2078}
     2079
    20222080/**
    20232081 * Retrieves an array of the latest posts, or posts matching the given criteria.
    20242082 *
  • new file tests/phpunit/tests/canonical/postStatus.php

    diff --git a/tests/phpunit/tests/canonical/postStatus.php b/tests/phpunit/tests/canonical/postStatus.php
    new file mode 100644
    index 0000000000..e189a72f1c
    - +  
     1<?php
     2
     3/**
     4 * @group canonical
     5 * @group rewrite
     6 * @group query
     7 */
     8class Tests_Canonical_PostStatus extends WP_Canonical_UnitTestCase {
     9
     10        /**
     11         * User IDs.
     12         *
     13         * @var array
     14         */
     15        public static $users;
     16
     17        /**
     18         * Post Objects.
     19         *
     20         * @var array
     21         */
     22        public static $posts;
     23
     24        public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
     25                self::setup_custom_types();
     26                self::$users = array(
     27                        'anon'           => 0,
     28                        'subscriber'     => $factory->user->create( array( 'role' => 'subscriber' ) ),
     29                        'content_author' => $factory->user->create( array( 'role' => 'author' ) ),
     30                        'editor'         => $factory->user->create( array( 'role' => 'editor' ) ),
     31                );
     32
     33                $post_statuses = array( 'publish', 'future', 'draft', 'pending', 'private', 'auto-draft', 'a-private-status' );
     34                foreach ( $post_statuses as $post_status ) {
     35                        $post_date = '';
     36                        if ( 'future' === $post_status ) {
     37                                $post_date = strftime( '%Y-%m-%d %H:%M:%S', strtotime( '+1 year' ) );
     38                        }
     39
     40                        self::$posts[ $post_status ] = $factory->post->create_and_get(
     41                                array(
     42                                        'post_type'    => 'post',
     43                                        'post_title'   => "$post_status post",
     44                                        'post_name'    => "$post_status-post",
     45                                        'post_status'  => $post_status,
     46                                        'post_content' => "Prevent canonical redirect exposing post slugs.\n\n<!--nextpage-->Page 2",
     47                                        'post_author'  => self::$users['content_author'],
     48                                        'post_date'    => $post_date,
     49                                )
     50                        );
     51
     52                        // Add fake attachment to the post (file upload not needed).
     53                        self::$posts[ "$post_status-attachment" ] = $factory->post->create_and_get(
     54                                array(
     55                                        'post_type'    => 'attachment',
     56                                        'post_title'   => "$post_status inherited attachment",
     57                                        'post_name'    => "$post_status-inherited-attachment",
     58                                        'post_status'  => 'inherit',
     59                                        'post_content' => "Prevent canonical redirect exposing post via attachments.\n\n<!--nextpage-->Page 2",
     60                                        'post_author'  => self::$users['content_author'],
     61                                        'post_parent'  => self::$posts[ $post_status ]->ID,
     62                                        'post_date'    => $post_date,
     63                                )
     64                        );
     65
     66                        // Set up a page with same.
     67                        self::$posts[ "$post_status-page" ] = $factory->post->create_and_get(
     68                                array(
     69                                        'post_type'    => 'page',
     70                                        'post_title'   => "$post_status page",
     71                                        'post_name'    => "$post_status-page",
     72                                        'post_status'  => $post_status,
     73                                        'post_content' => "Prevent canonical redirect exposing page slugs.\n\n<!--nextpage-->Page 2",
     74                                        'post_author'  => self::$users['content_author'],
     75                                        'post_date'    => $post_date,
     76                                )
     77                        );
     78                }
     79
     80                // Create a public CPT using a private status.
     81                self::$posts['a-public-cpt'] = $factory->post->create_and_get(
     82                        array(
     83                                'post_type'    => 'a-public-cpt',
     84                                'post_title'   => 'a-public-cpt',
     85                                'post_name'    => 'a-public-cpt',
     86                                'post_status'  => 'private',
     87                                'post_content' => 'Prevent canonical redirect exposing a-public-cpt titles.',
     88                                'post_author'  => self::$users['content_author'],
     89                        )
     90                );
     91
     92                // Add fake attachment to the public cpt (file upload not needed).
     93                self::$posts['a-public-cpt-attachment'] = $factory->post->create_and_get(
     94                        array(
     95                                'post_type'    => 'attachment',
     96                                'post_title'   => 'a-public-cpt post inherited attachment',
     97                                'post_name'    => 'a-public-cpt-inherited-attachment',
     98                                'post_status'  => 'inherit',
     99                                'post_content' => "Prevent canonical redirect exposing post via attachments.\n\n<!--nextpage-->Page 2",
     100                                'post_author'  => self::$users['content_author'],
     101                                'post_parent'  => self::$posts['a-public-cpt']->ID,
     102                        )
     103                );
     104
     105                // Create a private CPT with a public status.
     106                self::$posts['a-private-cpt'] = $factory->post->create_and_get(
     107                        array(
     108                                'post_type'    => 'a-private-cpt',
     109                                'post_title'   => 'a-private-cpt',
     110                                'post_name'    => 'a-private-cpt',
     111                                'post_status'  => 'publish',
     112                                'post_content' => 'Prevent canonical redirect exposing a-private-cpt titles.',
     113                                'post_author'  => self::$users['content_author'],
     114                        )
     115                );
     116
     117                // Add fake attachment to the private cpt (file upload not needed).
     118                self::$posts['a-private-cpt-attachment'] = $factory->post->create_and_get(
     119                        array(
     120                                'post_type'    => 'attachment',
     121                                'post_title'   => 'a-private-cpt post inherited attachment',
     122                                'post_name'    => 'a-private-cpt-inherited-attachment',
     123                                'post_status'  => 'inherit',
     124                                'post_content' => "Prevent canonical redirect exposing post via attachments.\n\n<!--nextpage-->Page 2",
     125                                'post_author'  => self::$users['content_author'],
     126                                'post_parent'  => self::$posts['a-private-cpt']->ID,
     127                        )
     128                );
     129
     130                // Post for trashing.
     131                self::$posts['trash'] = $factory->post->create_and_get(
     132                        array(
     133                                'post_type'    => 'post',
     134                                'post_title'   => 'trash post',
     135                                'post_name'    => 'trash-post',
     136                                'post_status'  => 'publish',
     137                                'post_content' => "Prevent canonical redirect exposing post slugs.\n\n<!--nextpage-->Page 2",
     138                                'post_author'  => self::$users['content_author'],
     139                        )
     140                );
     141
     142                self::$posts['trash-attachment'] = $factory->post->create_and_get(
     143                        array(
     144                                'post_type'    => 'attachment',
     145                                'post_title'   => 'trash post inherited attachment',
     146                                'post_name'    => 'trash-post-inherited-attachment',
     147                                'post_status'  => 'inherit',
     148                                'post_content' => "Prevent canonical redirect exposing post via attachments.\n\n<!--nextpage-->Page 2",
     149                                'post_author'  => self::$users['content_author'],
     150                                'post_parent'  => self::$posts['trash']->ID,
     151                        )
     152                );
     153
     154                // Page for trashing.
     155                self::$posts['trash-page'] = $factory->post->create_and_get(
     156                        array(
     157                                'post_type'    => 'page',
     158                                'post_title'   => 'trash page',
     159                                'post_name'    => 'trash-page',
     160                                'post_status'  => 'publish',
     161                                'post_content' => "Prevent canonical redirect exposing page slugs.\n\n<!--nextpage-->Page 2",
     162                                'post_author'  => self::$users['content_author'],
     163                        )
     164                );
     165                wp_trash_post( self::$posts['trash']->ID );
     166                wp_trash_post( self::$posts['trash-page']->ID );
     167        }
     168
     169        function setUp() {
     170                parent::setUp();
     171                self::setup_custom_types();
     172        }
     173
     174        /**
     175         * Set up a custom post type and private status.
     176         *
     177         * This needs to be called both in the class setup and
     178         * test setup.
     179         */
     180        public static function setup_custom_types() {
     181                // Register public custom post type.
     182                register_post_type(
     183                        'a-public-cpt',
     184                        array(
     185                                'public'  => true,
     186                                'rewrite' => array(
     187                                        'slug' => 'a-public-cpt',
     188                                ),
     189                        )
     190                );
     191
     192                // Register private custom post type.
     193                register_post_type(
     194                        'a-private-cpt',
     195                        array(
     196                                'public'             => false,
     197                                'publicly_queryable' => false,
     198                                'rewrite'            => array(
     199                                        'slug' => 'a-private-cpt',
     200                                ),
     201                                'map_meta_cap'       => true,
     202                        )
     203                );
     204
     205                // Register custom private post status.
     206                register_post_status(
     207                        'a-private-status',
     208                        array(
     209                                'private' => true,
     210                        )
     211                );
     212        }
     213
     214        /**
     215         * Test canonical redirect does not reveal private posts presence.
     216         *
     217         * @ticket 5272
     218         * @dataProvider data_canonical_redirects_to_ugly_permalinks
     219         *
     220         * @param string $post_key  Post key used for creating fixtures.
     221         * @param string $user_role User role.
     222         * @param string $requested Requested URL.
     223         * @param string $expected  Expected URL.
     224         */
     225        public function test_canonical_redirects_to_ugly_permalinks( $post_key, $user_role, $requested, $expected ) {
     226                wp_set_current_user( self::$users[ $user_role ] );
     227                $this->set_permalink_structure( '' );
     228                $post = self::$posts[ $post_key ];
     229                clean_post_cache( $post->ID );
     230
     231                /*
     232                 * The dataProvider runs before the fixures are set up, therefore the
     233                 * post object IDs are placeholders that needs to be replaced.
     234                 */
     235                $requested = str_replace( '%ID%', $post->ID, $requested );
     236                $expected  = str_replace( '%ID%', $post->ID, $expected );
     237
     238                $this->assertCanonical( $requested, $expected );
     239        }
     240
     241        /**
     242         * Data provider for test_canonical_redirects_to_ugly_permalinks.
     243         *
     244         * @return array[] Array of arguments for tests {
     245         *     @type string $post_key  Post key used for creating fixtures.
     246         *     @type string $user_role User role.
     247         *     @type string $requested Requested URL.
     248         *     @type string $expected  Expected URL.
     249         * }
     250         */
     251        function data_canonical_redirects_to_ugly_permalinks() {
     252                $data              = array();
     253                $all_user_list     = array( 'anon', 'subscriber', 'content_author', 'editor' );
     254                $select_allow_list = array( 'content_author', 'editor' );
     255                $select_block_list = array( 'anon', 'subscriber' );
     256                // All post/page keys
     257                $all_user_post_status_keys    = array( 'publish' );
     258                $select_user_post_status_keys = array( 'private', 'a-private-status' );
     259                $no_user_post_status_keys     = array( 'future', 'draft', 'pending', 'auto-draft' ); // Excludes trash for attachment rules.
     260                $select_user_post_type_keys   = array( 'a-public-cpt' );
     261                $no_user_post_type_keys       = array( 'a-private-cpt' );
     262
     263                foreach ( $all_user_post_status_keys as $post_key ) {
     264                        foreach ( $all_user_list as $user ) {
     265                                /*
     266                                 * In the event `redirect_canonical()` is updated to redirect ugly permalinks
     267                                 * to a canonical ugly version, these expected values can be changed.
     268                                 */
     269                                $data[] = array(
     270                                        "$post_key-page",
     271                                        $user,
     272                                        '/?post_type=page&p=%ID%',
     273                                        '/?post_type=page&p=%ID%',
     274                                );
     275
     276                                $data[] = array(
     277                                        $post_key,
     278                                        $user,
     279                                        "/?name=$post_key-post",
     280                                        "/?name=$post_key-post",
     281                                );
     282
     283                                // Ensure rss redirects to rss2.
     284                                $data[] = array(
     285                                        $post_key,
     286                                        $user,
     287                                        '/?feed=rss&p=%ID%',
     288                                        '/?feed=rss2&p=%ID%',
     289                                );
     290
     291                                // Ensure rss redirects to rss2.
     292                                $data[] = array(
     293                                        "$post_key-page",
     294                                        $user,
     295                                        '/?feed=rss&page_id=%ID%',
     296                                        '/?feed=rss2&page_id=%ID%',
     297                                );
     298                        }
     299                }
     300
     301                foreach ( $select_user_post_status_keys as $post_key ) {
     302                        foreach ( $select_allow_list as $user ) {
     303                                /*
     304                                 * In the event `redirect_canonical()` is updated to redirect ugly permalinks
     305                                 * to a canonical ugly version, these expected values can be changed.
     306                                 */
     307                                $data[] = array(
     308                                        "$post_key-page",
     309                                        $user,
     310                                        '/?post_type=page&p=%ID%',
     311                                        '/?post_type=page&p=%ID%',
     312                                );
     313
     314                                $data[] = array(
     315                                        $post_key,
     316                                        $user,
     317                                        "/?name=$post_key-post",
     318                                        "/?name=$post_key-post",
     319                                );
     320
     321                                // Ensure rss redirects to rss2.
     322                                $data[] = array(
     323                                        $post_key,
     324                                        $user,
     325                                        '/?feed=rss&p=%ID%',
     326                                        '/?feed=rss2&p=%ID%',
     327                                );
     328
     329                                // Ensure rss redirects to rss2.
     330                                $data[] = array(
     331                                        "$post_key-page",
     332                                        $user,
     333                                        '/?feed=rss&page_id=%ID%',
     334                                        '/?feed=rss2&page_id=%ID%',
     335                                );
     336                        }
     337
     338                        foreach ( $select_block_list as $user ) {
     339                                /*
     340                                 * In the event `redirect_canonical()` is updated to redirect ugly permalinks
     341                                 * to a canonical ugly version, these expected values MUST NOT be changed.
     342                                 */
     343                                $data[] = array(
     344                                        "$post_key-page",
     345                                        $user,
     346                                        '/?post_type=page&p=%ID%',
     347                                        '/?post_type=page&p=%ID%',
     348                                );
     349
     350                                $data[] = array(
     351                                        $post_key,
     352                                        $user,
     353                                        "/?name=$post_key-post",
     354                                        "/?name=$post_key-post",
     355                                );
     356
     357                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     358                                $data[] = array(
     359                                        $post_key,
     360                                        $user,
     361                                        '/?feed=rss&p=%ID%',
     362                                        '/?feed=rss&p=%ID%',
     363                                );
     364
     365                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     366                                $data[] = array(
     367                                        "$post_key-page",
     368                                        $user,
     369                                        '/?feed=rss&page_id=%ID%',
     370                                        '/?feed=rss&page_id=%ID%',
     371                                );
     372                        }
     373                }
     374
     375                foreach ( $no_user_post_status_keys as $post_key ) {
     376                        foreach ( $all_user_list as $user ) {
     377                                /*
     378                                 * In the event `redirect_canonical()` is updated to redirect ugly permalinks
     379                                 * to a canonical ugly version, these expected values MUST NOT be changed.
     380                                 */
     381                                $data[] = array(
     382                                        "$post_key-page",
     383                                        $user,
     384                                        '/?post_type=page&p=%ID%',
     385                                        '/?post_type=page&p=%ID%',
     386                                );
     387
     388                                $data[] = array(
     389                                        $post_key,
     390                                        $user,
     391                                        "/?name=$post_key-post",
     392                                        "/?name=$post_key-post",
     393                                );
     394
     395                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     396                                $data[] = array(
     397                                        $post_key,
     398                                        $user,
     399                                        '/?feed=rss&p=%ID%',
     400                                        '/?feed=rss&p=%ID%',
     401                                );
     402
     403                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     404                                $data[] = array(
     405                                        "$post_key-page",
     406                                        $user,
     407                                        '/?feed=rss&page_id=%ID%',
     408                                        '/?feed=rss&page_id=%ID%',
     409                                );
     410                        }
     411                }
     412
     413                foreach ( array( 'trash' ) as $post_key ) {
     414                        foreach ( $all_user_list as $user ) {
     415                                /*
     416                                 * In the event `redirect_canonical()` is updated to redirect ugly permalinks
     417                                 * to a canonical ugly version, these expected values MUST NOT be changed.
     418                                 */
     419                                $data[] = array(
     420                                        "$post_key-page",
     421                                        $user,
     422                                        '/?post_type=page&p=%ID%',
     423                                        '/?post_type=page&p=%ID%',
     424                                );
     425
     426                                $data[] = array(
     427                                        $post_key,
     428                                        $user,
     429                                        "/?name=$post_key-post",
     430                                        "/?name=$post_key-post",
     431                                );
     432
     433                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     434                                $data[] = array(
     435                                        $post_key,
     436                                        $user,
     437                                        '/?feed=rss&p=%ID%',
     438                                        '/?feed=rss&p=%ID%',
     439                                );
     440
     441                                // Ensure post's existence is not demonstrated by changing rss to rss2.
     442                                $data[] = array(
     443                                        "$post_key-page",
     444                                        $user,
     445                                        '/?feed=rss&page_id=%ID%',
     446                                        '/?feed=rss&page_id=%ID%',
     447                                );
     448                        }
     449                }
     450
     451                foreach ( $select_user_post_type_keys as $post_key ) {
     452                        foreach ( $select_allow_list as $user ) {
     453                                $data[] = array(
     454                                        $post_key,
     455                                        $user,
     456                                        '/?p=%ID%',
     457                                        '/?a-public-cpt=a-public-cpt',
     458                                );
     459
     460                                $data[] = array(
     461                                        "$post_key-attachment",
     462                                        $user,
     463                                        '/?attachment_id=%ID%',
     464                                        '/?attachment_id=%ID%',
     465                                );
     466
     467                                $data[] = array(
     468                                        $post_key,
     469                                        $user,
     470                                        "/?name=$post_key&post_type=$post_key",
     471                                        "/?name=$post_key&post_type=$post_key",
     472                                );
     473
     474                                // Ensure rss is replaced by rss2.
     475                                $data[] = array(
     476                                        $post_key,
     477                                        $user,
     478                                        '/?feed=rss&p=%ID%',
     479                                        '/?a-public-cpt=a-public-cpt&feed=rss2',
     480                                );
     481                        }
     482
     483                        foreach ( $select_block_list as $user ) {
     484                                $data[] = array(
     485                                        $post_key,
     486                                        $user,
     487                                        '/?p=%ID%',
     488                                        '/?p=%ID%',
     489                                );
     490
     491                                $data[] = array(
     492                                        "$post_key-attachment",
     493                                        $user,
     494                                        '/?attachment_id=%ID%',
     495                                        '/?attachment_id=%ID%',
     496                                );
     497
     498                                $data[] = array(
     499                                        $post_key,
     500                                        $user,
     501                                        "/?name=$post_key&post_type=$post_key",
     502                                        "/?name=$post_key&post_type=$post_key",
     503                                );
     504
     505                                // Ensure rss is not replaced with rss2.
     506                                $data[] = array(
     507                                        $post_key,
     508                                        $user,
     509                                        '/?feed=rss&p=%ID%',
     510                                        '/?feed=rss&p=%ID%',
     511                                );
     512                        }
     513                }
     514
     515                foreach ( $no_user_post_type_keys as $post_key ) {
     516                        foreach ( $all_user_list as $user ) {
     517                                $data[] = array(
     518                                        $post_key,
     519                                        $user,
     520                                        '/?p=%ID%',
     521                                        '/?p=%ID%',
     522                                );
     523
     524                                $data[] = array(
     525                                        "$post_key-attachment",
     526                                        $user,
     527                                        '/?attachment_id=%ID%',
     528                                        '/?attachment_id=%ID%',
     529                                );
     530
     531                                $data[] = array(
     532                                        $post_key,
     533                                        $user,
     534                                        "/?name=$post_key&post_type=$post_key",
     535                                        "/?name=$post_key&post_type=$post_key",
     536                                );
     537
     538                                $data[] = array(
     539                                        $post_key,
     540                                        $user,
     541                                        '/?feed=rss&p=%ID%',
     542                                        '/?feed=rss&p=%ID%',
     543                                );
     544                        }
     545                }
     546
     547                return $data;
     548        }
     549
     550        /**
     551         * Test canonical redirect does not reveal private slugs.
     552         *
     553         * @ticket 5272
     554         * @dataProvider data_canonical_redirects_to_pretty_permalinks
     555         *
     556         * @param string $post_key  Post key used for creating fixtures.
     557         * @param string $user_role User role.
     558         * @param string $requested Requested URL.
     559         * @param string $expected  Expected URL.
     560         */
     561        public function test_canonical_redirects_to_pretty_permalinks( $post_key, $user_role, $requested, $expected ) {
     562                wp_set_current_user( self::$users[ $user_role ] );
     563                $this->set_permalink_structure( '/%postname%/' );
     564                $post = self::$posts[ $post_key ];
     565                clean_post_cache( $post->ID );
     566
     567                /*
     568                 * The dataProvider runs before the fixures are set up, therefore the
     569                 * post object IDs are placeholders that needs to be replaced.
     570                 */
     571                $requested = str_replace( '%ID%', $post->ID, $requested );
     572                $expected  = str_replace( '%ID%', $post->ID, $expected );
     573
     574                $this->assertCanonical( $requested, $expected );
     575        }
     576
     577        /**
     578         * Data provider for test_canonical_redirects_to_pretty_permalinks.
     579         *
     580         * @return array[] Array of arguments for tests {
     581         *     @type string $post_key  Post key used for creating fixtures.
     582         *     @type string $user_role User role.
     583         *     @type string $requested Requested URL.
     584         *     @type string $expected  Expected URL.
     585         * }
     586         */
     587        function data_canonical_redirects_to_pretty_permalinks() {
     588                $data              = array();
     589                $all_user_list     = array( 'anon', 'subscriber', 'content_author', 'editor' );
     590                $select_allow_list = array( 'content_author', 'editor' );
     591                $select_block_list = array( 'anon', 'subscriber' );
     592                // All post/page keys
     593                $all_user_post_status_keys    = array( 'publish' );
     594                $select_user_post_status_keys = array( 'private', 'a-private-status' );
     595                $no_user_post_status_keys     = array( 'future', 'draft', 'pending', 'auto-draft' ); // Excludes trash for attachment rules.
     596                $select_user_post_type_keys   = array( 'a-public-cpt' );
     597                $no_user_post_type_keys       = array( 'a-private-cpt' );
     598
     599                foreach ( $all_user_post_status_keys as $post_key ) {
     600                        foreach ( $all_user_list as $user ) {
     601                                $data[] = array(
     602                                        $post_key,
     603                                        $user,
     604                                        '/?p=%ID%',
     605                                        "/$post_key-post/",
     606                                );
     607
     608                                $data[] = array(
     609                                        "$post_key-attachment",
     610                                        $user,
     611                                        '/?attachment_id=%ID%',
     612                                        "/$post_key-post/$post_key-inherited-attachment/",
     613                                );
     614
     615                                $data[] = array(
     616                                        "$post_key-page",
     617                                        $user,
     618                                        '/?post_type=page&p=%ID%',
     619                                        "/$post_key-page/",
     620                                );
     621
     622                                $data[] = array(
     623                                        "$post_key-page",
     624                                        $user,
     625                                        '/?page_id=%ID%',
     626                                        "/$post_key-page/",
     627                                );
     628
     629                                $data[] = array(
     630                                        $post_key,
     631                                        $user,
     632                                        "/?name=$post_key-post",
     633                                        "/$post_key-post/",
     634                                );
     635
     636                                $data[] = array(
     637                                        $post_key,
     638                                        $user,
     639                                        '/?feed=rss&p=%ID%',
     640                                        "/$post_key-post/feed/",
     641                                );
     642
     643                                $data[] = array(
     644                                        "$post_key-page",
     645                                        $user,
     646                                        '/?feed=rss&page_id=%ID%',
     647                                        "/$post_key-page/feed/",
     648                                );
     649                        }
     650                }
     651
     652                foreach ( $select_user_post_status_keys as $post_key ) {
     653                        foreach ( $select_allow_list as $user ) {
     654                                $data[] = array(
     655                                        $post_key,
     656                                        $user,
     657                                        '/?p=%ID%',
     658                                        "/$post_key-post/",
     659                                );
     660
     661                                $data[] = array(
     662                                        "$post_key-attachment",
     663                                        $user,
     664                                        '/?attachment_id=%ID%',
     665                                        "/$post_key-post/$post_key-inherited-attachment/",
     666                                );
     667
     668                                $data[] = array(
     669                                        "$post_key-page",
     670                                        $user,
     671                                        '/?post_type=page&p=%ID%',
     672                                        "/$post_key-page/",
     673                                );
     674
     675                                $data[] = array(
     676                                        "$post_key-page",
     677                                        $user,
     678                                        '/?page_id=%ID%',
     679                                        "/$post_key-page/",
     680                                );
     681
     682                                $data[] = array(
     683                                        $post_key,
     684                                        $user,
     685                                        "/?name=$post_key-post",
     686                                        "/$post_key-post/",
     687                                );
     688
     689                                $data[] = array(
     690                                        $post_key,
     691                                        $user,
     692                                        '/?feed=rss&p=%ID%',
     693                                        "/$post_key-post/feed/",
     694                                );
     695
     696                                $data[] = array(
     697                                        "$post_key-page",
     698                                        $user,
     699                                        '/?feed=rss&page_id=%ID%',
     700                                        "/$post_key-page/feed/",
     701                                );
     702                        }
     703
     704                        foreach ( $select_block_list as $user ) {
     705                                $data[] = array(
     706                                        $post_key,
     707                                        $user,
     708                                        '/?p=%ID%',
     709                                        '/?p=%ID%',
     710                                );
     711
     712                                $data[] = array(
     713                                        "$post_key-attachment",
     714                                        $user,
     715                                        '/?attachment_id=%ID%',
     716                                        '/?attachment_id=%ID%',
     717                                );
     718
     719                                $data[] = array(
     720                                        "$post_key-page",
     721                                        $user,
     722                                        '/?post_type=page&p=%ID%',
     723                                        '/?post_type=page&p=%ID%',
     724                                );
     725
     726                                $data[] = array(
     727                                        "$post_key-page",
     728                                        $user,
     729                                        '/?page_id=%ID%',
     730                                        '/?page_id=%ID%',
     731                                );
     732
     733                                $data[] = array(
     734                                        $post_key,
     735                                        $user,
     736                                        "/?name=$post_key-post",
     737                                        "/?name=$post_key-post",
     738                                );
     739
     740                                $data[] = array(
     741                                        $post_key,
     742                                        $user,
     743                                        '/?feed=rss&p=%ID%',
     744                                        '/?feed=rss&p=%ID%',
     745                                );
     746
     747                                $data[] = array(
     748                                        "$post_key-page",
     749                                        $user,
     750                                        '/?feed=rss&page_id=%ID%',
     751                                        '/?feed=rss&page_id=%ID%',
     752                                );
     753                        }
     754                }
     755
     756                foreach ( $select_user_post_type_keys as $post_key ) {
     757                        foreach ( $select_allow_list as $user ) {
     758                                $data[] = array(
     759                                        $post_key,
     760                                        $user,
     761                                        '/?p=%ID%',
     762                                        "/$post_key/$post_key/",
     763                                );
     764
     765                                $data[] = array(
     766                                        "$post_key-attachment",
     767                                        $user,
     768                                        '/?attachment_id=%ID%',
     769                                        "/$post_key/$post_key/$post_key-inherited-attachment/",
     770                                );
     771
     772                                $data[] = array(
     773                                        $post_key,
     774                                        $user,
     775                                        "/?name=$post_key&post_type=$post_key",
     776                                        "/$post_key/$post_key/?post_type=$post_key",
     777                                );
     778
     779                                $data[] = array(
     780                                        $post_key,
     781                                        $user,
     782                                        '/?feed=rss&p=%ID%',
     783                                        "/$post_key/$post_key/feed/",
     784                                );
     785                        }
     786
     787                        foreach ( $select_block_list as $user ) {
     788                                $data[] = array(
     789                                        $post_key,
     790                                        $user,
     791                                        '/?p=%ID%',
     792                                        '/?p=%ID%',
     793                                );
     794
     795                                $data[] = array(
     796                                        "$post_key-attachment",
     797                                        $user,
     798                                        '/?attachment_id=%ID%',
     799                                        '/?attachment_id=%ID%',
     800                                );
     801
     802                                $data[] = array(
     803                                        $post_key,
     804                                        $user,
     805                                        "/?name=$post_key&post_type=$post_key",
     806                                        "/?name=$post_key&post_type=$post_key",
     807                                );
     808
     809                                $data[] = array(
     810                                        $post_key,
     811                                        $user,
     812                                        '/?feed=rss&p=%ID%',
     813                                        '/?feed=rss&p=%ID%',
     814                                );
     815                        }
     816                }
     817
     818                foreach ( $no_user_post_type_keys as $post_key ) {
     819                        foreach ( $all_user_list as $user ) {
     820                                $data[] = array(
     821                                        $post_key,
     822                                        $user,
     823                                        '/?p=%ID%',
     824                                        '/?p=%ID%',
     825                                );
     826
     827                                $data[] = array(
     828                                        "$post_key-attachment",
     829                                        $user,
     830                                        '/?attachment_id=%ID%',
     831                                        '/?attachment_id=%ID%',
     832                                        // "/$post_key-inherited-attachment/",
     833                                );
     834
     835                                $data[] = array(
     836                                        $post_key,
     837                                        $user,
     838                                        "/?name=$post_key&post_type=$post_key",
     839                                        "/?name=$post_key&post_type=$post_key",
     840                                );
     841
     842                                $data[] = array(
     843                                        $post_key,
     844                                        $user,
     845                                        '/?feed=rss&p=%ID%',
     846                                        '/?feed=rss&p=%ID%',
     847                                );
     848                        }
     849                }
     850
     851                foreach ( $no_user_post_status_keys as $post_key ) {
     852                        foreach ( $all_user_list as $user ) {
     853                                $data[] = array(
     854                                        $post_key,
     855                                        $user,
     856                                        '/?p=%ID%',
     857                                        '/?p=%ID%',
     858                                );
     859
     860                                $data[] = array(
     861                                        "$post_key-attachment",
     862                                        $user,
     863                                        '/?attachment_id=%ID%',
     864                                        '/?attachment_id=%ID%',
     865                                );
     866
     867                                $data[] = array(
     868                                        "$post_key-page",
     869                                        $user,
     870                                        '/?post_type=page&p=%ID%',
     871                                        '/?post_type=page&p=%ID%',
     872                                );
     873
     874                                $data[] = array(
     875                                        "$post_key-page",
     876                                        $user,
     877                                        '/?page_id=%ID%',
     878                                        '/?page_id=%ID%',
     879                                );
     880
     881                                $data[] = array(
     882                                        $post_key,
     883                                        $user,
     884                                        "/?name=$post_key-post",
     885                                        "/?name=$post_key-post",
     886                                );
     887
     888                                $data[] = array(
     889                                        $post_key,
     890                                        $user,
     891                                        '/?feed=rss&p=%ID%',
     892                                        '/?feed=rss&p=%ID%',
     893                                );
     894
     895                                $data[] = array(
     896                                        "$post_key-page",
     897                                        $user,
     898                                        '/?feed=rss&page_id=%ID%',
     899                                        '/?feed=rss&page_id=%ID%',
     900                                );
     901                        }
     902                }
     903
     904                foreach ( array( 'trash' ) as $post_key ) {
     905                        foreach ( $all_user_list as $user ) {
     906                                $data[] = array(
     907                                        $post_key,
     908                                        $user,
     909                                        '/?p=%ID%',
     910                                        '/?p=%ID%',
     911                                );
     912
     913                                $data[] = array(
     914                                        "$post_key-attachment",
     915                                        $user,
     916                                        '/?attachment_id=%ID%',
     917                                        '/?attachment_id=%ID%',
     918                                );
     919
     920                                $data[] = array(
     921                                        "$post_key-attachment",
     922                                        $user,
     923                                        '/trash-post/trash-post-inherited-attachment/',
     924                                        '/?attachment_id=%ID%',
     925                                );
     926
     927                                $data[] = array(
     928                                        "$post_key-attachment",
     929                                        $user,
     930                                        '/trash-post__trashed/trash-post-inherited-attachment/',
     931                                        '/?attachment_id=%ID%',
     932                                );
     933
     934                                $data[] = array(
     935                                        "$post_key-page",
     936                                        $user,
     937                                        '/?post_type=page&p=%ID%',
     938                                        '/?post_type=page&p=%ID%',
     939                                );
     940
     941                                $data[] = array(
     942                                        "$post_key-page",
     943                                        $user,
     944                                        '/?page_id=%ID%',
     945                                        '/?page_id=%ID%',
     946                                );
     947
     948                                $data[] = array(
     949                                        $post_key,
     950                                        $user,
     951                                        "/?name=$post_key-post",
     952                                        "/?name=$post_key-post",
     953                                );
     954
     955                                $data[] = array(
     956                                        $post_key,
     957                                        $user,
     958                                        '/?feed=rss&p=%ID%',
     959                                        '/?feed=rss&p=%ID%',
     960                                );
     961
     962                                $data[] = array(
     963                                        "$post_key-page",
     964                                        $user,
     965                                        '/?feed=rss&page_id=%ID%',
     966                                        '/?feed=rss&page_id=%ID%',
     967                                );
     968                        }
     969                }
     970
     971                return $data;
     972        }
     973}
  • tests/phpunit/tests/link.php

    diff --git a/tests/phpunit/tests/link.php b/tests/phpunit/tests/link.php
    index 62f55fbd7d..9eae2a1401 100644
    a b public function test_attachment_attached_to_non_existent_post_type_has_a_pretty_ 
    204204                        }
    205205                }
    206206
    207                 $this->assertSame( home_url( user_trailingslashit( $attachment->post_name ) ), get_permalink( $attachment_id ) );
     207                $this->assertSame( home_url( "/?attachment_id={$attachment->ID}" ), get_permalink( $attachment_id ) );
     208                // Visit permalink.
     209                $this->go_to( get_permalink( $attachment_id ) );
     210                $this->assertQueryTrue( 'is_attachment', 'is_single', 'is_singular' );
    208211        }
    209212}
  • tests/phpunit/tests/media.php

    diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php
    index e7326fec65..055618f620 100644
    a b function test_gallery_shortcode_when_is_feed_true() { 
    31223122         * @ticket 51776
    31233123         *
    31243124         * @param string $post_key     Post as keyed in the shared fixture array.
    3125          * @param string $expected     Expected result.
     3125         * @param string $expected_url Expected permalink.
    31263126         * @param bool   $expected_404 Whether the page is expected to return a 404 result.
    31273127         *
    31283128         */
    3129         function test_attachment_permalinks_based_on_parent_status( $post_key, $expected, $expected_404 ) {
     3129        function test_attachment_permalinks_based_on_parent_status( $post_key, $expected_url, $expected_404 ) {
    31303130                $this->set_permalink_structure( '/%postname%' );
    31313131                $post = get_post( self::$post_ids[ $post_key ] );
    31323132
    function test_attachment_permalinks_based_on_parent_status( $post_key, $expected 
    31343134                 * The dataProvider runs before the fixures are set up, therefore the
    31353135                 * post object IDs are placeholders that needs to be replaced.
    31363136                 */
    3137                 $expected = home_url( str_replace( '%ID%', $post->ID, $expected ) );
     3137                $expected_url = home_url( str_replace( '%ID%', $post->ID, $expected_url ) );
    31383138
    3139                 $this->assertSame( $expected, get_permalink( $post ) );
    31403139                $this->go_to( get_permalink( $post ) );
    3141                 $this->assertSame( $expected_404, is_404() );
     3140                $this->assertSame( $expected_url, get_permalink( $post ) );
     3141                if ( $expected_404 ) {
     3142                        $this->assertQueryTrue( 'is_404' );
     3143                } else {
     3144                        $this->assertQueryTrue( 'is_attachment', 'is_single', 'is_singular' );
     3145                }
     3146                $this->assertSame( 'attachment', $post->post_type );
    31423147        }
    31433148
    31443149        /**
    function test_attachment_permalinks_based_on_parent_status( $post_key, $expected 
    31463151         *
    31473152         * @return array[] {
    31483153         *     @type string $post_key     Post as keyed in the shared fixture array.
    3149          *     @type string $expected     Expected result.
     3154         *     @type string $expected_url Expected permalink.
    31503155         *     $type bool   $expected_404 Whether the page is expected to return a 404 result.
    31513156         * }
    31523157         */
  • new file tests/phpunit/tests/post/isPostPubliclyViewable.php

    diff --git a/tests/phpunit/tests/post/isPostPubliclyViewable.php b/tests/phpunit/tests/post/isPostPubliclyViewable.php
    new file mode 100644
    index 0000000000..9d5d781e88
    - +  
     1<?php
     2
     3/**
     4 * @group post
     5 */
     6class Tests_Post_IsPostPubliclyViewable extends WP_UnitTestCase {
     7
     8        /**
     9         * Array of post IDs to use as parents.
     10         *
     11         * @var array
     12         */
     13        public static $parent_post_ids = array();
     14
     15        public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
     16                $post_statuses = array( 'publish', 'private', 'future', 'trash', 'delete' );
     17                foreach ( $post_statuses as $post_status ) {
     18                        $date          = '';
     19                        $actual_status = $post_status;
     20                        if ( 'future' === $post_status ) {
     21                                $date = strftime( '%Y-%m-%d %H:%M:%S', strtotime( '+1 year' ) );
     22                        } elseif ( in_array( $post_status, array( 'trash', 'delete' ), true ) ) {
     23                                $actual_status = 'publish';
     24                        }
     25
     26                        self::$parent_post_ids[ $post_status ] = $factory->post->create(
     27                                array(
     28                                        'post_status' => $actual_status,
     29                                        'post_name'   => "$post_status-post",
     30                                        'post_date'   => $date,
     31                                        'post_type'   => 'page',
     32                                )
     33                        );
     34                }
     35
     36                wp_trash_post( self::$parent_post_ids['trash'] );
     37                wp_delete_post( self::$parent_post_ids['delete'], true );
     38        }
     39
     40        /**
     41         * Unit tests for is_post_publicly_viewable().
     42         *
     43         * @dataProvider data_is_post_publicly_viewable
     44         * @ticket 49380
     45         *
     46         * @param string $post_type   The post type.
     47         * @param string $post_status The post status.
     48         * @param bool   $expected    The expected result of the function call.
     49         * @param string $parent_key  The parent key as set up in shared fixtures.
     50         */
     51        public function test_is_post_publicly_viewable( $post_type, $post_status, $expected, $parent_key = '' ) {
     52                $date = '';
     53                if ( 'future' === $post_status ) {
     54                        $date = strftime( '%Y-%m-%d %H:%M:%S', strtotime( '+1 year' ) );
     55                }
     56
     57                $post_id = $this->factory()->post->create(
     58                        array(
     59                                'post_type'   => $post_type,
     60                                'post_status' => $post_status,
     61                                'post_parent' => $parent_key ? self::$parent_post_ids[ $parent_key ] : 0,
     62                                'post_date'   => $date,
     63                        )
     64                );
     65
     66                $this->assertSame( $expected, is_post_publicly_viewable( $post_id ) );
     67        }
     68
     69        /**
     70         * Data provider for test_is_post_publicly_viewable().
     71         *
     72         * return array[] {
     73         *     @type string $post_type   The post type.
     74         *     @type string $post_status The post status.
     75         *     @type bool   $expected    The expected result of the function call.
     76         *     @type string $parent_key  The parent key as set up in shared fixtures.
     77         * }
     78         */
     79        public function data_is_post_publicly_viewable() {
     80                return array(
     81                        array( 'post', 'publish', true ),
     82                        array( 'post', 'private', false ),
     83                        array( 'post', 'future', false ),
     84
     85                        array( 'page', 'publish', true ),
     86                        array( 'page', 'private', false ),
     87                        array( 'page', 'future', false ),
     88
     89                        array( 'unregistered_cpt', 'publish', false ),
     90                        array( 'unregistered_cpt', 'private', false ),
     91
     92                        array( 'post', 'unregistered_cps', false ),
     93                        array( 'page', 'unregistered_cps', false ),
     94
     95                        array( 'attachment', 'inherit', true, 'publish' ),
     96                        array( 'attachment', 'inherit', false, 'private' ),
     97                        array( 'attachment', 'inherit', false, 'future' ),
     98                        array( 'attachment', 'inherit', true, 'trash' ),
     99                        array( 'attachment', 'inherit', true, 'delete' ),
     100
     101                        array( 'page', 'publish', true, 'publish' ),
     102                        array( 'page', 'publish', true, 'private' ),
     103                        array( 'page', 'publish', true, 'future' ),
     104                        array( 'page', 'publish', true, 'trash' ),
     105                        array( 'page', 'publish', true, 'delete' ),
     106                );
     107        }
     108}
  • new file tests/phpunit/tests/post/isPostStatusViewable.php

    diff --git a/tests/phpunit/tests/post/isPostStatusViewable.php b/tests/phpunit/tests/post/isPostStatusViewable.php
    new file mode 100644
    index 0000000000..9658ca2066
    - +  
     1<?php
     2
     3/**
     4 * @group post
     5 */
     6class Tests_Post_IsPostStatusViewable extends WP_UnitTestCase {
     7
     8        /**
     9         * Remove the test status from the global when finished.
     10         *
     11         * @global $wp_post_statuses
     12         */
     13        static function wpTearDownAfterClass() {
     14                global $wp_post_statuses;
     15                unset( $wp_post_statuses['wp_tests_ps'] );
     16        }
     17
     18        /**
     19         * Test custom post status.
     20         *
     21         * This may include emulations of built in (_builtin) statuses.
     22         *
     23         * @ticket 49380
     24         * @dataProvider data_custom_post_statuses
     25         *
     26         * @param array $cps_args Registration arguments.
     27         * @param bool  $expected Expected result.
     28         */
     29        public function test_custom_post_statuses( $cps_args, $expected ) {
     30                register_post_status(
     31                        'wp_tests_ps',
     32                        $cps_args
     33                );
     34
     35                // Test status passed as string.
     36                $this->assertSame( $expected, is_post_status_viewable( 'wp_tests_ps' ) );
     37                // Test status passed as object.
     38                $this->assertSame( $expected, is_post_status_viewable( get_post_status_object( 'wp_tests_ps' ) ) );
     39        }
     40
     41        /**
     42         * Data provider for custom post status tests.
     43         *
     44         * @return array[] {
     45         *     array CPS registration args.
     46         *     bool  Expected result.
     47         * }
     48         */
     49        public function data_custom_post_statuses() {
     50                return array(
     51                        // 0. False for non-publically queryable types.
     52                        array(
     53                                array(
     54                                        'publicly_queryable' => false,
     55                                        '_builtin'           => false,
     56                                        'public'             => true,
     57                                ),
     58                                false,
     59                        ),
     60                        // 1. True for publically queryable types.
     61                        array(
     62                                array(
     63                                        'publicly_queryable' => true,
     64                                        '_builtin'           => false,
     65                                        'public'             => false,
     66                                ),
     67                                true,
     68                        ),
     69                        // 2. False for built-in non-public types.
     70                        array(
     71                                array(
     72                                        'publicly_queryable' => false,
     73                                        '_builtin'           => true,
     74                                        'public'             => false,
     75                                ),
     76                                false,
     77                        ),
     78                        // 3. False for non-built-in public types.
     79                        array(
     80                                array(
     81                                        'publicly_queryable' => false,
     82                                        '_builtin'           => false,
     83                                        'public'             => true,
     84                                ),
     85                                false,
     86                        ),
     87                        // 4. True for built-in public types.
     88                        array(
     89                                array(
     90                                        'publicly_queryable' => false,
     91                                        '_builtin'           => true,
     92                                        'public'             => true,
     93                                ),
     94                                true,
     95                        ),
     96                );
     97        }
     98
     99        /**
     100         * Test built-in and unregistered post status.
     101         *
     102         * @dataProvider data_built_unregistered_in_status_types
     103         * @ticket 49380
     104         *
     105         * @param mixed $status   Post status to check.
     106         * @param bool  $expected Expected viewable status.
     107         */
     108        function test_built_unregistered_in_status_types( $status, $expected ) {
     109                // Test status passed as string.
     110                $this->assertSame( $expected, is_post_status_viewable( $status ) );
     111                // Test status passed as object.
     112                $this->assertSame( $expected, is_post_status_viewable( get_post_status_object( $status ) ) );
     113        }
     114
     115        /**
     116         * Data provider for built-in and unregistered post status tests.
     117         *
     118         * @return array[] {
     119         *     @type mixed $status   Post status to check.
     120         *     @type bool  $expected Expected viewable status.
     121         * }
     122         */
     123        public function data_built_unregistered_in_status_types() {
     124                return array(
     125                        array( 'publish', true ),
     126                        array( 'future', false ),
     127                        array( 'draft', false ),
     128                        array( 'pending', false ),
     129                        array( 'private', false ),
     130                        array( 'trash', false ),
     131                        array( 'auto-draft', false ),
     132                        array( 'inherit', false ),
     133                        array( 'request-pending', false ),
     134                        array( 'request-confirmed', false ),
     135                        array( 'request-failed', false ),
     136                        array( 'request-completed', false ),
     137
     138                        // Various unregistered statuses.
     139                        array( 'unregistered-status', false ),
     140                        array( false, false ),
     141                        array( true, false ),
     142                        array( 20, false ),
     143                        array( null, false ),
     144                        array( '', false ),
     145                );
     146        }
     147
     148        /**
     149         * Sanitize key should not be run when testing.
     150         *
     151         * @ticket 49380
     152         */
     153        public function test_sanitize_key_not_run() {
     154                register_post_status(
     155                        'WP_Tests_ps',
     156                        array(
     157                                'publicly_queryable' => true,
     158                                '_builtin'           => false,
     159                                'public'             => true,
     160                        )
     161                );
     162
     163                // Sanitized key should return true.
     164                $this->assertTrue( is_post_status_viewable( 'wp_tests_ps' ) );
     165                $this->assertTrue( is_post_status_viewable( get_post_status_object( 'wp_tests_ps' ) ) );
     166
     167                // Unsanitized key should return false.
     168                $this->assertFalse( is_post_status_viewable( 'WP_tests_ps' ) );
     169                $this->assertFalse( is_post_status_viewable( get_post_status_object( 'WP_tests_ps' ) ) );
     170        }
     171}