Adding term descriptions to the quick edit box in WordPress

Writing term descriptions for categories, tags, and custom taxonomies can be a real chore in WordPress. It is easy enough to edit term names and slugs with the quick edit box but if you’d like to edit descriptions it will be necessary to open up the full edit screen. This is less than optimal if we wish to write or maintain complex taxonomies with dozens or even hundreds of terms.

WordPress is highly customizable and extensible so there’s a way of making this easier. Of course, this being WordPress, things aren’t nearly as straight-forward as they could be. The problem, in this case, is that you cannot hook the quick_edit_custom_box action without the presence of a custom column. Since description is among the default columns this action is not called and there is no opportunity to modify the contents of the quick edit box. The current solution (as of version 4.0) is a bit of a hack: use a hidden custom column to trigger the quick_edit_custom_box action and output the necessary HTML.

Before I proceed, I’d like to provide credit where credit is due. My code is based entirely on the work of G.M., whose excellent answer to this question on the WordPress Development StackExchange led me in the right direction. I have made several changes to his code, adapting it for use with several taxonomies at once. I also use Markdown in term descriptions which requires a bit of a different approach, as you will see.

I will start at the end so you can see how all the pieces fit together as I explain each in turn:

function quick_term_init() {
  // Filter this to add or subtract taxonomies
  $term_description_taxonomies = apply_filters( 'term_description_taxonomies', array( 'category', 'post_tag' ) );

  // Setup all the necessary actions and filters
  foreach ( $term_description_taxonomies as $tax ) {
    add_action( "edited_{$tax}", 'quick_term_description_save' );
    add_filter( "manage_edit-{$tax}_columns", 'quick_term_hidden_column' );
    add_filter( "manage_{$tax}_custom_column", 'quick_term_hidden_column_contents', 10, 3 );
    add_filter( "get_user_option_manageedit-{$tax}columnshidden", 'quick_term_hidden_column_visibility' );
  }
}
add_action( 'init', 'quick_term_init' );

This initializes all necessary actions and filters and creates a new filter to alter what taxonomies this code applies to. By default it activates quick edit functionality for term descriptions in the default category and post_tag taxonomies but you can easily hook other taxonomies like so:

function my_taxonomy_quick_edit( $taxonomies ) {
  $taxonomies[] = 'my_taxonomy';
  return $taxonomies;
}
add_filter( 'term_description_taxonomies', 'my_taxonomy_quick_edit' );

With this code the quick edit box on my_taxonomy screens will include descriptions. You can also remove any of the built-in taxonomies as needed.

Of course, you can also specify an array of taxonomy names and skip the filtering. I tend to design for modularity whenever possible but it isn’t strictly necessary.

Now to setup a custom column and allow for it to be hidden from view:

function quick_term_hidden_column( $columns ) {
  $columns['_description'] = '';
  return $columns;
}

function quick_term_hidden_column_visibility( $columns ) {
  $columns[] = '_description';
  return $columns;
}

The third, more complex function provisions our new hidden column with content: the raw term description. By “raw” I mean that this content comes directly out of the database without passing through filters like term_description, which I happen to use to render Markdown. This allows for us to use Markdown, HTML, shortcodes, and whatever else in term descriptions while continuing to display rendered HTML output in the administrative interface. Here we go:

function quick_term_hidden_column_contents( $_, $column_name, $term_id ) {
  if ( $column_name === '_description' ) {

    // Get current screen, if available
    $screen = get_current_screen();

    // Set the taxonomy from the current screen; if this is unavailable, try the request (after saving the quick edit box `get_current_screen` returns `null`)
    if ( !empty( $screen ) ) {
      $taxonomy = $screen->taxonomy;
    } elseif ( !empty( $_REQUEST['taxonomy'] ) ) {
      $taxonomy = sanitize_text_field( $_REQUEST['taxonomy'] );
    } else {
      $taxonomy = '';
    }

    // Output the raw term description if there is one to be found
    if ( !empty( $term_id ) && !empty( $taxonomy ) ) {
      $term = get_term( $term_id, $taxonomy );
      if ( !empty( $term ) )
        echo $term->description;
    }
  }
}

A few notes about this chunk of code: the awkward-sounding manage_{$this->screen->taxonomy}_custom_column filter that calls this function takes three arguments starting with a blank string (hence the $_ placeholder variable). The get_current_screen() function returns null when we update a term from the quick edit box so we use the $_REQUEST['taxonomy'] variable instead. If you’re only using this code with a single taxonomy you can skip all this conditional stuff and explicitly define $taxonomy near the end of this function like so:

$term = get_term( $term_id, 'category' );

Next we’ll need a function to output the HTML and jQuery necessary to activate quick edit functionality:

function quick_term_description_edit( $column, $screen, $taxonomy = '' ) {
  // Check to see if we're in the right place
  if ( empty( $taxonomy ) || $screen !== 'edit-tags' || $column !== '_description' )
    return;

  // Fetch the target taxonomy and ensure the current user can edit terms
  $tax = get_taxonomy( $taxonomy );
  if ( ! current_user_can( $tax->cap->edit_terms ) )
    return;

  ?><fieldset>
    <div class="inline-edit-col">
    <label>
      <span class="title"><?php _e( 'Description' ); ?></span>
      <span class="input-text-wrap">
      <textarea name="description" rows="3" class="ptitle"></textarea>
      </span>
    </label>
    </div>
  </fieldset>
  <script>
  jQuery('#the-list').on('click', 'a.editinline', function(){
    var now = jQuery(this).closest('tr').find('td.column-_description').text();
    jQuery(':input[name="description"]').text(now);
  });
  </script><?php
}
add_action( 'quick_edit_custom_box', 'quick_term_description_edit', 10, 3 );

Since this code hooks the quick_edit_custom_box action it will be called on every admin screen where custom columns are specified—including posts and custom post types. To avoid mishaps and collisions we first check to ensure that we’re editing a taxonomy that has the hidden _description column. Next we check to ensure that the current user has the appropriate permissions to proceed.

You’ll notice that the HTML output (which merely copies what is found in the WordPress core) does not include the actual contents of the term description. This is provisioned via jQuery when the user clicks on the quick edit link using the .find function.

Our final function pulls in the current taxonomy from the request, ensures that the context is correct and that the user has the appropriate permissions, and then updates the term in the database:

function quick_term_description_save( $term_id ) {
  $tax = get_taxonomy( $_REQUEST['taxonomy'] );
  if (
    current_filter() === 'edited_' . $tax->name
    && current_user_can( $tax->cap->edit_terms )
  ) {
    $description = filter_input( INPUT_POST, 'description', FILTER_SANITIZE_STRING );
    remove_action( current_filter(), __FUNCTION__ ); // Removing action to avoid recursion
    wp_update_term( $term_id, $tax->name, array( 'description' => $description ) );
  }
}

WordPress handles the rest with AJAX magic, updating the column that has just been edited with whatever changes have been made. This includes our hidden _description column—which means you can edit the same terms over and over again without refreshing the page.

So there you have it: now you can quick edit term descriptions for any taxonomy you wish without conflicting with any non-plaintext formatting system you wish to use.

Finally, a note: this is untested development code; use at your own risk! I have incorporated this into Ubik, my library of useful WordPress snippets and hacks, but it might change at any time. Comments are welcome and I’ll be fixing and updating this code as needed. My WordPress level is not “ninja” but I try my best!

Related posts

Respond

Markdown and HTML enabled in comments.
Your email address will not be published. Required fields are marked *