Opened 14 years ago
Last modified 2 years ago
#15551 reopened defect (bug)
Custom post type pagination redirect
Reported by: | cbsad | Owned by: | |
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | 3.1 |
Component: | Query | Keywords: | |
Focuses: | Cc: |
Description
We seem to be having a problem with a redirect on a custom post page (e.g., http://www.example.com/show/slug/).
Everything on the page shows correctly. The template pulls in posts with the following query:
<?php query_posts(array('meta_key' => 'show', 'meta_value' => $show->ID, 'paged' => get_query_var('paged'), 'caller_get_posts' => true)); ?>
After the posts we display pagination links (e.g., http://www.example.com/show/slug/page/2/). In our rewrite rules, we include the following rule which should handle these URLs:
'show/([^/]+)/page/([0-9]{1,})/?$' => 'index.php?show=$matches[1]&paged=$matches[2]',
Instead of the page displaying and allowing us to pull in the second page of posts, the browser is being redirected back to the original page (http://www.example.com/show/slug/) with a 301. I see that this is happening inside the redirect_canonical function. The following code in wp-includes/canonical.php replaces /page/2/ with / causing the redirect:
// paging and feeds if ( get_query_var('paged') || is_feed() || get_query_var('cpage') ) { if ( !$redirect_url ) $redirect_url = $requested_url; $paged_redirect = @parse_url($redirect_url); while ( preg_match( "#/$wp_rewrite->pagination_base/?[0-9]+?(/+)?$#", $paged_redirect['path'] ) || preg_match( '#/(comments/?)?(feed|rss|rdf|atom|rss2)(/+)?$#', $paged_redirect['path'] ) || preg_match( '#/comment-page-[0-9]+(/+)?$#', $paged_redirect['path'] ) ) { // Strip off paging and feed $paged_redirect['path'] = preg_replace("#/$wp_rewrite->pagination_base/?[0-9]+?(/+)?$#", '/', $paged_redirect['path']); // strip off any existing paging $paged_redirect['path'] = preg_replace('#/(comments/?)?(feed|rss2?|rdf|atom)(/+|$)#', '/', $paged_redirect['path']); // strip off feed endings $paged_redirect['path'] = preg_replace('#/comment-page-[0-9]+?(/+)?$#', '/', $paged_redirect['path']); // strip off any existing comment paging }
Change History (21)
#2
@
14 years ago
A simple workaround could be disabling the canonical redirect for this particular case.
A possible solution could look like this. I'm not sure if this would be the preferred way to do this, though.
function no_canonical( $url ) { return false; } function adjust_show_request( $request ) { if ($request->query_vars['post_type'] === 'show' && $request->is_singular === true && $request->current_post == -1 && $request->is_paged === true ) { add_filter( 'redirect_canonical', 'no_canonical' ); } return $request; } add_action( 'parse_query', 'adjust_show_request' );
#4
@
14 years ago
- Component changed from General to Query
- Milestone changed from Awaiting Review to 3.1
#7
@
14 years ago
- Milestone 3.1 deleted
- Resolution set to invalid
- Status changed from new to closed
Not sure that there is a bug here. Re-open if I'm missing something.
#8
@
13 years ago
- Resolution invalid deleted
- Status changed from closed to reopened
I'd like to reopen this one, as I've helped two people with a fix on this issue in two days.
This issue has been discussed in a few forums, with use cases in each:
http://wordpress.org/support/topic/custom-post-type-singular-page-with-pagination?replies=5
http://www.wptavern.com/forum/bbpress/1710-bbpress-alternative-wordpress.html
The main use case is a custom post type that requires pagination on the single page.
This could be used for a few reasons I could imagine:
- If you have a static photo gallery post type that has so many photos you want to split it onto two pages without any javascript
- A forum that would have tons of threads, which would require pagination
In both of these scenarios developers use the 'redirect_canonical' filter to turn off canonical redirects on the singular post. However, perhaps we can build this into core? I could imagine many developers being thrown for a loop trying to debug this issue, never even tracking it down to canonical redirects.
#10
@
13 years ago
Isn't this achieved with 'paged' and the <--nextpage--> identifier? Or does that not work with custom post types for some reason?
#13
@
11 years ago
- Keywords reporter-feedback removed
- Milestone Awaiting Review deleted
- Resolution set to invalid
- Status changed from reopened to closed
no traction in 18 months - this wasn't convincing to begin with
#14
@
11 years ago
- Resolution invalid deleted
- Status changed from closed to reopened
I can confirm that this is still very much an issue in 3.9.x when generating a custom post type archive, applying pagination, and using permalink rewrites.
Case: a custom post type for items in a collection.
You want this:
../collection/home-decor/page/2/
You click and get this:
../collection/home-decor/
It happens every single time.
It was fixed using:
// Disable canonical urls for pagination add_filter( 'redirect_canonical','custom_disable_redirect_canonical' ); function custom_disable_redirect_canonical( $redirect_url ){ global $post; $ptype = get_post_type( $post ); if ( $ptype == 'items' && is_archive() ) $redirect_url = false; return $redirect_url; }
The original loop was basically:
/** * The logic for displaying a collection gallery */ wp_reset_postdata(); global $post; $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; $my_items = array( 'post_type' => 'items', 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', 'posts_per_page' => 12, 'paged' => $paged ); $my_postlist = new WP_Query( $my_items ); if($my_postlist->have_posts()) : while($my_postlist->have_posts()) : $my_postlist->the_post(); // Build your post loop here endwhile; else : // Build your contingency content here endif;
The actual loop was using a meta-query out of wp-types but that wasn't the issue. The issue was a forced canonical redirect from the page permalink to the root.
Doesn't matter if I use default permalinks, WP_PageNavi, or Hybrid Core; obviously. While I never have to tear my hair out over this again, it's a horribly user-unfriendly issue.
#15
@
11 years ago
- Keywords needs-patch added
- Milestone set to Future Release
This is super obnoxious. You need to do 2 things when you want to use page/[0-9]+
in the URL for a CPT:
// filter _wp_link_page() in the most ghetto way possible add_filter( 'wp_link_pages_link', function ($link) { if ( preg_match( '#whatever/([^/]+)/([0-9]+?)/#', $link, $match ) ) { $link = str_replace( $match[0], "whatever/{$match[1]}/page/{$match[2]}/", $link ); } return $link; } ); // shove your rules in there somehow add_filter( 'rewrite_rules_array', function ($rules) { $mine = array( 'whatever/([^/]+)/page/([0-9]+?)/?$' => 'index.php?whatever=$matches[1]&page=$matches[2]', ); return array_merge( $mine, $rules ); } );
Notice, the query var has to be page
, not paged
- after that, you can at least view the page, but it will redirected from page/2/
to 2/
If someone works through the headache and creates a patch, we can do this in 4.0
#17
@
9 years ago
I have this problem too.
I have set custom page as Front page, and that page has set custom template with posts loop.
When the permalinks are set to different value than "Default", paged URL is redirected to homepage. (E.g. www.example.com/page/3 -> www.example.com)
Just question: Why are in WordPress used two different query vars for pagination?
I mean get_query_var('page') vs get_query_var('pages') I know one is used on is_home() (main blog) and second one is used in archives. But why you cannot merge it up?
I've tried to add this code:
<?php if(!!get_query_var('page')) set_query_var('paged', get_query_var('page'));
into /wp-includes/canonical.php - function redirect_canonical() (line 45) and it helps. But I don't know whether it cause problem somewhere else.
+
In query in my custom page template cannot be set "offset".
<?php if (is_front_page()) { unset($args['offset']); } $wp_query = new WP_Query($args);
Many thanks for any answers.
#18
@
8 years ago
In the single post for custom post type I had to show some extra data that requires pagination. WordPress redirects any sub-page requests to the single page (WordPress 4.5.3). Following code (taken from previous comment, and updated a little) removes the redirection, and pagination finally works:
<?php function fix_request_redirect( $request ) { if ( isset( $request->query_vars['post_type'] ) && 'custom_type' === $request->query_vars['post_type'] && true === $request->is_singular && - 1 == $request->current_post && true === $request->is_paged ) { add_filter( 'redirect_canonical', '__return_false' ); } return $request; } add_action( 'parse_query', 'fix_request_redirect' );
#19
follow-up:
↓ 20
@
8 years ago
- Keywords needs-patch needs-unit-tests removed
Can someone explain, how to make it work with an array of post types?
For example, we got a few post types - band, person, release, cinema, place etc...
I tried this one
<?php function fix_request_redirect( $request ) { if ( isset( $request->query_vars['post_type'] ) && 'place' === $request->query_vars['post_type'] OR 'org' === $request->query_vars['post_type'] OR 'label' === $request->query_vars['post_type'] OR 'band' === $request->query_vars['post_type'] OR 'person' === $request->query_vars['post_type'] OR 'company' === $request->query_vars['post_type'] OR 'fest' === $request->query_vars['post_type'] OR 'release' === $request->query_vars['post_type'] && true === $request->is_singular && - 1 == $request->current_post && true === $request->is_paged ) { add_filter( 'redirect_canonical', '__return_false' ); } return $request; } add_action( 'parse_query', 'fix_request_redirect' );
and it works!
But it also make errors in php logs, and, I think, not so perfect
I also playing with arrays, but cannot figure how to make it work properly
Is it possible at all?
Thanks in advance
#20
in reply to:
↑ 19
@
8 years ago
Use in_array
http://php.net/manual/en/function.in-array.php
Your example will look like:
<?php function fix_request_redirect( $request ) { $post_types = array( 'place', 'org', 'label', 'person', 'company', 'fest', 'release' ); if ( isset( $request->query_vars['post_type'] ) && in_array( $request->query_vars['post_type'], $post_types ) && true === $request->is_singular && - 1 == $request->current_post && true === $request->is_paged ) { add_filter( 'redirect_canonical', '__return_false' ); } return $request; } add_action( 'parse_query', 'fix_request_redirect' );
Replying to yuranikolaev:
Can someone explain, how to make it work with an array of post types?
For example, we got a few post types - band, person, release, cinema, place etc...
I tried this one
<?php function fix_request_redirect( $request ) { if ( isset( $request->query_vars['post_type'] ) && 'place' === $request->query_vars['post_type'] OR 'org' === $request->query_vars['post_type'] OR 'label' === $request->query_vars['post_type'] OR 'band' === $request->query_vars['post_type'] OR 'person' === $request->query_vars['post_type'] OR 'company' === $request->query_vars['post_type'] OR 'fest' === $request->query_vars['post_type'] OR 'release' === $request->query_vars['post_type'] && true === $request->is_singular && - 1 == $request->current_post && true === $request->is_paged ) { add_filter( 'redirect_canonical', '__return_false' ); } return $request; } add_action( 'parse_query', 'fix_request_redirect' );and it works!
But it also make errors in php logs, and, I think, not so perfect
I also playing with arrays, but cannot figure how to make it work properly
Is it possible at all?
Thanks in advance
Calls to query_posts() made within the theme have no affect on pagination. To affect pagination links you need to hook into the parse_query action and manipulate the main query. Paging is done off of the results of the main query.