Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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...

  • 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;
}
share|improve this question
add comment

3 Answers

up vote 6 down vote accepted

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>';
}
share|improve this answer
    
Wow, thanks sanchothefat. I appreciate your effort. I've cobbled most of it together myself, but I'm going to accept your answer and work that into my code. –  Scott B Jan 13 '11 at 22:38
    
Sanchothefat, you deserve the bounty I put up on this, did you get any? –  Scott B Jan 14 '11 at 2:22
    
How difficult would it be to amend your code to save the images in a folder under uploads called "category-images"? And If the folder does not exist, create it. –  Scott B Jan 14 '11 at 2:25
    
I'm not sure to be honest. There might be a filter for the upload directory path. I like to use the built-in media library though because it makes the uploads and thumbnail generation a doddle. Check out adambrown.info/p/wp_hooks - its a great resource outlining every hook and filter in wordpress. I didn't get the bounty :( must've expired. –  sanchothefat Jan 14 '11 at 10:52
add comment

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>';
}
share|improve this answer
add comment

The easy way would be with something like this http://wordpress.org/extend/plugins/category-images/ . Or you could write your own hook.

share|improve this answer
    
Thanks Wyck, I've tested this plugin but it doesn't exactly do ALL I need it to. For example, I don't want to replace the link text with the image, I want the image AND the link text. –  Scott B Jan 5 '11 at 20:59
1  
It appears that the thumbnail is only shown on the single.php page for the post. I need a custom category listing (sort of an index page) of all categories. I've updated my question with more specificity and added a bounty for a conclusive solution. –  Scott B Jan 5 '11 at 21:53
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.