Skip to content

Instantly share code, notes, and snippets.

@khromov
Last active December 22, 2016 15:01
Show Gist options
  • Save khromov/8485b713876469088329 to your computer and use it in GitHub Desktop.
Save khromov/8485b713876469088329 to your computer and use it in GitHub Desktop.
WordPress - Remove the front permastruct / base slug from a custom post type (CPT)
<?php
class Plugin
{
/**
* This will let a CPT with the slug 'story' be used without a base slug, ie:
* Before:
* /story/hello
*
* After:
* /hello
*/
function __construct()
{
}
function initialize()
{
//Handle CPT rewrites for Story to remove base
add_filter('post_type_link', array(
&$this,
'story_preview_url_remove_base'
), 10, 3);
add_action('pre_get_posts', array(
&$this,
'story_modify_query_remove_base'
));
add_filter('wp_unique_post_slug', array(
&$this,
'story_fix_slug'
), 100, 6);
//Add the Story post type to the front page static page dropdown under Settings > Reading
add_filter('get_pages', array(
&$this,
'story_add_to_frontpage_dropdown'
), 10, 2);
//Let story be used as the front page on the routing level
add_action('pre_get_posts', array(
&$this,
'story_enable_frontpage'
), 11);
}
/**
* Let's the Story CPT be used as the front page properly.
*
* @param $query
*/
function story_enable_frontpage($query)
{
// Only touch the main query
if (!$query->is_main_query())
return;
//Look for empty post_type, meaning start page (?) TODO: Really?
//http://wordpress.stackexchange.com/questions/18013/how-do-you-use-a-cpt-as-the-default-home-page
if (empty($query->query_vars['post_type']) && 0 != $query->query_vars['page_id']) {
$query->set('post_type', array(
'story',
'page'
));
}
}
/**
* Add the Story post type to the front page static page dropdown under Settings > Reading
*
* http://wordpress.stackexchange.com/questions/18013/how-do-you-use-a-cpt-as-the-default-home-page
*
* @param $pages
* @param $r
*
* @return array
*/
function story_add_to_frontpage_dropdown($pages, $r)
{
if ((isset($r['name']) && ('page_on_front' === $r['name']))) {
$args = array(
'post_type' => 'story'
);
$stacks = get_posts($args);
$pages = array_merge($pages, $stacks);
}
return $pages;
}
/**
* Fix the Story CPT slug so it does not conflict with pages or posts.
*
* @param $slug
* @param $post_ID
* @param $post_status
* @param $post_type
* @param $post_parent
* @param $original_slug
*
* @return mixed
*/
function story_fix_slug($slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug)
{
global $wpdb;
//These post types will never clash
$post_types_no_conflict = array(
'story',
'post',
'page',
'attachment'
);
$post_types_sql = function() use ($post_types_no_conflict)
{
$ret = '';
foreach ($post_types_no_conflict as $post_type)
$ret .= "'{$post_type}', ";
return substr($ret, 0, -2);
};
//If we are creating the unique slug for an affected post type
if (in_array($post_type, $post_types_no_conflict)) {
$unique = false;
//While we haven't found a unique link
while ($unique !== true) {
$post_name_check = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN (" . $post_types_sql() . ") AND ID != %d LIMIT 1", $slug, $post_ID));
//Not unique? Append some numbers and let's try again!
if ($post_name_check) {
$slug = "{$slug}-2";
} else {
//If unique, we're done
$unique = true;
}
}
return $slug;
} else
return $slug;
}
/**
* Remove the slug from published post permalinks. Only affect our CPT though.
*
* http://kellenmace.com/remove-custom-post-type-slug-from-permalinks/
*/
function story_preview_url_remove_base($post_link, $post)
{
if ('story' != $post->post_type || 'publish' != $post->post_status) {
return $post_link;
}
$post_link = str_replace('/' . $post->post_type . '/', '/', $post_link);
return $post_link;
}
/**
* Have WordPress match postname to any of our public post types (page, post, race)
* All of our public post types can have /post-name/ as the slug, so they better be unique across all posts
* By default, core only accounts for posts and pages where the slug is /post-name/
*
* TODO: Make sure this is 100% safe, especially the "2 != count( $query->query )"
*
* @param $query
*/
function story_modify_query_remove_base($query)
{
// Only touch the main query
if (!$query->is_main_query())
return;
// Only noop our very specific rewrite rule match
if (2 != count($query->query) || !isset($query->query['page'])) {
return;
}
// 'name' will be set if post permalinks are just post_name, otherwise the page rule will match
if (!empty($query->query['name'])) {
$query->set('post_type', array(
'story',
'page',
'post'
));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment