If you're anything like me, sometimes a post type doesn't function like you want, or you need to do something slightly different. Many plugins register new custom post types, label them wonky or make them public when you want it to be private, etc. Whatever the case, you do not need the plugin developer to have a filter prior to the registration.
For example:
<?php | |
add_action( 'init', 'gs_register_books_cpt' ); | |
/** | |
* Register Books Custom Post Type | |
*/ | |
function gs_register_books_cpt() { | |
// change 'gs_books' to whatever your text_domain is. | |
/** Setup labels */ | |
$labels = array( | |
'name' => x_( 'Books', 'gs_books' ), | |
'singular_name' => x_( 'Book', 'gs_books' ), | |
'add_new' => x_( 'Add New', 'gs_books' ), | |
'all_items' => x_( 'All Books', 'gs_books' ), | |
'add_new_item' => x_( 'Add New Book', 'gs_books' ), | |
'edit_item' => x_( 'Edit Book', 'gs_books' ), | |
'new_item' => x_( 'New Book', 'gs_books' ), | |
'view_item' => x_( 'View Book', 'gs_books' ), | |
'search_items' => x_( 'Search Books', 'gs_books' ), | |
'not_found' => x_( 'No Books found', 'gs_books' ), | |
'not_found_in_trash' => x_( 'No Books found in trash', 'gs_books' ), | |
'parent_item_colon' => x_( 'Parent Book:', 'gs_books' ), | |
'menu_name' => x_( 'Amazon Books', 'gs_books' ) | |
); | |
/** Setup args */ | |
$args = array( | |
'labels' => $labels, | |
'description' => x_( 'Amazon Books post type', 'gs_books' ), | |
'public' => true, | |
'menu_position' => 20, | |
'supports' => array( 'title', 'editor', 'excerpt', 'page-attributes', ), | |
'has_archive' => 'books', | |
'rewrite' => array( 'slug' => 'book', ), | |
); | |
/** Register Custom Post Type */ | |
register_post_type( 'gs_books', $args ); | |
} |
This creates a menu item labeled Amazon Books, but what if I want that to be just Books?
So if I wanted to change the label, I would do something like this:
<?php | |
add_action( 'registered_post_type', 'gs_books_label_rename', 10, 2 ); | |
/** | |
* Modify registered post type menu label | |
* | |
* @param string $post_type Registered post type name. | |
* @param array $args Array of post type parameters. | |
*/ | |
function gs_books_label_rename( $post_type, $args ) { | |
if ( 'gs_books' === $post_type ) { | |
global $wp_post_types; | |
$args->labels->menu_name = __( 'Books', 'gs_books' ); | |
$wp_post_types[ $post_type ] = $args; | |
} | |
} |
This is only one method to change custom post types defaults. Alternatively, you can also hook into init
at a later time to change them.
<?php | |
add_action( 'init', 'gs_books_label_rename', 999 ); | |
/** | |
* Modify registered post type menu label | |
* | |
*/ | |
function gs_books_label_rename() { | |
global $wp_post_types; | |
$wp_post_types['gs_books']->labels->menu_name = __( 'Books', 'gs_books' ); | |
} |
Travis, this is the clearest explanation of modifying a CPT that I’ve found. Thanks so much!
I have everything working the way I want, except when it comes to capabilities for the CPT.
The plugin I’m modifying has assigned all Post capabilities to a single capability, as in this example:
‘capabilities’ => array(
‘publish_posts’ => ‘manage_downloads’,
‘edit_posts’ => ‘manage_downloads’,
I’ve created a new capability (using Members) called ‘manage_own_downloads’ to which I want to reassign some capabilities. Here’s what one of the lines looks like within the function I’ve created based on your article:
$args->capabilities->edit_posts = ‘manage_own_downloads’;
But this isn’t working.
Yea capabilities can be problematic and requires knit gloves as it can make a lot disappear. My recommendation is this: use Members plugin & read Justin’s post Meta Capabilities for Custom Post Types on these as well.
Hi Travis,
I tried your example above, but they didn’t work for the ‘rewrite’ arg. Any ideas?
add_action( ‘registered_post_type’, ‘gs_books_label_rename’, 10, 2 );
/**
* Modify registered post type menu label
*
* @param string $post_type Registered post type name.
* @param array $args Array of post type parameters.
*/
function gs_books_label_rename( $post_type, $args ) {
if ( ‘project’ === $post_type ) {
global $wp_post_types;
$args->rewrite[‘slug’] = ‘%project_category%’;
$wp_post_types[ $post_type ] = $args;
}
}
Regards,
Daniel
Hello Daniel,
That is correct because you have missed the rewrite functions. So you would need to use the following two functions: `add_rewrite_rule()` and `add_permastruct()`. For your specific question, I believe this is what you need (please note that this is untested):
Thanks,
Travis
Hi Travis,
Thanks for this post, it seems to be the best way to modify a custom post type.
Like Daniel, I am attempting to modify the slug for a custom post type. It all seems to be working as expected. I just have a few questions:
– Why is the $permastruct_args[‘feed’] = $permastruct_args[‘feeds’]; line needed?
– Also, is global $wp_post_types; actually necessary? I don’t seem to need it.
– What is the advantage of this method over re-registering the post type? (with register_post_type, which also seems to work)
Thanks again for making this information accessible.
Ash
Thank you Travis. Very appreciated!
Hello Travis, thank you for sharing this. I wanted to know if it’s possible to just change the slug of a CPT using your above method?
I used this technique in a child theme to add excerpt to a custom post that had not in parent theme, but I do not see Excerpt in the Screen Options on top of post. Why?
I got the labels working but thats not what i’m trying to accomplish. I want to remove the archive option from a custom post type.
I tried has_archive but it doesnt seem to do anything…
i tried:
add_action( ‘registered_post_type’, ‘test1’, 10, 2 );
function test1( $post_type, $args ) {
if ( ‘location’ === $post_type ) {
global $wp_post_types;
$wp_post_types[ $post_type ]->has_archive = false;
}
}
add_action( ‘init’, ‘test2’, 999 );
function test2() {
global $wp_post_types;
$wp_post_types[‘location’]->has_archive = false;
}