Create a WordPress Upload Meta Box

This post was updated on March 22, 2013. Please review the code changes below.

WordPress makes it pretty easy to capture additional information with your posts through the use of custom meta boxes. Adding checkboxes, select options, textareas, radio buttons, and other input elements are easy.

But giving users the ability to upload files from a post screen requires a little more work.

If you’re not interested in tapping into the Media Uploader, then here’s how you can programmatically create a WordPress upload meta box.

TL;DR: All of this code is available in this GitHub repository as a plugin that can be downloaded and installed in your copy of WordPress.

Though there is some additional code, such as JavaScript and localization files, the bulk of the plugin is as follows:

<?php
/*
Plugin Name: WordPress Upload Custom Meta Box 
Plugin URI: http://github.com/tommcfarlin/WordPress-Upload-Custom-Meta-Box/
Description: An example plugin for how to include a metabox for attaching files to your WordPress posts outside of the media uploader.
Version: 1.0
Author: Tom McFarlin
Author URI: http://tommcfarlin.com
Author Email: tom@tommcfarlin.com
License:

  Copyright 2012 - 2013 Tom McFarlin (tom@tommcfarlin.com)

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License, version 2, as 
  published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  
*/

if( ! defined( 'UMB_VERSION' ) ) {
	define( 'UMB_VERSION', 1.0 );
} // end if

class Upload_Meta_Box {

	/--------------------------------------------
	 * Attributes
	 --------------------------------------------/

	 // Represents the nonce value used to save the post media
	 private $nonce = 'wp_upm_media_nonce';

	/--------------------------------------------
	 * Constructor
	 --------------------------------------------/

	 /**
	  * Initializes localiztion, sets up JavaScript, and displays the meta box for saving the file
	  * information.
	  */
	 public function __construct() {
	 
	 	// Localization, Styles, and JavaScript
	 	add_action( 'init', array( $this, 'plugin_textdomain' ) );
	 	add_action( 'admin_enqueue_scripts', array( $this, 'register_admin_scripts' ) );
	 
	 	// Setup the meta box hooks
	 	add_action( 'add_meta_boxes', array( $this, 'add_file_meta_box' ) );
	 	add_action( 'save_post', array( $this, 'save_custom_meta_data' ) );

	 } // end construct
	 
	/--------------------------------------------
	 * Localization, Styles, and JavaScript
	 --------------------------------------------/
	
	/**
	 * Defines the text domain for localization.
	 */
	public function plugin_textdomain() {
		load_plugin_textdomain( 'umb', false, dirname( plugin_basename( __FILE__ ) ) . '/lang' );
	} // end plugin_textdomain
	
	/**
	 * Addings the admin JavaScript
	 */
	public function register_admin_scripts() {
		wp_enqueue_script( 'umb-admin', plugins_url( 'WordPress-Upload-Meta-Box/js/admin.js' ), array( 'jquery'), UMB_VERSION );
	} // end register_scripts
	
	/--------------------------------------------
	 * Hooks
	 --------------------------------------------/
	
	/**
	 * Introduces the file meta box for uploading the file to this post.
	 */ 
	public function add_file_meta_box() {
	
		add_meta_box(
			'post_media',
			__( 'Media', 'umb' ),
			array( $this, 'post_media_display' ),
			'post',
			'side'
		);
	
	} // add_file_meta_box
	
	/**
	 * Adds the file input box for the post meta data.
	 *
	 * @param		object	$post	The post to which this information is going to be saved.
	 */
	public function post_media_display( $post ) {
	
		wp_nonce_field( plugin_basename( __FILE__ ), $this->nonce );
	
		$html = '<input id="post_media" type="file" name="post_media" value="" size="25" />';
		
		$html .= '<p class="description">';
		if( '' == get_post_meta( $post->ID, 'umb_file', true ) ) {
			$html .= __( 'You have no file attached to this post.', 'umb' );
		} else {
			$html .= get_post_meta( $post->ID, 'umb_file', true );
		} // end if
		$html .= '</p><!-- /.description -->';
		
		echo $html;
	
	} // end post_media
	
	/**
	 * Determines whether or not the current user has the ability to save meta data associated with this post.
	 *
	 * @param		int		$post_id	The ID of the post being save
	 * @param		bool				Whether or not the user has the ability to save this post.
	 */
	public function save_custom_meta_data( $post_id ) {
	
		// First, make sure the user can save the post
		if( $this->user_can_save( $post_id, $this->nonce ) ) { 

			// If the user uploaded an image, let's upload it to the server
			if( ! empty( $_FILES ) && isset( $_FILES['post_media'] ) ) {
			
				// Upload the goal image to the uploads directory, resize the image, then upload the resized version
				$goal_image_file = wp_upload_bits( $_FILES['post_media']['name'], null, wp_remote_get( $_FILES['post_media']['tmp_name'] ) );

				// Set post meta about this image. Need the comment ID and need the path.
				if( false == $goal_image_file['error'] ) {
				
					// Since we've already added the key for this, we'll just update it with the file.
					update_post_meta( $post_id, 'umb_file', $goal_image_file['url'] );
		
				} // end if/else

			} // end if
	
		} // end if
	
	} // end update_data

	/--------------------------------------------
	 * Helper Functions
	 --------------------------------------------/

	/**
	 * Determines whether or not the current user has the ability to save meta data associated with this post.
	 *
	 * @param		int		$post_id	The ID of the post being save
	 * @param		bool				Whether or not the user has the ability to save this post.
	 */
	function user_can_save( $post_id, $nonce ) {
		
	    $is_autosave = wp_is_post_autosave( $post_id );
	    $is_revision = wp_is_post_revision( $post_id );
	    $is_valid_nonce = ( isset( $_POST[ $nonce ] ) && wp_verify_nonce( $_POST[ $nonce ], plugin_basename( __FILE__ ) ) );
	    
	    // Return true if the user is able to save; otherwise, false.
	    return ! ( $is_autosave || $is_revision ) && $is_valid_nonce;
	 
	} // end user_can_save

} // end class
$GLOBALS['upload-meta-box'] = new Upload_Meta_Box();

If you want to reuse this code for different custom post types, you would have to update the administrator’s JavaScript file to check for the existance of another element.

40 Comments

Thank you for the tutorial. It actually worked exactly as advertised and was so nice to find such a nice little script without the bloat that does a job really well.

I have one request which for the life of me I can’t figure out. Hoping you might be able to help.

I am looking to do the same thing as this tutorial with one change though. Instead of having the ‘upload’ button, I am trying to find a way of getting the media_button() input there.

Talking about the same little ‘insert media’ button that you get above the ‘post content’ area when writing a new post. *between the title and the editor*

Reason:
I actually use another plugin for inserting kaltura video into my posts. The way their plugin works is that they added a new button next to the ‘add media’ button that WordPress uses.

screenshot:
http://awesomescreenshot.com/0dfb1nn6a

By any chance have you done this before, or seen a tutorial on how to access the media_button in a custom field?

thanks for your time

Thank you, works beautifully! One small detail: any way to have the file name remain in the form field after upload?

    Currently, no. You wouldn’t be able to display the name of the file in the input box because of browser limitations. If it were to happen after upload, it’s possible, but it’d need some customization – I’m not sure that’s something that’s universally applicable to everyone who uses the plugin.

    If you’re interested in this customization, let me know!.

Hi, me again! Had one more question – just noticed that if that no file is uploaded, there is an automatic error…any way (surely there is :)) to make the upload optional ?

cheers, and thanks again for sharing this!
-jennyb

    This shouldn’t happen – the plugin is written in such a way that if an image isn’t supplied, it simply skips the logic. This is the first issue I’ve heard about this.

    Shoot me a link to a page on your blog and I’ll take a look.

      Hey – thanks for getting back to me so quickly. Not sure what you could see from the frontend as it’s a backend issue. Ultimately everything works great, just the error message when creating a new post and no file is uploaded. I could send you my modified code maybe?

        Oh! My fault – I misread the post on which this comment was posted (was commenting in the backend :)).

        So this must be the result of something that’s changed since I first wrote the code. Unfortunately, I don’t have time to go back and revise this right now.

        If this is something you really need some help with, feel free to contact me and let me know!

          Hello Tom, Jenny,
          Same issue for me unfortunately :(
          I thought it was maybe due to the javascript test but I can’t find the real problem… This feature would help me since I’m trying to create a CPT on which you can add multiple files.
          If someone find something, that would be great to share it ;)
          Regards.

          I’ll make a note to look into this as soon as I can. Perhaps tomorrow, but it may be next week.

          I’ll post here as soon as I have a solution in place.

    Hey guys,

    This has been fixed. I’ve written a plugin full of example code that’s available here for both review, and for download.

    It’s been tested against WordPress 3.5.1 – and should be fully compliant.

    I’ll be updating the post as well.

    Hope this helps!

Hi, me again – the plugin was working great, and then just suddenly stopped. Impossible to save any files uploaded. Haven’t made any significant changes since – at a bit of a loss…any idea what might cause a conflict?

many thanks!!
-jennyb

    Hey Jenny,

    I spent sometime looking and testing the plugin this morning. I wasn’t able to reproduce the problem :(.

    I did, however, implement a couple of very, very minor changes. If you download the latest version it may help, but I’m skeptical.

    The only other thing that I can think of is that it another plugin may be causing a conflict, though if you’ve not changed anything I’m not sure what else to suggest :/.

      Hey Tom,

      Thanks for the speedy reply – will give the update a try *fingers crossed*. Otherwise, I saw the 2 part tutorial you did on tutsplus – any different than this code? (sorry, I could compare, but thought asking would be quicker :)).

      cheers!
      -jennyb

PS & for info: zip download link isn’t working on github

Quick question – is there a specific function used to call the file, as there is with a post thumbnail or a metadata string?

    Can you clarify your question just a little bit? Are you looking for a function to call in the context of your theme after the above code has been implemented, or something else?

      Yes that’s exactly what I’m looking for. Some way to call the file within the theme. For example, I’d want to use this for a restaurant website where menus are a custom post type, and each menu has an associated PDF version. Is there a function I can use in the post template to call the PDF version once I’ve uploaded it using this plugin? And thanks for replying so quickly!

        Once the plugin is active, you’d use this:

        get_post_meta( $post_id, 'umb_file' );

        To get the path to the file :).

          Hey Tom,

          Sorry it took me so long to say thanks. As it were I’m also having the same problem as the user above – not having any submit button or option to save once the file has been specified in the upload path. Any help?

          Colin

Hi,
I’ve installed this plugin in 3.6.1, selected a file but nothing happens. In latest Firefox

“You have no file attached to this post.” this all I can see

Is this compatible with wordpress 3.8.1? I have just downloaded the latest version of the plugin and installed it but it doesn’t seem to save the file?

The module appears in the post admin and you can click to upload but when saving it still says You have no file attached to this post.

Am I doing something wrong?

I have tried with the default twenty fourteen theme as well as my own custom theme but its the same result…. I have also de-activated all plugins apart from this one.

any help would be appreciated
J

    Honestly, I’m not sure, John — I haven’t taken a look at the project since 3.8.1 has been released (or really, since 3.8 was released).

    My best advice would be to setup a couple of debug statements throughout the code to see where something is failing (or where it’s not behaving correctly) and then go from there.

      Hi Tom, I turned Debugging on in the wordpress config file but it didn’t return any errors at all, not even in the log?

        The next thing I’d try is doing a line-by-line `print_r` or `var_dump` of what’s going on to see where the uploading is filing.

        I wish I could be more help. but I’m backed up with other stuff right now.

Hi, im having a problem to activate the plugin, displays this error:

Warning: Cannot modify header information – headers already sent by (output started at /home/notiluca/www/wp-content/themes/twentyeleven/functions.php:775) in /home/notiluca/www/wp-includes/pluggable.php on line 876

Warning: Cannot modify header information – headers already sent by (output started at /home/notiluca/www/wp-content/themes/twentyeleven/functions.php:775) in /home/notiluca/www/wp-includes/pluggable.php on line 876

    Looks like you’re missing something in your functions.php file (specifically on line 775). Perhaps you’re doing an echo, print_r, or var_dump, or something similar.

Trackbacks and Pingbacks

Filuppladdning i meta boxar - Daniel HanssonSeptember 30, 2011 at 1:56 am

[...] i meta boxar Postat 30 september, 2011 av daniel http://moreco.de/upload-files-wordpress-meta-box/ Det här inlägget postades i Länkar och märktes fileupload, meta boxar av daniel. Bokmärk [...]

[...] from More Code, has written an excellent tutorial on How To Upload Files in WordPress Meta Boxes. A sweet and short, yet quite detailed, step-by-step instruction batch, with all the necessary code [...]

Leave a Reply

Name and email address are required. Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>