Create a custom “Download” post type using WordPress

Download Custom Post Type in WordPress

I’ve been asked several times about the download functionality on my blog so I’ve decided to create this 2-part tutorial on how you can create your own download section on your site using custom post types.

In this part I’ll teach you

  1. How the register the post type
  2. A custom “Download Categories” taxonomy
  3. A custom meta box with information about the download
  4. Create the archive
  5. Create the download counter
  6. Conclusion

The next part will be about creating a download shortcode, a widget and the archive for the custom taxonomy.

Download the code on Github

The code in this tutorial should be in your themes functions.php or your own functionality plugin.

Create the Post Type

<?php
/**
 * Add the download post type
 */
add_action( 'init', 'jayj_create_download_post_type' );

function jayj_create_download_post_type() {

  register_post_type( 'jayj_download', array(
	'labels' => array(
		'name' => 'Downloads',
		'add_new' => 'Add New',
		'add_new_item' => 'Add New Download',
		'edit' => 'Edit',
		'edit_item' => 'Edit Download',
		'new_item' => 'New Download',
		'view' => 'View Download',
		'view_item' => 'View Download',
		'search_items' => 'Search Downloads',
		'not_found' => 'No downloads found',
		'not_found_in_trash' => 'No downloads found in Trash',
	),
	'description' => 'The latest downloads',
	'public' => true,
	'show_ui' => true,
	'exclude_from_search' => true,
	'supports' => array( 'title', 'excerpt', 'thumbnail', 'custom-fields' ),
	'has_archive' => 'downloads', // The archive slug
	'rewrite' => array( 'slug' => 'download' ),
  ));
}

?>

This will register the custom post type and give you a ‘Downloads’ menu in your dashboard, as seen on the image below.

I won’t go over all the values – if you want a better understanding on what’s going on, I recommend reading this tutorial on custom post types by Justin Tadlock.

But I’d like to explain this part

'has_archive' => 'downloads', // The archive slug
'rewrite' => array( 'slug' => 'download' )

It means that your download archive can be found at example.com/downloads and the individual downloads can be found at example.com/download/%slug%


A custom “Download Categories” taxonomy

Now that we have the post type, let’s create a custom taxonomy called “Download Categories”. That way you can categorize your downloads just like you can with posts. Examples of download categories can be PDFs, PSD files, WordPress themes and so on.

After the register_post_type(); function, add the following code

/**
 * Custom "Download Categories" taxonomy
 */
register_taxonomy( 'download-categories', array( 'jayj_download' ),
  array(
	'public' => true,
	'labels' => array( 'name' => 'Download Categories', 'singular_name' => 'Download Category' ),
	'hierarchical' => true,
	'rewrite' => array( 'slug' => 'download-types' )
  )
);

'hierarchical' => true makes the taxonomy behave like categories. If you want it to behave like tags, set it to false.

When you create or edit a download, you'll now have have the option to choose a category


Create a custom meta box

The third step is to add custom data fields to the add/edit download page. To give you an idea of what I mean, take a look at the following image

I’ve decided to keep it simple in this tutorial. You should add and edit them so they fit to your needs.

Let’s move on!

/**
 * Adds the download meta box for the download post type
 */
function jayj_meta_box_download() {
  add_meta_box( 'jayj-meta-box-download', 'Download Settings', 'jayj_meta_box_download_display', 'jayj_download', 'normal', 'high' );
}

add_action( 'add_meta_boxes', 'jayj_meta_box_download' );

This will add the meta box to the jayj_download post type.
If you try it out and gets an error, don’t worry. That’s because we haven’t defined the jayj_meta_box_download_display() function yet. Let’s do that know.

The code has been breaked up into pieces. First part:

/**
 * Displays the download meta box
 */
function jayj_meta_box_download_display( $object, $box ) {

   // Setup some default values
   $defaults = array(
	'file' => '',
	'version' => '1.0',
	'postid' => '',
	// Add more!
   );

   // Get the post meta
  $download = get_post_meta( $object->ID, 'download', true );

  // Merge them
  $download = wp_parse_args( $download, $defaults );
}
?>

This part will set up some default values and merge them with the download post meta using the wp_parse_args() function. Some of the values are set to empty by default to prevent PHP notices when there’s no download meta.

Next part is the fields

<input type="hidden" name="jayj-meta-box-download" value="<?php echo wp_create_nonce( basename( __FILE__ ) ); ?>" />

<br />

<table class="form-table">

   <thead><tr><th><span class="description">None of the fields are required</span></th></tr></thead>

   <tr>
      <th><label for="download-hide">Hide download</label></th>
      <td>
         <?php $hide = get_post_meta( $object->ID, '_hide_download', true ); ?>
         <label><input type="checkbox" name="download-hide" id="download-hide" value="1" <?php checked( '1', $hide ); ?> />
         <span class="description">Check this if you don't want to show the download at the archive page</span></label>
      </td>
   </tr>

   <tr>
      <th><label for="download-file">File</label></th>
      <td>
         <input type="text" id="download-file" size="60" name="download-file" value="<?php echo esc_url( $download['file'] ); ?>" />
         <input type="button" id="upload-file-button"  class="button" value="Upload file" />
         <label for="download-file"><span class="description">Upload or link to download file</span></label>
      </td>
   </tr>

   <tr>
      <th><label for="download-version">Version</label></th>
      <td>
         <input type="text" id="download-version" name="download-version" value="<?php echo esc_attr( $download['version'] ); ?>" size="3" />
      </td>
   </tr>

   <tr>
      <th><label for="download-count">Download count</label></th>
      <td>
         <?php $count = isset( $object->ID ) ? get_post_meta( $object->ID, 'download_count', true ) : 0; ?>
         <input type="number" id="download-count" name="download-count" value="<?php echo absint( $count ); ?>" size="7" min="0" />
         <?php printf( '<p>This file has been downloaded <b>%d</b> times</p>', absint( $count ) ); ?>
      </td>
   </tr>

   <tr>
      <th><label for="download-postid">Post ID</label></th>
      <td>
         <label><input type="text" id="download-postid" name="download-postid" value="<?php echo absint( $download['postid'] ); ?>" size="3" />
         <span class="description">ID of the post which the download is associated with</span></label>
         <?php if ( !empty( $download['postid'] ) ) { ?>
            <p>This download is associated with <a href="<?php echo get_permalink( $download['postid'] ); ?>"><?php echo get_the_title( $download['postid'] ); ?></a></p>
         <?php } ?>
      </td>
   </tr>

</table>

The code might look long and confusing. It adds a table with all the fields and gets the values from the $download array defined earlier.

But we are not done yet. The metabox and the fields has been added, but we haven’t made a function that saves them yet.

/**
 * Save the download information
 */
function jayj_meta_box_download_save( $post_id, $post ) {

   /* Verify that the post type supports the meta box and the nonce before preceding. */
   if ( ! isset( $_POST['jayj-meta-box-download'] ) || ! wp_verify_nonce( $_POST['jayj-meta-box-download'], basename( __FILE__ ) ) )
	return $post_id;

   /* Don't save them if... */
   if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
   if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) return;
   if ( defined( 'DOING_CRON' ) && DOING_CRON ) return;
   if ( $post->post_type == 'revision' ) return;

   /* Get the post type object. */
   $post_type = get_post_type_object( $post->post_type );

   /* Check if the current user has permission to edit the post. */
   if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) )
	return $post_id;

/**
 * Add the download meta
 * CUSTOMIZE IF YOU'VE ADDED/EDITED/REMOVED FIELDS
 */
   update_post_meta( $post_id, 'download', array(
	'file' => esc_url_raw( $_POST['download-file'] ),
	'version' => strip_tags( $_POST['download-version'] ),
	'postid' => absint( $_POST['download-postid'] ),
	// Add your own if you've added fields
   ) ); 

   // Seperate the download count and "hide" from the rest
   update_post_meta( $post_id, 'download_count', absint( $_POST['download-count'] ) );
   update_post_meta( $post_id, '_hide_download', ( $_POST['download-hide'] == 1 ? 1 : 0 ) );
}

add_action( 'save_post', 'jayj_meta_box_download_save', 10, 2 );

This saves all the meta data, excerpt the count and “hide”, in a single array in a custom field called download. The download count is saved to download_count and the "hide" option is saved in _hide_download. They're apart from the others because it's easier to use them to do other things when they're alone.

As you might has noticed, there's a button called "Upload file" in the metabox. Currently it doesn't do anything but we make it open a lightbox window, just like when you add images, and upload the file.


Go to the next page for the rest of the tutorial

By in WordPress | Published September 2, 2011

+5

Posts in this series

8 Responses to Create a custom “Download” post type using WordPress

  1. Marlon siger:

    Really usefull! Thanks for sharing.

  2. Rajat Agarwal siger:

    Hi,

    Can you please throw any light on adding a “pay to download” method in custom download post type? Or any recommendation of any plugin which can be used to achieve this?

    Thanks,
    Rajat.

  3. Pingback: Create a custom “Download” post type using WordPress - Part 2 | Jayj.dk

  4. Pingback: Hello Downloads! | "Download" Post Type

  5. Pingback: welikesport by dogriad38370 - Pearltrees

  6. Pingback: Part 2: Create a custom "Download" post type using WordPress

Leave a Reply