If we want to create our own permalinks for custom post types, we can do so by using the WP_Rewrite WordPress object.
We can also create our own permalink rules from scratch, by using the same object.
One very important function while creating permalink rules is the $wp_rewrite->flush_rules() function. This function does the following -
- Deletes the ‘rewrite_rules’ option. This is where all the permalink rules for our blog are stored. When someone clicks on one of our blog pages, the parse_request function retrieves these rewrite rules and translates them into a query request.
- Regenerates new rewrite rules from scratch using the wp_rewrite_rules function. This function regenerates the rules and updates the ‘rewrite_rules’ option.
- It may also write to the .htaccess file if necessary.
When we add new permalink structures with register_post_type or add_permastruct, the structures only get added to the global $wp_rewrite object. It does not manifest as a rewrite rule until after we execute the $wp_rewrite->flush_rules() command.
That is why if we have a plugin that adds new permalink rules (including through register_post_type) we want to run $wp_rewrite->flush_rules() -
- During the activation process to ensure that our new permalink structures show up in the blog rewrite rules.
- During the deactivation process to remove our permalink structures from the blog rewrite rules.
function _activate() {
global $wp_rewrite;
$wp_rewrite->add_rewrite_tag("%gallery%", '([^/]+)', "gallery=");
$wp_rewrite->add_permastruct('gallery', $gallery_structure, false);
$wp_rewrite->flush_rules();
}
function _deactivate() {
global $wp_rewrite;
$wp_rewrite->add_permastruct( 'gallery', '');
$wp_rewrite->flush_rules();
}
Note – flush_rules is also run by the WordPress system when we Save Changes in Settings >> Permalinks. Therefore, if our blog permalinks are not working properly, we can manually flush our permalink rules by going into Settings >> Permalinks and doing a save.
Flushing Permalink Rules in WordPress Multi-Site
In WordPress multi-site, we may need to flush rewrite rules for multiple blogs during network activation and deactivation.
However, there is a fly in the ointment. When we change blogs using set_blog_id, we are only changing the database context. The global $wp_rewrite object remains the same, i.e., it is the $wp_rewrite object of the original blog from where we executed the network activate or deactivate.
If we cycle through the blogs of a multi-site configuration and simply execute flush_rules, the wrong permalink rules from the non-updated $wp_rewrite object will be written into the ‘rewrite_rules’ option of each of the blogs. In effect, we would mess up the permalink rules of all our blogs except for the original activation/deactivation blog.
To properly flush permalink rules, we must limit our changes to the database and not access blog specific data stored within the $wp_rewrite object. Below, we do this by adding new permalink rules directly into the ‘rewrite_rules’ database option instead of using the flush_rules function.
function shiba_add_rewrite_rules($permastruct, $ep_mask=EP_NONE) {
if (!$permastruct)return;
global $wp_rewrite;
$wp_rewrite->matches = 'matches'; // this is necessary to write the rules properly
$new_rules = $wp_rewrite->generate_rewrite_rules($permastruct, $ep_mask);
$rules = get_option('rewrite_rules');
$rules = array_merge($new_rules, $rules);
update_option('rewrite_rules', $rules);
}
// remove all rewrite rules for a given permastruct
function shiba_remove_rewrite_rules($permastruct, $ep_mask=EP_NONE) {
// replace all tags within permastruct
if (!$permastruct)return;
global $wp_rewrite;
$wp_rewrite->matches = 'matches';
$remove_rules = $wp_rewrite->generate_rewrite_rules($permastruct);
$num_rules = count($remove_rules);
// Get first rule
$rule1 = reset($remove_rules); $key_rule1 = key($remove_rules);
$rules = get_option('rewrite_rules');
$i = $num_rules;
foreach ($rules as $pretty_link => $query_link) {
// find the first rule
if (($pretty_link == $key_rule1) && ($query_link == $rule1)) { $i = 0; }
if ($i < $num_rules) {
// Delete next $num_rules
unset($rules[$pretty_link]); $i++;
}
}
update_option('rewrite_rules', $rules);
}
Line 5 – Generate permalink rules for our plugin permalink structures. Even though we are using $wp_rewrite here, we are only using one of its functions. Most importantly, that function does not rely on any blog specific data or state stored in the $wp_rewrite object.
Line 6 – Get the ‘rewrite rules’ for the current blog. This is a database operation which does get updated with set_blog_id.
Line 7 – Merge our new permalink rules with the previous blog rules.
Line 8 – Update the ‘rewrite rules’ for the current blog.
In the shiba_remove_rewrite_rules function we use a similar process to remove the permalink rules added by our plugin.
We Are Done!
Now we can flush permalink rules in our regular and multi-site blogs!



Shiba Skins WordPress Theme
How to Add the Media Manager Menu to the Theme Preview Interface
Debugging the w3tc Page Cache
How to Translate blogs.dir CDN Files in w3tc
How to Create Dynamic User Agent Groups in w3tc
How to Selectively Load Plugins for Specific Pages
WordPress Theme Customizer Javascript Interface
Mobile Responsive Galleries and Images
How to Add the WordPress 3.5 Media Manager Interface - Part 2
How to Add the WordPress 3.5 Media Manager Interface
Custom Post Type Permalinks – Part 2 (116)
Shiba Gallery 3.7 (28)
Add Tags and Categories to Your WordPress Page (46)
WordPress Media Library Plugin (242)
WordPress Example Plugin (7)
WordPress Search Widget – How to Style It (47)
WordPress Custom Taxonomy Input Panels (98)
Create Pop-up Windows in Your WordPress Blog with Thickbox (35)
You can also add rewrite rules within the rewrite_rules_array filter directly. I wrote up this gem earlier today with the purpose of manually creating callback linked action pages. The usage would be something like:
new action_page(‘cart’, array(‘items’));
add_action(‘admin_post_nopriv_cart’, array($this, ‘display_cart’));
add_action(‘admin_post_cart’, array($this, ‘display_cart’));
<?php action_page::init(); class action_page { protected $action; protected $vars; public static function init() { add_action('template_redirect', array(__CLASS__, 'action_redirect')); } public function __construct( $action, $vars = array()) { $this->action = $action; $this->vars = $vars; add_filter('query_vars', array($this, 'add_query_vars')); add_filter('rewrite_rules_array', array($this, 'add_rewrite_rules')); add_filter("{$action}_query_vars", array($this, 'import_vars')); } public function add_query_vars( $vars ) { $vars[] = 'action'; return array_merge($vars, $this->vars); } public function add_rewrite_rules( $rules ) { $regex = implode("/", $this->regex()); $regex = !empty($regex) ? "{$regex}/?$" : "?$"; $rules = array("{$this->action}/{$regex}" => $this->match()) + $rules; return $rules; } protected function regex() { $m = array_fill_keys($this->vars, "([^/]+)"); return apply_filters("{$this->action}_regex", $m); } protected function match() { $size = sizeof($this->vars); $match = "/index.php?action={$this->action}"; for($i = 0; $i < $size; $i++) { $m = $i + 1; $match .= "&{$this->vars[$i]}=\$matches[{$m}]"; } return apply_filters("{$this->action}_match", $match); } public function import_vars( $vars = array() ) { global $wp_query; $vars += array_intersect_key($wp_query->query_vars, array_flip($this->vars)); return $vars; } public static function action_redirect() { if($action = get_query_var('action')) { $vars = apply_filters("{$action}_query_vars", array()); $action = wp_validate_auth_cookie() ? "admin_post_{$action}" : "admin_post_nopriv_{$action}"; do_action($action, $vars); } } }Still a bit confuse here.. I am trying to add a new category using the WP API (multiple sites), the permalinks are not refreshing.
So when a visitor goes to the page on the site it throws a 404 error. I have to manually go to to wp-admin -> settings -> permalinks and save to solve the problem.
Here is what I am doing that is not working:
add_action(‘init’, ‘flush_permalinks’);
function flush_permalinks(){
global $wp_rewrite;
$wp_rewrite->set_permalink_structure(‘/%postname%/’);
//$wp_rewrite->flush_rules();
// flush_rewrite_rules();
}
Could you find my error? Thanks.
what the $gallery_structure, in $wp_rewrite->add_permastruct(‘gallery’, $gallery_structure, false); contains, (I understand its a struct based on google search). BUt its initialized or assigned a value?
$gallery_structure contains a regular WP permalink structure, e.g. /galleries/%year%/%monthnum%/%gallery%.
http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2