The Category's metabox is set when the category is registered with register_taxonomy function. The default behavior of this function is to set the metabox callback (meta_box_cb) to a wordpress function called post_categories_meta_box. It is this function that renders the "+Add Category" Button. Unfortunately, this metabox does not allow you to selectively hide this button. As other answerers pointed out, it will hide that button based on the taxonomy's edit_terms capability, so you can effectively hide it by limiting who has this capability. However, this does not work if, like me, you want to hide this button without limiting capabilities.
To do this you need to supply your own meta_box_cb. I did this by simply copying post_categories_meta_box and deleting that button.
My custom function:
function custom_post_categories_meta_box_without_add_new( $post, $box ) {
$defaults = array( 'taxonomy' => 'category' );
if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) {
$args = array();
} else {
$args = $box['args'];
}
$parsed_args = wp_parse_args( $args, $defaults );
$tax_name = esc_attr( $parsed_args['taxonomy'] );
$taxonomy = get_taxonomy( $parsed_args['taxonomy'] );
?>
<div id="taxonomy-<?php echo $tax_name; ?>" class="categorydiv">
<ul id="<?php echo $tax_name; ?>-tabs" class="category-tabs">
<li class="tabs"><a href="#<?php echo $tax_name; ?>-all"><?php echo $taxonomy->labels->all_items; ?></a></li>
<li class="hide-if-no-js"><a href="#<?php echo $tax_name; ?>-pop"><?php echo esc_html( $taxonomy->labels->most_used ); ?></a></li>
</ul>
<div id="<?php echo $tax_name; ?>-pop" class="tabs-panel" style="display: none;">
<ul id="<?php echo $tax_name; ?>checklist-pop" class="categorychecklist form-no-clear" >
<?php $popular_ids = wp_popular_terms_checklist( $tax_name ); ?>
</ul>
</div>
<div id="<?php echo $tax_name; ?>-all" class="tabs-panel">
<?php
$name = ( 'category' === $tax_name ) ? 'post_category' : 'tax_input[' . $tax_name . ']';
// Allows for an empty term set to be sent. 0 is an invalid term ID and will be ignored by empty() checks.
echo "<input type='hidden' name='{$name}[]' value='0' />";
?>
<ul id="<?php echo $tax_name; ?>checklist" data-wp-lists="list:<?php echo $tax_name; ?>" class="categorychecklist form-no-clear">
<?php
wp_terms_checklist(
$post->ID,
array(
'taxonomy' => $tax_name,
'popular_cats' => $popular_ids,
)
);
?>
</ul>
</div>
</div>
<?php
}
(I hope I can include here my custom function which as I mentioned is largely copied from wordpress. If there are legal reasons I cannot do this, please let me know.)
Applied to my custom taxonomy
register_taxonomy(
"my_custom_taxonomy",
"my_custom_type",
array(
...
'hierarchical' => true,
'meta_box_cb' => 'custom_post_categories_meta_box_without_add_new'
)
);
The downside of this is that, unlike the default, your meta_box_cb will not change if wordpress updates their styling.
Note that this only works if you are registering your taxonomies manually. If you are using a plugin, it may not allow you to configure the meta_box_cb.
#category-adder { display: none ;}
to any CSS you enqueue onadmin_enqueue_scripts