Category listing with thumbnail and description on home page

Note: This is for older versions of WP < 3.9 prior to the new media upload being introduced

Here’s how to add fields and save the values on the category edit screen as well as a method of adding an image upload field.

Adding fields to category edit screen

To start we need to get some code showing up on the category edit screen.

add_action( 'edit_category_form_fields', 'my_category_custom_fields' );
add_action( 'edit_category', 'save_my_category_custom_fields' );

function my_category_custom_fields( $tag ) {
    // your custom field HTML will go here
    // the $tag variable is a taxonomy term object with properties like $tag->name, $tag->term_id etc...

    // we need to know the values of our existing entries if any
    $category_meta = get_option( 'category_meta' );
    ?>
    <tr class="form-field">
        <th scope="row" valign="top"><label for="category-title"><?php _e("Title"); ?></label></th>
        <td>
            <input id="category-title" name="category_meta[<?php echo $tag->term_id ?>]Category listing with thumbnail and description on home page" value="<?php if ( isset( $category_meta[ $tag->term_id ] ) ) esc_attr_e( $category_meta[ $tag->term_id ]['title'] ); ?>" />
            <span class="description"><?php _e('Enter an alternative title for this category.'); ?></span>
        </td>
    </tr>
    <!-- rinse & repeat for other fields you need -->
    <?php
}

The simplest way to store our custom values is in the options table (there should be a taxonomy-meta table really but never mind). This way we only need to make one query to get the meta data for all of our categories. If anyone has a better idea for storage then speak up!

function save_my_category_custom_fields() {
    if ( isset( $_POST['category_meta'] ) && !update_option('category_meta', $_POST['category_meta']) )
        add_option('category_meta', $_POST['category_meta']);
}

For a checkbox you’re simply storing true or false so you’d use category_extras[$tag->term_id][show_on_home] for the name attribute and use the value stored in $category_meta to determine if it’s checked or not.

You may want to add some extra processing or sanitisation to the save function – mine is just a quick n dirty example.

The image field

This is a fair bit of code and quite complicated so I won’t explain it all here but the comments describe the purpose of each function. We can discuss in the comments if you want to.

The following functions add a link to the category edit screen which brings up the wordpress media library/image upload popup. You can then upload a picture and click to use it. You’ll then have the image ID and thumbnail url available to you with the other meta above.

// add the image size you need
add_image_size( 'category_thumb', 125, 125, true );

// setup our image field and handling methods
function setup_category_image_handling() {
    // add the image field to the rest
    add_action( 'edit_category_form_fields', 'category_image' );

    global $pagenow;
    if ( is_admin() ) {
    add_action( 'admin_init', 'fix_async_upload_image' );
    if ( 'edit-tags.php' == $pagenow ) {
        add_thickbox();
        add_action('admin_print_footer_scripts', 'category_image_send_script', 1000);
    } elseif ( 'media-upload.php' == $pagenow || 'async-upload.php' == $pagenow ) {
        add_filter( 'media_send_to_editor', 'category_image_send_to_editor', 1, 8 );
        add_filter( 'gettext', 'category_image_replace_text_in_thickbox', 1, 3 );
    }
    }
}
add_action( 'admin_init', 'setup_category_image_handling' );

// the taxonomy edit screen image field
function category_image( $tag ) {
    // get our category meta data
    $category_meta = get_option('category_meta');
    ?>
           <tr class="form-field hide-if-no-js">
               <th scope="row" valign="top"><label for="taxonomy-image"><?php _e("Image"); ?></label></th>
               <td>
                   <div id="taxonomy-image-holder">
               <?php if( !empty($category_meta[$tag->term_id]['image']) ) { ?>
                   <img style="max-width:100%;display:block;" src="https://wordpress.stackexchange.com/questions/5955/<?php echo esc_attr( $category_meta[ $tag->term_id ]["image']['thumb'] ); ?>" alt="" />
                   <a id="taxonomy-image-select" class="thickbox" href="media-upload.php?is_term=true&amp;type=image&amp;TB_iframe=1"><?php _e('Change image'); ?></a>
                   <a class="deletion" id="taxonomy-image-remove" href="#remove-image">Remove image</a>
               <?php } else { ?>
                   <a id="taxonomy-image-select" class="thickbox" href="media-upload.php?is_term=true&amp;type=image&amp;TB_iframe=1"><?php _e('Choose an image'); ?></a>
               <?php } ?>
                   </div>
                   <input type="hidden" name="category_meta[<?php echo $tag->term_id ?>][image][id]" value="<?php if( isset($category_meta[ $tag->term_id ]['image']['id']) ) echo esc_attr($category_meta[ $tag->term_id ]['image']['id']); ?>" class="tax-image-id" />
                   <input type="hidden" name="category_meta[<?php echo $tag->term_id ?>][image][thumb]" value="<?php if( isset($category_meta[ $tag->term_id ]['image']['thumb']) ) echo esc_attr($category_meta[ $tag->term_id ]['image']['thumb']); ?>" class="tax-image-thumb" />
               <span class="description"><?php _e('A category image.'); ?></span></td>
           </tr>
    <?php
}

   // required for uploading images on non post/page screens
   function fix_async_upload_image() {
       if(isset($_REQUEST['attachment_id'])) {
           $GLOBALS['post'] = get_post($_REQUEST['attachment_id']);
       }
   }

   // are we dealing with the taxonomy edit screen?
   function is_category_context() {
       if ( isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'],'is_term') !== false ) {
           return true;
       } elseif ( isset($_REQUEST['_wp_http_referer']) && strpos($_REQUEST['_wp_http_referer'],'is_term') !== false ) {
           return true;
       } elseif ( isset($_REQUEST['is_term']) && $_REQUEST['is_term'] !== false ) {
           return true;
       }
       return false;
   }

   // replace Insert Into Post text with something more appropriate
   function category_image_replace_text_in_thickbox($translated_text, $source_text, $domain) {
       if ( is_category_context() ) {
           if ('Insert into Post' == $source_text) {
               return __('Use this image', MB_DOM );
           }
       }
       return $translated_text;
   }

   // output a script that sets variables on the window object so that they can be accessed elsewhere
   function category_image_send_to_editor( $html, $id, $attachment ) {
       // context check might not be necessary, and, might not work in all cases
       if ( is_category_context() ) {
           $item = get_post($id);
           $src = wp_get_attachment_image_src($id,'thumbnail',true); // 0 = url, 1 = width, 2 = height, 3 = icon(bool)
           ?>
           <script type="text/javascript">
               // send image variables back to opener
               var win = window.dialogArguments || opener || parent || top;
               win.TI.id = <?php echo $id ?>;
               win.TI.thumb = '<?php echo $src[0]; ?>';
           </script>
           <?php
       }
       return $html;
   }

   // write out the javascript that handles what happens when a user clicks to use an image
   function category_image_send_script() { ?>
       <script>
           self.TI = {};
           var tb_position;

           function send_to_editor(h) {
               // ignore content returned from media uploader and use variables passed to window instead
               jQuery('.tax-image-id').val( self.TI.id );
               jQuery('.tax-image-thumb').val( self.TI.thumb );

               // display image
               jQuery('#taxonomy-image-holder img, #taxonomy-image-remove').remove();
               jQuery('#taxonomy-image-holder')
                   .prepend('<img style="max-width:100%;display:block;" src="'+ self.TI.thumb +'" alt="" />')
                   .append('<a class="deletion" id="taxonomy-image-remove" href="#remove-image">Remove image</a>');

               jQuery('#taxonomy-image-select').html('Change image');

               // close thickbox
               tb_remove();
           }

           (function($){
               $(document).ready(function() {

                   tb_position = function() {
                       var tbWindow = $('#TB_window'), width = $(window).width(), H = $(window).height(), W = ( 720 < width ) ? 720 : width;

                       if ( tbWindow.size() ) {
                           tbWindow.width( W - 50 ).height( H - 45 );
                           $('#TB_iframeContent').width( W - 50 ).height( H - 75 );
                           tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
                           if ( typeof document.body.style.maxWidth != 'undefined' )
                               tbWindow.css({'top':'20px','margin-top':'0'});
                       };

                       return $('a.thickbox').each( function() {
                           var href = $(this).attr('href');
                           if ( ! href ) return;
                           href = href.replace(/&width=[0-9]+/g, '');
                           href = href.replace(/&height=[0-9]+/g, '');
                           $(this).attr( 'href', href + '&width=" + ( W - 80 ) + "&height=" + ( H - 85 ) );
                       });
                   };
                   $(window).resize(function(){ tb_position(); });

                   $("#taxonomy-image-select').click(function(event){
                       tb_show("Choose an image:", $(this).attr("href"), false);
                       return false;
                   });
                   $('#taxonomy-image-remove').live('click',function(event){
                       $('#taxonomy-image-select').html('Choose an image');
                       $('#taxonomy-image-holder img').remove();
                       $('input[class^="tax-image"]').val("");
                       $(this).remove();
                       return false;
                   });
               });
           })(jQuery);
       </script>
       <?php
   }

Accessing the information

In your my_function bit taken from your own answer you would then access the metadata like so:

function my_function(){
    $args=array(
    'orderby' => 'name',
    'order' => 'ASC'
    );
    $categories=get_categories($args);

    // get our category meta data and exit out if there isn't any
    $category_meta = get_option('category_meta');
    if ( !$category_meta )
        return;

    echo '<ul style="list-style-type:none;margin:0;padding:0">';
    foreach($categories as $category) {
        // skip unticked categories & categories with no meta data
        if ( !isset( $category_meta[ $category->term_id ] ) || ( isset( $category_meta[ $category->term_id ] ) && !$category_meta[ $category->term_id ]['show_on_home'] ) )
            continue;
        echo '<li><a style="display:block;margin-top:20px;" href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . '>' . $category_meta[ $category->term_id ]['title'].'</a>';
        if ( "" != $category_meta[ $category->term_id ]['image']['id'] )
            echo wp_get_attachment_image( $category_meta[ $category->term_id ]['image']['id'], 'category_thumbnail', false, array( 'alt' => $category->name, 'class' => '' ) );
        echo $category->description . '</li>';
        } 
    echo '</ul>';
}

Leave a Comment