Skip to content

feat: add filter for customizable post statuses using plain permalinks #9247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from

Conversation

kushagra-goyal-14
Copy link

Allow Customization of Post Statuses That Use Plain Permalinks

Overview

This pull request introduces a new filter wp_force_plain_post_permalink_statuses that allows developers to customize which post statuses should use plain permalinks (e.g., /?p=123) instead of pretty permalinks (e.g., /my-post-title/).

This addresses the long-standing issue where scheduled posts show inconsistent permalink formats - pretty permalinks in the admin interface but plain permalinks in theme templates and functions like the_permalink().

Trac Ticket: #32322


Issue Fixed

Scheduled Posts Show Inconsistent Permalink Formats

  • Problem:
    Scheduled posts (status = future) display different permalink formats in different contexts:

    • Admin interface: Shows pretty permalinks (/my-scheduled-post/)
    • Theme templates: Shows plain permalinks (/?p=123)
    • Direct access: Pretty permalink URLs work without redirection
  • Root Cause:
    The wp_force_plain_post_permalink() function treats all protected post statuses (including future) as requiring plain permalinks for security, but this creates user confusion since scheduled posts are meant to be public eventually.

  • Solution:
    Introduced a filterable array of post statuses that should use plain permalinks, allowing developers to customize this behavior on a per-site basis while maintaining security for truly sensitive content.


Code Changes

New Filter Implementation

/**
 * Filters the post statuses that should use plain permalinks instead of pretty permalinks.
 *
 * By default, posts with 'draft', 'pending', 'auto-draft', and 'future' statuses
 * will use plain permalinks (e.g., /?p=123) for security and consistency reasons.
 *
 * To allow scheduled posts to use pretty permalinks, you can remove 'future' from this array:
 *
 * add_filter( 'wp_force_plain_post_permalink_statuses', function( $statuses ) {
 *     return array_diff( $statuses, [ 'future' ] );
 * });
 *
 * @since 6.9.0
 *
 * @param string[] $statuses Array of post status names that should use plain permalinks.
 * @param WP_Post  $post     The post object.
 */
$plain_permalink_statuses = apply_filters(
    'wp_force_plain_post_permalink_statuses',
    array( 'draft', 'pending', 'auto-draft', 'future' ),
    $post
);

Updated Logic

if (
    // Publicly viewable links never have plain permalinks.
    is_post_status_viewable( $post_status_obj ) ||
    (
        // Private posts don't have plain permalinks if the user can read them.
        $post_status_obj->private &&
        current_user_can( 'read_post', $post->ID )
    ) ||
    // Protected posts don't have plain links if getting a sample URL.
    ( $post_status_obj->protected && $sample ) ||
    // Allow customization of which post statuses should use plain permalinks.
    ! in_array( $post->post_status, $plain_permalink_statuses, true )
) {
    return false;
}

Usage Examples

Basic: Allow Scheduled Posts Pretty Permalinks

add_filter( 'wp_force_plain_post_permalink_statuses', function( $statuses ) {
    return array_diff( $statuses, [ 'future' ] );
});

Advanced: Selective by Post Type

add_filter( 'wp_force_plain_post_permalink_statuses', function( $statuses, $post ) {
    // Only allow pretty permalinks for future posts of type 'post'
    if ( 'future' === $post->post_status && 'post' === $post->post_type ) {
        return array_diff( $statuses, [ 'future' ] );
    }
    return $statuses;
}, 10, 2 );

Conditional by User Capability

add_filter( 'wp_force_plain_post_permalink_statuses', function( $statuses, $post ) {
    // Allow editors to see pretty permalinks for future posts
    if ( 'future' === $post->post_status && current_user_can( 'edit_others_posts' ) ) {
        return array_diff( $statuses, [ 'future' ] );
    }
    return $statuses;
}, 10, 2 );

Testing Instructions

Test Case 1: Default Behavior (Unchanged)

  1. Create a scheduled post with future publication date
  2. View the post in admin - should show pretty permalink
  3. Use the_permalink() in a theme template - should show plain permalink
  4. Expected: No change from current behavior

Test Case 2: Enable Pretty Permalinks for Future Posts

  1. Add the filter to remove 'future' from plain permalink statuses
  2. Create a scheduled post with future publication date
  3. Use the_permalink() in a theme template
  4. Expected: Shows pretty permalink (/my-scheduled-post/)

Test Case 3: Selective Application

  1. Add the filter with post type condition (only 'post', not 'page')
  2. Create a scheduled post and scheduled page
  3. Check permalinks for both
  4. Expected: Post shows pretty permalink, page shows plain permalink

Test Case 4: Security Maintained

  1. Apply filter to allow future posts pretty permalinks
  2. Create draft and pending posts
  3. Check their permalinks
  4. Expected: Draft and pending posts still use plain permalinks

Test Case 5: Direct Access

  1. Enable pretty permalinks for future posts
  2. Create a scheduled post
  3. Try accessing the pretty permalink URL directly
  4. Expected: Should work without redirection (existing behavior)

Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@kushagra-goyal-14 kushagra-goyal-14 force-pushed the fix/permalink-future-posts branch from ecafa53 to 62505d3 Compare July 15, 2025 20:11
@kushagra-goyal-14 kushagra-goyal-14 marked this pull request as ready for review July 15, 2025 20:21
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props kush123.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant