Using Chosen for a replacement taxonomy metabox

If you’ve ever created many taxonomies for a post type, you know that the add/edit screen can get very unwieldy with all of those metaboxes, especially with hierarchical taxonomies. And then sometimes you have something that is a taxonomy but doesn’t need items added very often, especially in client work where there are often predetermined options that may occasionally be added to by the client, which can be done in the taxonomy’s admin screen.

This is where we can use Chosen, a JS plugin to make multi-selects much more user-friendly, and just so happens to blend right in to the WordPress admin. You can take multiple taxonomies and put them into a single metabox. It’s not limited to taxonomies, either – you can use it for post meta (custom fields) as well. I’ve used it to select taxonomy terms for which a post is “essential” – optgroups came in very handy there. I actually would love to see tag selection look and behave more like this in core, but there would be more work to be done for adding new terms.

The basic idea is that you enqueue the script and style on the appropriate admin page (NOT all admin pages) and then initialize the field(s) based on a class or ID. I usually just put the initialize right in the metabox itself, but you could always be clean and put it in the admin_head if you want. From there, you show all terms in the taxonomy in a select field (including empties) and use the nifty selected() function to show which ones are associated with the current post. When saving, it’s as easy as wp_set_post_terms() after the usual checks for nonce, etc. You’ll also want to hide the original taxonomy metabox(es) as well, of course.

Full Github Gist (updated July 10, 2012):


<?php
/**
* WordPress Chosen Taxonomy Metabox
* Author: Helen Hou-Sandi
*
* Use Chosen for a replacement taxonomy metabox in WordPress
* Useful for taxonomies that aren't changed much on the fly and are
* non-hierarchical in nature, as Chosen is for flat selection only.
* You can always use the taxonomy admin screen to add/edit taxonomy terms.
* Categories need slightly different treatment from the rest in order to
* leverage the correct form field name for saving without interference.
*
* Includes two filters:
* * ctm_taxes_and_types for filtering the array of post types and taxonomies.
* Post types and taxonomies should both be the slug versions.
* This filter is more of an example for the moment,
* as you would likely edit the member variable directly.
* * ctm_tax_label for filtering the label used in the meta box display.
* Receives the $tax (slug name of the taxonomy) and the $post_type slug.
*
* Example screenshot: http://cl.ly/2T2D232x172G353i2V2C
*
* Chosen: http://harvesthq.github.com/chosen/
*/
class ChosenTaxMetabox {
public $types_and_taxes = array (
'post' => array (
'post_tag',
'category',
'customtax',
),
'custom_post_type' => array (
'category',
'customtax',
),
);
public function __construct() {
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'save_post', array( $this, 'save_post' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
$this->types_and_taxes = apply_filters( 'ctm_taxes_and_types', $this->types_and_taxes );
}
/**
* Remove the default meta boxes for each taxonomy and add the replacement
* Note that the naming is different based on whether or not the taxonomy is hierarchical
*/
public function add_meta_boxes() {
foreach ( $this->types_and_taxes as $post_type => $taxes ) {
foreach ( $taxes as $tax ) {
if ( is_taxonomy_hierarchical( $tax ) )
remove_meta_box( $tax . 'div', $post_type, 'side' );
else
remove_meta_box( 'tagsdiv-' . $tax, $post_type, 'side' );
}
add_meta_box( 'chosen-tax', 'Choose Terms', array( $this, 'meta_box_display' ), $post_type, 'side', 'default' );
}
}
/**
* Display the replacement meta box itself
*/
public function meta_box_display() {
$post_type = get_post_type();
// double check to make sure there are taxonomies to show
if ( empty( $this->types_and_taxes[$post_type] ) )
return;
$taxes = $this->types_and_taxes[$post_type];
wp_nonce_field( 'chosen-save-tax-terms', 'chosen_taxonomy_meta_box_nonce' );
?>
<script type="text/javascript">
jQuery(document).ready(function($){
$( '.chzn-select' ).chosen();
});
</script>
<?php
foreach ( $taxes as $tax ) {
/**
* You may want to check if the current user can assign terms to
* the taxonomy in question before going any further.
* It will not save their assignments anyway, but it's a bit of a
* funny user experience.
*/
$taxonomy = get_taxonomy( $tax );
$label = apply_filters( "ctm_tax_label" , $taxonomy->labels->name, $tax, $post_type );
// add more args if you want (e.g. orderby)
$terms = get_terms( $tax, array( 'hide_empty' => 0 ) );
$current_terms = wp_get_post_terms ( get_the_ID(), $tax, array('fields' => 'ids') );
// category needs a special treatment for the input name
if ( 'category' == $tax )
$name = 'post_category';
else
$name = "tax_input[$tax]";
?>
<p><label for="<?php echo $tax; ?>"><?php echo $label; ?></label>:</p>
<p><select name="<?php echo $name; ?>[]" class="chzn-select widefat" data-placeholder="Select one or more" multiple="multiple">
<?php
foreach ( $terms as $term ) {
// hierarchical taxonomies save by IDs, whereas non save by slugs
if ( is_taxonomy_hierarchical( $tax ) )
$value = $term->term_id;
else
$value = $term->slug;
?>
<option value="<?php echo $value; ?>"<?php selected( in_array( $term->term_id, $current_terms ) ); ?>><?php echo $term->name; ?></option>
<?php } ?>
</select>
</p>
<?php
}
}
/**
* When saving the post, check to see if the taxonomy has been emptied out.
* If so, it will not exist in the tax_input array and thus WP won't be aware of it,
* so we have to take of emptying the terms for the object.
*/
public function save_post( $post_id ) {
// verify nonce
if ( ! isset($_POST['chosen_taxonomy_meta_box_nonce'] ) || ! wp_verify_nonce( $_POST['chosen_taxonomy_meta_box_nonce'], 'chosen-save-tax-terms' ) )
return;
// check autosave
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return;
$post_type = get_post_type( $post_id );
// double check to make sure there are taxonomies to process
if ( empty( $this->types_and_taxes[$post_type] ) )
return;
$taxes = $this->types_and_taxes[$post_type];
// if nothing is posted for a relevant taxonomy, remove the object terms!
// otherwise, WP will take care of assigning the terms
foreach ( $taxes as $tax ) {
// once again, category gets special treatment
if ( 'category' === $tax )
$input = isset( $_POST['post_category'] ) ? $_POST['post_category'] : '';
else
$input = isset( $_POST['tax_input'][$tax] ) ? $_POST['tax_input'][$tax] : '';
if ( empty( $input ) ) {
$taxonomy = get_taxonomy( $tax );
if ( $taxonomy && current_user_can( $taxonomy->cap->assign_terms ) ) {
wp_set_object_terms( $post_id, '', $tax );
}
}
}
}
/**
* Chosen JS and CSS enqueue – assumes you are doing this in a theme
* with the JS, CSS, and sprite files in themefolder/js/chosen/
* You'd want to use plugins_url() instead if using this in a plugin
*/
function admin_enqueue_scripts() {
$screen = get_current_screen();
$post_types = array_keys( $this->types_and_taxes );
if ( 'post' !== $screen->base || ! in_array( $screen->post_type, $post_types ) )
return;
wp_enqueue_script( 'chosen', get_template_directory_uri().'/js/chosen/chosen.jquery.min.js', array( 'jquery' ), '1.0' );
wp_enqueue_style( 'chosen', get_template_directory_uri().'/js/chosen/chosen.css' );
}
}
new ChosenTaxMetabox;

If you see anything that could use improving, let me know!

Using Chosen for a replacement taxonomy metabox

16 thoughts on “Using Chosen for a replacement taxonomy metabox

    1. Helen says:

      Somehow I missed a ton of comments – sorry about that! If you’re doing this in the theme, you can put the code as-is into functions.php (or, I’d probably actually recommend putting it into a separate file that you include in functions.php) and then put the Chosen folder in a js folder in the theme. With Chosen, you’ll only need the jQuery minified version of the Javascript plus the CSS and images. If you put it somewhere else, just change the enqueue call. If you do this in a plugin, you’ll want to use `plugins_url()` instead of `get_template_directory_uri()`. Similarly, a child theme will need `get_stylesheet_directory_uri()` instead of the get_template version.

      Like

  1. Jay Collier says:

    Hi, Helen.

    I love this concept, and I’ve installed per your comment. Nothing happening in the back-end yet.

    So, when included into a theme’s functions.php file, do the individual taxonomies have to be included in this code, or does it pick them all up into a single meta box?

    Like

    1. Helen says:

      You’ll need to specify which taxonomies are used in the `$taxes` array, which are then put into a single metabox. There may be some other things that need tweaking here and there depending on how and where you’re using the code – I haven’t done a ton of testing in various use cases. Are you getting any errors or is just nothing happening at all?

      Like

  2. Jay Collier says:

    Since my coding expertise is primarily copy and paste, I’ll be spending some time with our developer this afternoon to better understand the code and answer your questions. This is very promising!

    Like

  3. Hi Helen,

    Thanks for the great info! I just wrote about styling forms to match Gravity Forms (enhanced using Chosen jQuery), but never thought about using it for the admin.

    What a great idea…I’m gonna try it out and see how it works.

    Thanks again!

    Tony

    Like

  4. Your enthusiasm on make.wordpress.org is infectious. 🙂

    We’ll include this Chosen on a feature release of our own plugin.

    Thx for sharing.

    Like

  5. kingbt says:

    Very nice code. Used in my site but it needs improvements like:
    #1 I can only add terms and I should be able to remove them too.
    #2 does not work with tags ( should use $term->slug; for tags )
    #3 the IFs in the hhs_add_admin_scripts function seem not to work.
    #4 when I edit the post type I see “Select one or more” even if there are selected taxonomies from a previous edit

    Like

  6. A little bit of knowledge goes a long way in all situations in life.
    Buying a car is no different! That means you need to read advice from experts, as detailed below, to ensure that when you shop for that car,
    you really know what you’re doing and how to get the best deal.

    Like

    1. Chosen is pretty simple – you can’t have “Add New” on the post edit screen. You can always go to the taxonomy’s edit screen to add terms. I think Select 2 can do “Add New” behavior, although you’d probably have to do some magic to make it work with hierarchical taxonomies. I would definitely say that this is geared toward fixed or rarely-changing sets of terms.

      Like

  7. Hello Helen! I have some troubles with doing metaboxes in post-page. Coud you give some suggestions how do such functionallity: we know, for example, after entered data in custom fields we press save button so we can enter as many values in custom fields as we want, but in the site show only values. And I need such metaboxes that can allow enter as title (its a key) and the text (its a value) and than press the save button in order to continue enter new data in metaboxes, so we can enter data in the same metabox as many as we need. And in the site show both data: title and its text as many times as we’ve entered in metaboxes. Thanks in advance.

    Like

Leave a reply to kingbt Cancel reply