How do you use a CPT as the default home page?

I have a client who’s site will be making heavy use of custom post types to configure their site. But I’m between a rock and a hard place for their requested home page.

In reality, the home page will be a stack of specific “pages” within WordPress. Basically, there will be pages for: Intro, Blog, About Us, Portfolio, and Contact Us. They’ll all be stacked on top of one another so you can scroll from one page to another.

Read More

My first instinct was to just use a page (called Home) and embed a shortcode that accepts page slugs and outputs the proper order (i.e. [pageOrder]intro, blog, about-us, portfolio, contact-us[/pageOrder]). The page would use a custom page template to lay things out, control the loop, and add navigation to the left side of the page. But that all seems klunky.

My ideal solution would be to create a custom post type (called Stack) that allows the end user to position the pages with drag-drop and have the CPT take care of layout and navigation and such.

The problem with my ideal solution is settings. WordPress allows you to select a page for the default home page of the site. But it’s tied to a post type of page, and I’m not sure where to hook in to modify that so that users could also select a Stack as the default home page.

So, where do I hook in to in order to add a CPT to the dropdown of available pages for the default home page?

Related posts

Leave a Reply

5 comments

  1. Thanks to @toscho for the useful answer, but it felt a bit hackish to me, so I poked around a bit and figured out I could add a filter instead:

    function wpa18013_add_pages_to_dropdown( $pages, $r ){
        if('page_on_front' == $r['name']){
            $args = array(
                'post_type' => 'stack'
            );
            $stacks = get_posts($args);
            $pages = array_merge($pages, $stacks);
        }
    
        return $pages;
    }
    add_filter( 'get_pages', 'wpa18013_add_pages_to_dropdown', 10, 2 );
    

    Update

    After adding the above code I was, indeed, able to use a custom post type as the home page, but WordPress would redirect the permalinks because it wasn’t a “page” post type. So http://localhost/test would redirect to http://localhost/test/stacks/home-stack, which wasn’t what I wanted.

    Adding this action, though, fixed that and queries my custom post type along with pages for the home page:

    function enable_front_page_stacks( $query ){
        if('' == $query->query_vars['post_type'] && 0 != $query->query_vars['page_id'])
            $query->query_vars['post_type'] = array( 'page', 'stack' );
    }
    add_action( 'pre_get_posts', 'enable_front_page_stacks' );
    
  2. Maybe so? Refined version of my earlier solution.

    add_filter( 'wp_dropdown_pages', 'add_cpt_to_front_page_dropdown', 10, 1 );
    
    
    /**
     * Adds CPTs to the list of available pages for a static front page.
     *
     * @param  string $select Existing select list.
     * @return string
     */
    function add_cpt_to_front_page_dropdown( $select )
    {
        if ( FALSE === strpos( $select, '<select name="page_on_front"' ) )
        {
            return $select;
        }
    
        $cpt_posts = get_posts(
            array(
                'post_type'      => 'YOUR_POST_TYPE'
            ,   'nopaging'       => TRUE
            ,   'numberposts'    => -1
            ,   'order'          => 'ASC'
            ,   'orderby'        => 'title'
            ,   'posts_per_page' => -1
            )
        );
    
        if ( ! $cpt_posts ) // no posts found.
        {
            return $select;
        }
    
        $current = get_option( 'page_on_front', 0 );
    
        $options = walk_page_dropdown_tree(
            $cpt_posts
        ,   0
        ,    array(
                'depth'                 => 0
             ,  'child_of'              => 0
             ,  'selected'              => $current
             ,  'echo'                  => 0
             ,  'name'                  => 'page_on_front'
             ,  'id'                    => ''
             ,  'show_option_none'      => ''
             ,  'show_option_no_change' => ''
             ,  'option_none_value'     => ''
            )
        );
    
        return str_replace( '</select>', $options . '</select>', $select );
    }
    
  3. Why not just create a front-page.php template file, that uses either a normal query/Loop, or (if a custom Theme option is set to display the CPT on the Front Page), outputs a custom query/Loop, based on the CPT?

    The issue there is that you would have to create a separate Theme option to control the Front Page output, while at the same time instructing users to set Front Page to static page.

    To make things easier, you could hook your Theme option into settings-reading, using the “Reading” option group in your call to register_setting via the Settings API, so that it displays with the existing Front-Page options.

  4. I think something may have changed since EAMann wrote his update in 2011, and the wpa18013_add_pages_to_dropdown() function he supplies no longer works. As mentioned in a comment by Dameer, this function currently (December 2013) throws an error “Missing argument 2 for wpa18013_add_pages_to_dropdown()”

    The solution, for me anyway, was to rewrite the function without that second parameter, or the check for its contents. The full block of code then becomes:

    function add_unbox_tabs_to_dropdown( $pages ){
        $args = array(
            'post_type' => 'unbox_tabs'
        );
        $items = get_posts($args);
        $pages = array_merge($pages, $items);
    
        return $pages;
    }
    add_filter( 'get_pages', 'add_unbox_tabs_to_dropdown' );
    
    function enable_front_page_unbox_tabs( $query ){
        if('' == $query->query_vars['post_type'] && 0 != $query->query_vars['page_id'])
            $query->query_vars['post_type'] = array( 'page', 'unbox_tabs' );
    }
    add_action( 'pre_get_posts', 'enable_front_page_unbox_tabs' );
    

    Please note that in the above example, the custom post type I’m adding is “unbox_tabs” rather than “stack”.

  5. I’m using this technique to include a template from a plugin. So, @ChipBennett’s solution doesn’t apply.

    To add the CPT’s ( 'hierarchical' => 'false' ) from the plugin in the list of Static Pages, I modified @EAMann’s solution as follows:

    add_action( 'admin_head-options-reading.php', 'wpse_18013_modify_front_pages_dropdown' );
    add_action( 'pre_get_posts', 'wpse_18013_enable_front_page_stacks' );
    
    function wpse_18013_modify_front_pages_dropdown()
    {
        // Filtering /wp-includes/post-templates.php#L780
        add_filter( 'get_pages', 'wpse_18013_add_cpt_to_pages_on_front' );
    }
    
    function wpse_18013_add_cpt_to_pages_on_front( $r )
    {
        $args = array(
            'post_type' => 'stack'
        );
        $stacks = get_posts( $args );
        $r = array_merge( $r, $stacks );
    
        return $r;
    }
    
    function wpse_18013_enable_front_page_stacks( $query )
    {
        if( '' == $query->query_vars['post_type'] && 0 != $query->query_vars['page_id'] )
            $query->query_vars['post_type'] = array( 'page', 'stack' );
    }
    

    Core reference v3.4.2:
    slight difference of line numbers in 3.5 RC3, but code remains the same

    /**
     * wp-admin/options-reading.php#L96
     * Happens inside a <li><label>-</label></li>
     */
        wp_dropdown_pages( array( 
                    'name' => 'page_on_front'
                ,   'echo' => 0
                ,   'show_option_none' => __( '&mdash; Select &mdash;' )
                ,   'option_none_value' => '0'
                ,   'selected' => get_option( 'page_on_front' ) 
            ) 
        )
    
    /**
      * wp-includes/post-template.php#L768
      */
    function wp_dropdown_pages($args = '') {
        $defaults = array( /* defaults array */ );    
        $r = wp_parse_args( $args, $defaults );
        extract( $r, EXTR_SKIP );  
        $pages = get_pages( $r );