I certainly didn’t expect a part 5, but my monthly archive links suddenly stopped working! I hadn’t made any theme changes related to them so this was a most surprising problem.
First of all, I tried to find out which template file was getting called, but my debug statements in various files weren’t being called at all. Finally, a close look at the template hierarchy revealed that a 404 error goes straight to the 404 page, and there it was. This suggests that the query couldn’t find anything. Printing out the array of query variables made me realize that the URL segments were being mapped to the wrong variables.
This was most unexpected, and for some unknown reason, even reactivating the Custom Post Type Archives plugin didn’t work.
A lot of Googling revealed this discussion which suggests <domain>/2010/?post_type=cpt works. It was time to find out more about how URL rewriting works.
The Codex didn’t have much information about them, just some shallow user level instructions on what they look like and how to activate them. I did remember reading before that the rewriting was done by parsing the URL and giving everything to index.php to handle. Since I could get my custom post types to display with the right template file when using ugly URLs, getting the rewrite rules to work would be the key to the solution.
The code for rewrites is in wp-includes/rewrite.php, which is where the WP_Rewrite class is defined. Looking through the whole file gave me a better understanding of what’s happening. It seems that lots of rules are actually stored internally. These rules convert the pretty links into query variables which the rest of the WordPress framework can understand.
The function reference for WP_Rewrite explained the variables and the filters, which could be used to modify the rewrite rules. With this clue, I went through the source code of the plugin to find where it adds the rewrite rules. I then modified it to suit my own purposes.
The code is as follows.
function register_post_type_rewrite_rules($wp_rewrite) {
$args = array('public' => true, '_builtin' => false); //get all public custom post types
$output = 'names';
$operator = 'and';
$post_types = get_post_types($args,$output,$operator);
$url_base = ($url_base == '') ? $url_base : $url_base . '/';
$custom_rules = array();
$post_types = implode('|', $post_types);
$custom_rules = array( "$url_base($post_types)/([0-9]+)/([0-9]{1,2})/([0-9]{1,2})/?$" =>
'index.php?post_type_index=1&post_type=' . $wp_rewrite->preg_index(1) . '&year=' . $wp_rewrite->preg_index(2) . '&monthnum=' . $wp_rewrite->preg_index(3) . '&day=' . $wp_rewrite->preg_index(4), //year month day
"$url_base($post_types)/([0-9]+)/([0-9]{1,2})/?$" =>
'index.php?post_type_index=1&post_type=' . $wp_rewrite->preg_index(1) . '&year=' . $wp_rewrite->preg_index(2) . '&monthnum=' . $wp_rewrite->preg_index(3), //year month
"$url_base($post_types)/([0-9]+)/?$" =>
'index.php?post_type_index=1&post_type=' . $wp_rewrite->preg_index(1) . '&year=' . $wp_rewrite->preg_index(2) //year
);
$wp_rewrite->rules = array_merge($custom_rules, $wp_rewrite->rules); // merge existing rules with custom ones
return $wp_rewrite;
}
add_filter('generate_rewrite_rules', 'register_post_type_rewrite_rules', 100);
First, get all public custom post types. This is explained in the function reference for get_post_types()
Next, add rewrite rules for year/month/day, year/month, and year URL permalinks to a custom array.
Finally, merge this array to the existing one and return the entire set of rules.
Once again, custom post type archives work like they’re supposed to. Phew!
Read about the next improvement to the custom post type archives widget at the Custom Post Type Archives jQuery Accordion post.