Make WordPress Core

Opened 4 years ago

Last modified 11 months ago

#19958 new enhancement

Allow custom post types as "home" and get_posts() to return results for more than one post type

Reported by: sooskriszta Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 3.3.1
Component: Posts, Post Types Keywords: has-patch needs-testing needs-codex
Focuses: administration Cc:



Wordpress admin > Settings > Reading

there is an option to define what the home page or the front page should be

Front page displays

Radio button

A static page (select below)

is followed by a dropdown containing a list of all pages.

I would request that custom page types be allowed in this dropdown. This way, I could make my bbPress forums or my All-in-one event calendar, etc my homepage.

Attachments (3)

CustomPostTypeAsHomePages.patch (3.0 KB) - added by pbearne 3 years ago.
incomplete patch
CustomPostTypeAsHomePages.2.patch (4.5 KB) - added by pbearne 3 years ago.
incomplete patch after tonight work
CustomPostTypeAsHomePages.3.patch (8.1 KB) - added by pbearne 3 years ago.
Current dev version (not working)

Download all attachments as: .zip

Change History (28)

comment:1 @pbearne3 years ago

  • Cc pbearne@… added
  • Severity changed from normal to major
  • Type changed from enhancement to defect (bug)

This is needed or / and an option to push / block it in the custom post type config / setup

Last edited 3 years ago by pbearne (previous) (diff)

comment:2 @SergeyBiryukov3 years ago

  • Type changed from defect (bug) to enhancement

Related: #16379

comment:3 @jpyper3 years ago

  • Cc jarrod@… added

What about instead putting a filter on the arguments for the wp_dropdown_pages call that produces the list of pages to choose from (line 96 in wp-admin/options-reading.php)? A developer could then use :

function filter_my_cpt($args) {
  $args[post_type] = 'cpt-slug';[[BR]]

  //to also maintain pages: $args[post_type] = 'pages, cpt-slug'
add_filter('options-front-page' , filter_my_cpt');
add_filter('options-posts-page' , filter_my_cpt');
Last edited 2 years ago by SergeyBiryukov (previous) (diff)

comment:4 @pbearne3 years ago

Yes feels that it would work

wp_dropdown_pages() call's get_pages() just passing the args through and get_pages() takes in post_type

And if we update the custom post/page type page to inform how to add this

I don't see the need to add it to the posts page as this is just a place holder so remove that from the code.

Added (untested) code to make the filter work with any number of filters so that this call can be part of custom post/page type page setups.

function filter_my_cpt($args) {
// pass through current value if any to allow for other filters
     $args[post_type] = array_push($args[post_type],'cpt-slug');
     $args[post_type] = 'cpt-slug';

add_filter('options-front-page' , filter_my_cpt');

so the change to options-reading.php would just a filter around the front page $args array

I that feel we should break this out a bit to make readable but this looks like the code we need.

     <li><label for="page_on_front"><?php printf( __( 'Front page: %s' ), wp_dropdown_pages(apply_filters('options-front-page', array( 'name' => 'page_on_front', 'echo' => 0, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => get_option( 'page_on_front' ) ) ) ) ); ?></label></li>
     <li><label for="page_for_posts"><?php printf( __( 'Posts page: %s' ), wp_dropdown_pages( array( 'name' => 'page_for_posts', 'echo' => 0, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => get_option( 'page_for_posts' ) ) ) ); ?></label></li>

Does this all make sense?


comment:5 @jpyper3 years ago

Yes, that's a better idea for a function to utilize the filter and makes sense to me.

comment:6 @jpyper3 years ago

I was doing some testing and I think I see a further problem. WP doesn't seem to properly handle having a CPT as the front page in some aspects.

For instance, the url for the front page doesn't get changed to just website.com, it's website.com/post-type/post-slug. Same goes for when a link to the new "front page" is placed in a menu. Normally the link is changed to just be website.com but it keeps it's full URL.

Part of that problem seems to be in /wp-admin/includes/nav-menu.php about line 726. It checks for a post type name of 'page' before changing the url out for menus. I haven't found where it does the URL change for the URL of the front of the site.

comment:7 @pbearne3 years ago


Lets keep digging


comment:8 @pbearne3 years ago

We could just reverse this

	if ( 'page' == $post_type_name ) {


	if ( 'post' != $post_type_name ) {

and let the

if ( ! empty( $front_page ) ) {

do it's stuff for all the menu types but this would mean that the code will run more than needed

So the other way would to do a look-up for the page type that is used for the home page test for that setting 'page' as the default as we might not get anything back

$front_page_type = 'page' == get_option('show_on_front') ? 
                 get_post_type((int) get_option( 'page_on_front' )) : 'page';

if($front_page_type == $post_type_name ){

If this works it's better and cover all the bases

or this might be even better

$front_page_type = 'page' == get_option('show_on_front') ? 
                 get_post_type((int) get_option( 'page_on_front' )) : null;

if($front_page_type == $post_type_name ){


comment:9 @pbearne3 years ago


I have done some work on this but have not sorted the URL code

So I am just pasting the patch for now

Index: wp-admin/options-reading.php
--- wp-admin/options-reading.php	(revision 22831)
+++ wp-admin/options-reading.php	(working copy)
@@ -114,7 +114,7 @@
-	<li><label for="page_on_front"><?php printf( __( 'Front page: %s' ), 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' ) ) ) ); ?></label></li>
+	<li><label for="page_on_front"><?php printf( __( 'Front page: %s' ), wp_dropdown_pages( apply_filters('options-front-page',array( 'name' => 'page_on_front', 'echo' => 0, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => get_option( 'page_on_front' ) ) ) ) ); ?></label></li>
 	<li><label for="page_for_posts"><?php printf( __( 'Posts page: %s' ), wp_dropdown_pages( array( 'name' => 'page_for_posts', 'echo' => 0, 'show_option_none' => __( '&mdash; Select &mdash;' ), 'option_none_value' => '0', 'selected' => get_option( 'page_for_posts' ) ) ) ); ?></label></li>
 <?php if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) == get_option( 'page_on_front' ) ) : ?>
Index: wp-includes/post.php
--- wp-includes/post.php	(revision 22831)
+++ wp-includes/post.php	(working copy)
@@ -3628,9 +3628,14 @@
 	$number = (int) $number;
 	$offset = (int) $offset;
-	// Make sure the post type is hierarchical
+	// Make sure we have a valid post type
+	if ( !is_array( $post_type ) )
+		$post_type = explode( ',', $post_type );
+	if ( array_diff( $post_type, get_post_types() ) )
+		return $pages;
+	// Make sure the all post types are hierarchical
 	$hierarchical_post_types = get_post_types( array( 'hierarchical' => true ) );
-	if ( !in_array( $post_type, $hierarchical_post_types ) )
+	if ( array_intersect( $post_type, $hierarchical_post_types ) == count($post_type) )
 		return $pages;
 	// Make sure we have a valid post status
@@ -3733,11 +3738,19 @@
 	if ( $parent >= 0 )
 		$where .= $wpdb->prepare(' AND post_parent = %d ', $parent);
+	// Check if post_type is an array so that Custom Page types can be added to dropdowns and lists
+	if ( 1 == count( $post_type ) ) {	
+		$where_post_type = $wpdb->prepare("post_type = %s"  , array_shift( $post_type ) );
+	} else{
+		$post_type = implode( "', '", $post_type );
+		$where_post_type = "post_type IN ('$post_type')";
+	}
 	if ( 1 == count( $post_status ) ) {
-		$where_post_type = $wpdb->prepare( "post_type = %s AND post_status = %s", $post_type, array_shift( $post_status ) );
+		$where_post_type .= $wpdb->prepare( " AND post_status = %s",  array_shift( $post_status ) );
 	} else {
 		$post_status = implode( "', '", $post_status );
-		$where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $post_type );
+		$where_post_type .= "AND post_status IN ('$post_status')" ;
 	$orderby_array = array();

the test functions I used where

add_action( 'init', 'create_post_type' );
function create_post_type() {
	register_post_type( 'acme_product',
			'labels' => array(
				'name' => __( 'tests' ),
				'singular_name' => __( 'test' )
			'public' => true,
			'has_archive' => true,
			'rewrite' => array('slug' => 'tests'),

function filter_my_cpt($args) {
	$args['post_type'] = array('page', 'acme_product');
	return $args;

add_filter('options-front-page' , 'filter_my_cpt');

I will look at the URl code later

And I want to look getting this into the register_post_type() call

comment:10 @pbearne3 years ago

I see the bug on the menus I will need to be look for if there is a set home page and then test for that post type add add the Home: XXXXX to the correct list

comment:11 follow-up: @SergeyBiryukov3 years ago

You should attach the patch as a file instead of pasting it in the comment box.

comment:12 in reply to: ↑ 11 @pbearne3 years ago

Replying to SergeyBiryukov:

You should attach the patch as a file instead of pasting it in the comment box.

even if its not complete?
I have more work to do before finished.

comment:13 @SergeyBiryukov3 years ago

Yes, just leave a note about what's already implemented and what else needs to be done.

It's easier to follow the discussions and development when patches are in separate files.

@pbearne3 years ago

incomplete patch

comment:14 @pbearne3 years ago

Just added a patch this works OK but WP has "page" hard coded in a few places so I need to provide patch's so that we do a look-up for the page type for the home page see nav-menu.php line 729 and jpyper comment above for example.

We also need to find and recode the rewrite rule that hides the path/filename when loading a homepage

I also what to have look at what changes would be needed to make this a switch in the register_post_type function and not a extra filter step

And it would be good to check that we keep "page" in the $args!['post_type'!] array

comment:15 @pbearne3 years ago

Notes to self

check that removing $wpdb->prepare have I broken SQL santation
this looks OK as we check for valid values before using them

check this 3639

 if ( array_intersect( $post_type, $hierarchical_post_types ) == count($post_type) ) 
               return $pages; 

this should be a not the same return need to check the output of array_intersect maybe use array_diff

Last edited 2 years ago by SergeyBiryukov (previous) (diff)

comment:16 @pbearne3 years ago

  • Summary changed from Allow custom post types as "home" to Allow custom post types as "home" and get_posts() to return results for more than one post type

@pbearne3 years ago

incomplete patch after tonight work

comment:17 @pbearne3 years ago

  • Keywords has-patch needs-testing needs-codex added

This now all works

Apart from if a custom post type is set as a home page it does get servered as the root page eg. Domain.com rather at the URL of domain.com/customPostType/pagename the code for this looks like its somewhere in query.php so might be better if someone else who knows that code looks at that.

And I haven't add code to register_post_type make this a feature but will look at that this week

Last edited 3 years ago by pbearne (previous) (diff)

comment:18 @toscho3 years ago

  • Cc info@… added

comment:19 @wpsmith3 years ago

  • Cc travis@… added

comment:20 @SergeyBiryukov3 years ago

#23100 was marked as a duplicate.

@pbearne3 years ago

Current dev version (not working)

comment:21 @pbearne3 years ago

Hi All

I need a bit of help to finish this

I have one last bug and need a code review as the function I added to options-reading needs to be move to better location.

The bug: WordPress doesn't render the custom page at the root all I am getting the 404 page

if you look at line 109 in query.php you will see I have remove the hardcoded "page" and pass in the current page type.

Without this change the custom page loads but with the URL of /pagetype/pagename and not at the root /

With this change I am getting the 404 so there is an additional change needed somewhere

I am sure that this will come back for some rework on variable name etc.

use this function to test, Note the 'show_in_home_page_list'=> true, which how I am declaring that this custom post should be added to the list

add_action( 'init', 'create_post_type' );
function create_post_type() {
	register_post_type( 'acme_product',
			'labels' => array(
				'name' => __( 'tests' ),
				'singular_name' => __( 'test' )
			'public' => true,
			'has_archive' => true,
			'rewrite' => array('slug' => 'tests'),
			'hierarchical' => true,
			'show_in_home_page_list'=> true,

I have tried to remove all the hard code strings so in itself will an improvement.

All and any help to finish this gratefully accepted.

comment:22 @navjotjsingh3 years ago

  • Cc navjotjsingh@… added

comment:23 @jeremyfelt21 months ago

  • Component changed from Administration to Posts, Post Types
  • Focuses administration added
  • Severity changed from major to normal

comment:24 @SergeyBiryukov18 months ago

#27685 was marked as a duplicate.

comment:25 @iammathews11 months ago

What's the status on this?

Note: See TracTickets for help on using tickets.