Category listing with thumbnail and description on home page

I really need a comprehensive solution to this, so I’m offering up almost a quarter of my rep in bounty 🙂

I would like to have a plugin that creates a custom category listing on my home page. To do this, I’d like the “Category Edit” screen to be augmented with some additional functions as described below…

Read More
  • Adds “Upload/Delete Image” handler to the Category Edit Screen for a Given Category (this allows the end user to upload an image that will be used to represent the category. Ideally, it should automatically be resized to 125 pixels wide by the plugin when uploaded.
  • Adds “Category Title” input field to the Category Edit Screen for a Given Category. This is in addition to the existing default “Name” field.
  • Adds a checkbox titled “Show on homepage listing” to allow selective display of each category on the home page listing.
  • When the home page is viewed, the plugin appends the_content with the custom category listing, including each category’s custom thumbnail and title.

The resulting markup should be an unordered list with no nesting, like so…

<ul class="custom-categories">
    <li>
        <span><a href="link-to-category-1"><img src="the-category-1-image" /></a></span>
        <a href="link-to-category-1">Category 1 Title</a> The category description text goes here
    </li>
    <li>
        <span><a href="link-to-category-2"><img src="the-category-2-image" /></a></span>
        <a href="link-to-category-2">Category 2 Title</a> The category description text goes here
    </li>
    etc...
</ul>

Here’s a stub file I’ve started…

add_filter( 'the_content', 'cb_category_listing' );

function cb_category_listing( $content )
{
    if ( is_home() ) {
        $cat_args['title_li'] = '';
        $cat_args['exclude_tree'] = 1;
        $cat_args['exclude'] = 1;
        $myContent = wp_list_categories(apply_filters('widget_categories_args', $cat_args)); 
        $content .= $myContent;
    }
    return $content;
}

Related posts

Leave a Reply

3 comments

  1. 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 ?>][title]" 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="<?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>';
    }
    
  2. Here was my first attempt…

    add_filter( 'the_content', 'cb_category_listing' );
    
    function cb_category_listing( $content )
    {
        if ( is_home() ) {
            $cat_args['title_li'] = '';
            $cat_args['exclude_tree'] = 1;
            $cat_args['exclude'] = 1;
            $myContent = wp_list_categories(apply_filters('widget_categories_args', $cat_args)); 
            $content .= $myContent;
        }
        return $content;
    }
    

    Obviously, this simply creates a listing of category names with a link to each category. So it gets me started. However, I still need…

    • A means to associate a category thumbnail image with each category, and show it on the category listing in my code above.

    • A means to include a custom category title, apart from the category name, and show it on the category listing (in place of the “Category Name”)

    • A means to show/hide each category from the listing. Ideally, with a checkbox “Show on homepage listing” that can be edited on the category’s edit screen.

    Here’s my updated code. Getting there, but still haven’t tackled the thumbnail image or adding the custom fields (thumbnail image, custom title, show/hide) to the category editor admin…

    function _add_my_filter() {
        if ( is_home() OR is_sticky() )
        {
        add_filter( 'the_content', 'my_function' );
        }
    }
    add_action('template_redirect', '_add_my_filter');
    
    function my_function(){
        $args=array(
        'orderby' => 'name',
        'order' => 'ASC'
        );
        $categories=get_categories($args);
        echo '<ul style="list-style-type:none;margin:0;padding:0">';
        foreach($categories as $category) { 
            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->name.'</a>';
            echo $category->description . '</li>';
            } 
        echo '</ul>';
    }