I am trying to replace the categories metabox with what looks and works like the tags metabox because there’s too much hierarchy and scrolling to check the appropriate categories and sub-categories isn’t an option. So in my case tags’-like metabox is better.
This is how I am doing it:
/*
* Non-hierarchal metabox for categories
* (like the tags metabox)
*
* SOURCES:
* http://wordpress.stackexchange.com/a/50098
* http://wordpress.stackexchange.com/a/49048
* http://wordpress.stackexchange.com/a/48816
*
*/
// De-register categories metabox
add_action( 'admin_menu', 'flatsy_remove_meta_box' );
function flatsy_remove_meta_box() {
remove_meta_box( 'categorydiv', 'post', 'normal' );
}
// Add new taxonomy meta box
add_action( 'add_meta_boxes', 'flatsy_add_custom_cat_meta_box' );
function flatsy_add_custom_cat_meta_box() {
add_meta_box( 'flatsy_categorydiv', 'Categories', 'flatsy_custom_cat_metabox', 'post', 'side', 'core' );
}
// This function determines what displays in your metabox
function flatsy_custom_cat_metabox( $post ) {
$defaults = array('taxonomy' => 'category');
if ( !isset($box['args']) || !is_array($box['args']) )
$args = array();
else
$args = $box['args'];
extract( wp_parse_args($args, $defaults), EXTR_SKIP );
$tax_name = esc_attr($taxonomy);
$taxonomy = get_taxonomy($taxonomy);
$disabled = !current_user_can($taxonomy->cap->assign_terms) ? 'disabled="disabled"' : '';
?>
<div class="tagsdiv" id="<?php echo $tax_name; ?>">
<div class="jaxtag">
<div class="nojs-tags hide-if-js">
<p><?php echo $taxonomy->labels->add_or_remove_items; ?></p>
<textarea name="<?php echo "tax_input[$tax_name]"; ?>" rows="3" cols="20" class="the-tags" id="tax-input-<?php echo $tax_name; ?>" <?php echo $disabled; ?>><?php echo get_terms_to_edit( $post->ID, $tax_name ); // textarea_escaped by esc_attr() ?></textarea></div>
<?php if ( current_user_can($taxonomy->cap->assign_terms) ) : ?>
<div class="ajaxtag hide-if-no-js">
<label class="screen-reader-text" for="new-tag-<?php echo $tax_name; ?>"><?php echo $box['title']; ?></label>
<div class="taghint"><?php echo $taxonomy->labels->add_new_item; ?></div>
<p><input type="text" id="new-tag-<?php echo $tax_name; ?>" name="newtag[<?php echo $tax_name; ?>]" class="newtag form-input-tip" size="16" autocomplete="off" value="" />
<input type="button" class="button tagadd" value="<?php esc_attr_e('Add'); ?>" tabindex="3" /></p>
</div>
<p class="howto"><?php echo esc_attr( $taxonomy->labels->separate_items_with_commas ); ?></p>
<?php endif; ?>
</div>
<div class="tagchecklist"></div>
</div>
<?php if ( current_user_can($taxonomy->cap->assign_terms) ) : ?>
<p class="hide-if-no-js"><a href="#titlediv" class="tagcloud-link" id="link-<?php echo $tax_name; ?>"><?php echo $taxonomy->labels->choose_from_most_used; ?></a></p>
<?php endif; ?>
<?php
}
That piece of code works as it should… see…
…except that it isn’t saving the category metadata when the post is saved. A little bit of searching revealed that I must be doing something like this:
<?php
add_action( 'save_post', 'cd_meta_box_save' );
function cd_meta_box_save( $post_id )
{
// Bail if we're doing an auto save
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
// if our nonce isn't there, or we can't verify it, bail
if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return;
// if our current user can't edit this post, bail
if( !current_user_can( 'edit_post' ) ) return;
// now we can actually save the data
$allowed = array(
'a' => array( // on allow a tags
'href' => array() // and those anchors can only have href attribute
)
);
// Make sure your data is set before trying to save it
if( isset( $_POST['my_meta_box_text'] ) )
update_post_meta( $post_id, 'my_meta_box_text', wp_kses( $_POST['my_meta_box_text'], $allowed ) );
if( isset( $_POST['my_meta_box_select'] ) )
update_post_meta( $post_id, 'my_meta_box_select', esc_attr( $_POST['my_meta_box_select'] ) );
// This is purely my personal preference for saving check-boxes
$chk = isset( $_POST['my_meta_box_check'] ) && $_POST['my_meta_box_select'] ? 'on' : 'off';
update_post_meta( $post_id, 'my_meta_box_check', $chk );
}
?>
But as I am dealing with the default functionality (i.e. how/what wordpress already does with tags metabox), I want to know what checks are in place for save_post
for ‘category’ and ‘tag’ meta boxes and how WordPress does it by default.
// Save post metadata when a post is saved.
add_action( 'save_post', 'flatsy_save_cat_meta' );
function flatsy_save_cat_meta( $post_id, $post, $update ) {
return 'WHAT DO I DO HERE? HOW DOES WORDPRESS DO IT FOR THE TAGS METABOX?';
}
AND if that’s not how it’s done, what should the code look like when I am converting the category metabox to look like a tag metabox and vice-versa (two cases)?
CLARIFICATION: I don’t want to change Categories from hierarchical to non-hierarchical. I just want a tags-like metabox for categories. If I wanted a non-hierarchical taxonomy I’d simply have registered a custom taxonomy.
2 s
It’s informative to check out the /wp-admin/post.php
file, that contains the edit_post()
function that calls wp_update_post()
, which is a wp_insert_post()
wrapper.
Here’s a skeleton for saving the assigned category terms:
/**
* Saving assigned category terms (skeleton)
*/
add_action( 'admin_action_editpost', function()
{
add_filter( 'wp_insert_post_data', function( $data, $parr )
{
add_action( 'save_post_post', function( $post_ID, $post ) use ( $parr )
{
if(
isset( $parr['_wpnonce'] )
&& wp_verify_nonce( $parr['_wpnonce'], 'update-post_' . absint( $post_ID ) )
&& current_user_can( 'manage_categories' )
&& function_exists( 'wpse_save_assigned_cats' )
&& ! did_action( 'wpse_save_assigned_cats' )
) {
wpse_save_assigned_cats( $post_ID, $parr );
do_action( 'wpse_save_assigned_cats' );
}
}, 10, 2 );
return $data;
}, 10, 2 );
} );
where our helper function wpse_save_assigned_cats()
is based on the edit_post()
function:
/**
* Helper function based on the cat/tax handling of the edit_post() functions
*/
function wpse_save_assigned_cats( $post_ID, $parr )
{
if( ! empty( $parr['tax_input']['category'] ) && $post_ID > 0 )
{
// Change the comma seperated string of category names,
// in $parr['tax_input']['category'], to an array of cats id
$input_cats = explode( ',', trim( $parr['tax_input']['category'], " \n\t\r\0\x0B," ) );
$clean_cats = array();
foreach ( $input_cats as $cat_name )
{
// Don't allow empty categories
if ( empty( $cat_name ) )
continue;
// Check if there already exists such a category
$_cat = get_terms( 'category', array(
'name' => $cat_name,
'fields' => 'ids',
'hide_empty' => false,
) );
// The category name already exists
if ( ! empty( $_cat ) )
{
// Collect the (first) category id
$clean_cats[] = intval( $_cat[0] );
}
else
{
// Create the category, since it doesn't exists
$cat_id = wp_create_category( $cat_name );
// Collect the category id
if( $cat_id > 0 )
$clean_cats[] = $cat_id;
}
}
// Current post's category IDs
$cats = (array) wp_get_post_categories( $post_ID, array( 'fields' => 'ids' ) );
// Unique array of category IDs
$post_categories = array_unique( array_merge( $cats, $clean_cats ) );
// Assign the categories to the current post
wp_set_post_categories( $post_ID, $post_categories );
}
}
Previous :
Here’s my Friday answer, so it might need some testing 😉
I just re-registered the category
taxonomy as non-hierarchical with:
'hierarchical' => false,
Then the category box showed up like this:
and saving terms worked as expected.
Here’s my testing code snippet, so you can try it further:
add_action( 'init', function()
{
global $wp_rewrite;
register_taxonomy( 'category', 'post', array(
'hierarchical' => false,
'query_var' => 'category_name',
'rewrite' => array(
'hierarchical' => true,
'slug' => get_option('category_base') ? get_option('category_base') : 'category',
'with_front' => ! get_option('category_base') || $wp_rewrite->using_index_permalinks(),
'ep_mask' => EP_CATEGORIES,
),
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'_builtin' => true,
'labels' => array(
'name' => __( 'Categories' ),
'singular_name' => __( 'Category' ),
'search_items' => __( 'Search Categories' ),
'popular_items' => null,
'all_items' => __( 'All Categories' ),
'edit_item' => __( 'Edit Category' ),
'update_item' => __( 'Update Category' ),
'add_new_item' => __( 'Add New Category' ),
'new_item_name' => __( 'New Category Name' ),
'separate_items_with_commas' => null,
'add_or_remove_items' => null,
'choose_from_most_used' => null,
),
'capabilities' => array(
'manage_terms' => 'manage_categories',
'edit_terms' => 'manage_categories',
'delete_terms' => 'manage_categories',
'assign_terms' => 'edit_posts',
),
) );
} );