3

I am writing my custom theme from scratch and I have a custom post type 'my_frontpage' and want to declare one of it's posts as front-page. I want to do this via admin, hence simply add my cpt to the Front Page select box in Appearance >> Customize >> Static Front Page.

This issue has been discussed quite a few times in the internet. However, I could not find how to instructions that comprehensively explain all the steps to reach that goal.

So far, I understand that I need to use some kind of hook to expand the choice of available front page 'pages' with posts from my cpt. But which hook to use? I wouldn't even know, if I have to use an action or a filter hook? So, could someone please guide me through this issue in laymen's terms?

The closest result I could find was This question. However, I am not yet able to fully understand what happens there...

  • Setting a custom post type post as a static front page causes some issues as WordPress is not geared for this. We will need to change some conditionals because as soon as you go the static front page, we get redirected to the single post page. I was busy testing this, but our power station blew up, so I will have to wait till power is restored to test this further. Will keep you up to date. Enjoy your weekend. ;-) – Pieter Goosen Jan 22 '16 at 6:59
  • I hope it was not your massive P experimenting that caused the power station to go down... ;) Looking forward to reading your ideas. Enjoy your weekend, too Pieter! – Bunjip Jan 22 '16 at 10:36
  • I think I might have blown something after that huge bug hunt yesterday with regards to that one bounty question ;-). Enjoy – Pieter Goosen Jan 22 '16 at 10:39
2

I had time to look at your issue and the options-reading.php page which is the template used to render the reading settings page in backend.

There are unfortunately no filters to filter or add custom posts as sticky posts in a selectable dropdown. There are two hidden filters though which we can use, they are

IMHO, I think get_pages here is a better option. This way we will let wp_dropdown_pages() to take care of all the markup. We need to take care here though when we use the get_pages filter

  • We will need to make sure we only target the admin area and in particular the reading settings page otherwise we will alter any function/page that uses the get_pages() function

You need to decide whether you would need pages to show with the custom post type posts or just need the custom post types

You can try the following:

add_filter( 'get_pages', function ( $pages, $args )
{
    // First make sure this is an admin page, if not, bail
    if ( !is_admin() )
        return $pages;

    // Make sure that we are on the reading settings page, if not, bail
    global $pagenow;
    if ( 'options-reading.php' !== $pagenow )
        return $pages;

    // Remove the filter to avoid infinite loop
    remove_filter( current_filter(), __FUNCTION__ );

    $args = [
        'post_type'      => 'my_frontpage',
        'posts_per_page' => -1
    ];
    // Get the post type posts with get_posts to allow non hierarchical post types
    $new_pages = get_posts( $args );    

    /**
     * You need to decide if you want to add custom post type posts to the pages
     * already in the dropdown, or just want the custom post type posts in
     * the dropdown. I will handle both, just remove what is not needed
     */
    // If we only need custom post types
    $pages = $new_pages;

    // If we need to add custom post type posts to the pages
    // $pages = array_merge( $new_pages, $pages );

    return $pages;
}, 10, 2 );

You should now see your custom post type posts in the dropdown. Take note, this code will affect the dropdown for the blog page as well.

To avoid that, you can use a static counter to count the amount of times the filter has run and then bail just before the filter is applied to the blogpage dropdown. The filter will run a total of 3 times as get_pages() runs 3 times:

  • first to check if we actually have pages to set as a static front page.

  • second run will be inside wp_dropdown_pages() which is used by the static front page dropdown

  • the last run will be inside wp_dropdown_pages() which is used by the blogpage dropdown

So, based on this, we can try

add_filter( 'get_pages', function ( $pages, $args )
{
    // First make sure this is an admin page, if not, bail
    if ( !is_admin() )
        return $pages;

    // Make sure that we are on the reading settings page, if not, bail
    global $pagenow;
    if ( 'options-reading.php' !== $pagenow )
        return $pages;

    // Remove the filter to avoid infinite loop
    remove_filter( current_filter(), __FUNCTION__ );

    // Setup our static counter
    static $counter = 0;

    // Bail on the third run all runs after this. The third run will be 2
    if ( 2 <= $counter )
        return $pages;

    // Update our counter
    $counter++;

    $args = [
        'post_type'      => 'my_frontpage',
        'posts_per_page' => -1
    ];
    // Get the post type posts with get_posts to allow non hierarchical post types
    $new_pages = get_posts( $args );    

    /**
     * You need to decide if you want to add custom post type posts to the pages
     * already in the dropdown, or just want the custom post type posts in
     * the dropdown. I will handle both, just remove what is not needed
     */
    // If we only need custom post types
    $pages = $new_pages;

    // If we need to add custom post type posts to the pages
    // $pages = array_merge( $new_pages, $pages );

    return $pages;
}, 10, 2 );

If you visit the front end and visit the front page, you'll find that it will redirect to the single post page. That is because, by default, the main query on a static front page is set to query the page post type. This causes a 404 being returned and redirect_canonical() then redirects to the single post page. This is easy to solve, all we must do is to adjust the main query on the static front page.

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end
         && $q->is_main_query() // Only target the main query
         && 'page' === get_option( 'show_on_front' ) // Only target the static front page
    ) {
        $q->set( 'post_type', 'my_frontpage' );
    }
});

You will now have your static front page displaying correctly.

All you are left to do is to set a template. You can simply create a front-page.php, WordPress will automatically use it

  • I am really impressed by the quality and detail depth of your answer. Thank you so much, Pieter! While I have note yet implemented your solution completely, I am convinced that this solves my issue to the fullest. One question remains: How can I buy you a beer? – Bunjip Jan 25 '16 at 15:50
  • Hahahaha, thanks for the nice words. I'm up for a good beer anytime. If you have any concerns regarding this answer, feel free to leave me a comment. Enjoy ;-) – Pieter Goosen Jan 25 '16 at 15:53
0

While Pieter solved the issue elegant and comprehensively with no doubt, I finally went with another solution, which I want to share here, too. Maybe some folks are facing similiar issues in the time to come.

For custom post type definition as described in my question I used a plugin called Pods. As the plugin devs and community are likely to handle custom post types regularly, I thought it might be helpful to ask my question in their support channel, too.

It was a very helpful guy from the Pods development team who pointed me in the direction, I finally went with. So, instead of defining a custom post type for the purpose of creating a highly individual static front page, he recommended to rather add custom fields to a standard page, which should become the front page. That's what I did to reach my goal and that's what I also recommend to other users.

From a data modelling point of view, there is no reason to define a complete data type or, say, class, if you only want to apply it to a single item - in my case a single front page. I used another plugin called Advanced Custom Fields, as this allows for more advanced data types for your custom fields than Wordpress offers out of the box. You could add your custom fields via functions.php as well. Hope, that helps.

0

Well there’s also an additional way to avoid using a page template and let Wordpress load the front page by using the right post type template. This helps to avoid duplicading code if you want to let the front page look the same to the single post itself:

add_filter( 'template_include', 'add_front_page_template_path', 10, 1);

function add_front_page_template_path( $template_path ) {
    if (
        is_front_page() 
        && get_option( 'show_on_front' ) == 'page' 
        && ( $post_id = get_option( 'page_on_front' ) )
        && ( $post_type = get_post_type( $post_id ) ) != 'page' 
    ) {
        $_template_path = get_single_template( $post_type );

        /* In case there’s no template */
        $template_path = ( $template_path == '' ) ? $template_path : $_template_path;
    }

    return $template_path;  
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.