Attaching Files To Your Posts Using WordPress Custom Meta Boxes, Part 2

Attaching Files To Your Posts Using WordPress Custom Meta Boxes, Part 2

Tutorial Details
  • Program: WordPress
  • Version (if applicable): 3.3
  • Difficulty: Intermediate
  • Estimated Completion Time: 1.5 hours

In the first post, we took a look at how to attach a file – specifically, a PDF – to WordPress posts and pages without having to use a plugin or third-party solution. At this point, you can only upload files – there’s no way to actually deactivate the link or delete the link to the file once it has been uploaded. In this post, we’ll take a look at how to provide some slightly better styling for the download link and how to extend the custom meta box functionality by allowing users to delete files after they’ve downloaded them.


Dress It Up

If you followed along with the code in the first post, then you should have a functional demo of how attaching a PDF to a WordPress post (or page) works; however, the overall presentation doesn’t look very good:

Before going any further, let’s clean this up a bit so that it looks a bit more integrated with the default theme. Remember that we’re using Twentyeleven as our default theme so that we’re all on the same page as we work through the tutorial.

First, let’s move the download link into a more logical location. If you’ve been following along since the first post, you’ll recall that we placed the download link in single.php which is located in the root of the theme directory. Open the file and locate the block of code that looks like this:

get_template_part( 'content', 'single' );
$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
	<a href="	<?php echo $doc['url']; ?>">
	Download PDF Here
	</a>

Copy all but the first line of code and remove it from the file. This should leave only a call to get_template_part.

Next, locate custom-single.php. This is a template file located in the root of the theme directory. Find the call to the_content() and then paste the code you just copied directly below it. This should result in the following block of code:

the_content();
$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
	<a href="	<?php echo $doc['url']; ?>">
	Download PDF Here
	</a> 

Now, let’s wrap the link in a container so that we can easily style it. I’m giving my container the ID of ‘wp_custom_attachment.’ Feel free to use whatever you like, just remember to refer to it correctly in your stylesheet.

Here’s my markup:

the_content();
$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
<div id="wp_custom_attachment">
	<a href="">
		Download PDF Here
	</a> 
</div><!-- #wp_custom_attachment -->

And here’s my CSS:

.wp_custom_attachment { 
	margin: 8px 0 8px 0;
	border: 1px solid #DDD;
	background: #EEE;
	padding: 8px;
	text-align: center;
	border-radius: 4px;
}

Permitting you’ve written everything correctly, the download link should now look like this:

Much better, right? It’s now styled such that it looks to be more tightly integrated with the theme. Remember to make the corresponding changes to page.php, as well (considering that we’re supporting file attachments on both posts and pages).


Download, When Available

Before moving on, we have one more small change to make to the code we just added. Right now, the download link is displayed unconditionally. This means that whether or not a post actually has a file, we’re displaying the download link.

Ideally, we only want to display the download link whenever there is a file to download. As such, we’ll need a conditional. Specifically, we’ll need to get the associated post meta data, determine if there is an actual URL for the file attachment. If so, we’ll display the link; otherwise, we won’t.

In the block of code we just added to content-single.php, place an opening if statement just above the opening wp_custom_attachment tag and close the statement just below the closing container tag. Right now, the code should look something like this:

the_content();
$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
if() { 
	<div id="wp_custom_attachment">
		<a href="">
			Download PDF Here
		</a> 
	</div><!-- #wp_custom_attachment -->
} // end if

We need to check the presence of the document’s URL. There are a number of ways to do this, but I typically check by taking a look at its URL attribute. The resulting code block is:

the_content();
$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
if(strlen(trim($doc['url'])) > 0) { 
	<div id="wp_custom_attachment">
		<a href="">
			Download PDF Here
		</a> 
	</div><!-- #wp_custom_attachment -->
} // end if

At this point, the download link should only display when there is a valid file attached to the give post or page. Try it out.


Deleting The File

At this point, we’ve tied up some loose ends from the previous post and we’re ready to finish up the functionality necessary to delete attachments.

Laying The Foundation

Recall from the last post that once the user attempts to upload a file, we have a serialization function that fires that’s responsible for actually writing the file to disk. For reference, this takes place in save_custom_meta_data.

In order to properly delete a file, we need to track the existing file’s location and whether or not a user has actually requested to delete the file. We’ll do that with a combination of an input box and an anchor.

First, locate the ‘wp_custom_attachment’ function that we created in the last post. This is where we added the file input element. Just below the input element add the following code (the full function will be provided below):

// Create the input box and set the file's URL as the text element's value
$html .= '<input type="text" id="wp_custom_attachment_url" name="wp_custom_attachment_url" value=" ' . $doc['url'] . '" size="30" />';

This will add an a text input element which tracks the value of the uploaded document’s URL. We’ll clean this up a bit later in the tutorial, but for now, let’s introduce an an anchor for deleting the file. Just below the two lines of code we just added, write the following:

$html .= '<a href="javascript:;" id="wp_custom_attachment_delete">' . __('Delete File') . '</a>';

Take note: we’ve given the anchor a unique ID. This will be necessary when we begin hooking up the administration area to handle user events. If you don’t give your anchor this ID, make note of whatever you do assign.

We’re not quite done yet. Remember how we setup the single post and page views to conditionally display the download link? We need to do the same thing for the delete link. Specifically, we need to only show the delete link if a document exists. So, in similar fashion, wrap the anchor in a conditional statement that checks for the presence of the document’s URL:

// Display the 'Delete' option if a URL to a file exists
if(strlen(trim($doc['url'])) > 0) {
	$html .= '<a href="javascript:;" id="wp_custom_attachment_delete">' . __('Delete File') . '</a>';
} // end if

It’s nothing too heavy, right? To be complete, here’s the full function as it stands:

function wp_custom_attachment() {

	wp_nonce_field(plugin_basename(__FILE__), 'wp_custom_attachment_nonce');
	
	$html = '

'; $html .= 'Upload your PDF here.'; $html .= '</p>'; $html .= '<input type="file" id="wp_custom_attachment" name="wp_custom_attachment" value="" size="25" />'; // Grab the array of file information currently associated with the post $doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true); // Create the input box and set the file's URL as the text element's value $html .= '<input type="text" id="wp_custom_attachment_url" name="wp_custom_attachment_url" value=" ' . $doc['url'] . '" size="30" />'; // Display the 'Delete' option if a URL to a file exists if(strlen(trim($doc['url'])) > 0) { $html .= '<a href="javascript:;" id="wp_custom_attachment_delete">' . __('Delete File') . '</a>'; } // end if echo $html; } // end wp_custom_attachment

We’ll revisit this function a little but later in the tutorial but, for now, test it out. First, navigate to a post that has no attachment. You should see the file input box and an empty input box. After uploading a file, you should see the input box contain the URL of the file followed by a link for deleting the file.

But we’re not done yet. After all, the delete link doesn’t actually do anything.

Wiring It Up

Next, locate the ‘js’ directory in the theme root. Add a new file called custom_attachment.js. We’ll write code for this momentarily, but the purpose of the file will be what allows us to actually delete the PDF that we’ve attached to a post.

After that, open up functions.php and add the following function at the end of the file:

function add_custom_attachment_script() {

	wp_register_script('custom-attachment-script', get_stylesheet_directory_uri() . '/js/custom_attachment.js');
	wp_enqueue_script('custom-attachment-script');

} // end add_custom_attachment_script
add_action('admin_enqueue_scripts', 'add_custom_attachment_script');

This function will read the JavaScript file that we just created and include it on any administrative page in the WordPress backend. Enqueuing and Registering scripts is beyond the scope of the tutorial, but I recommend reading up on it.

Next, let’s revisit the JavaScript file. Generally speaking, the code should do the following things:

  • Determine if the delete link is present
  • If the link is present, attach a custom event handler that clears out the text input that contains the URL of the file
  • Hide the link once the file has been marked for deletion

The source code is below and it has been fully commented to help explain what each line is doing:

jQuery(function($) {

	// Check to see if the 'Delete File' link exists on the page...
	if($('a#wp_custom_attachment_delete').length === 1) {

		// Since the link exists, we need to handle the case when the user clicks on it...
		$('#wp_custom_attachment_delete').click(function(evt) {
		
			// We don't want the link to remove us from the current page
			// so we're going to stop it's normal behavior.
			evt.preventDefault();
			
			// Find the text input element that stores the path to the file
			// and clear it's value.
			$('#wp_custom_attachment_url').val('');
			
			// Hide this link so users can't click on it multiple times
			$(this).hide();
		
		});
	
	} // end if

});

At this point, the file will not be deleted but you should have a functional view. Locate a page that has a file attached to it. Your custom meta box should look something like this:

After clicking on the ‘Delete Link’ anchor, the custom meta box should look like this:

If not, double-check your debugging console to verify that you don’t have any JavaScript errors.


Deleting The File

At this point, we’ve done all but actually delete the file. To do this, we’ll need to update the save_custom_meta_data function that we wrote in the first post. Recall that the functional includes a conditional checks the contents of the $_FILES collection coming from the POST request. If the collection is populated, then we serialization the file.

Since we’re attempting to delete the file, the $_FILES collection shouldn’t contain any data so all of our code will need to be contained in an else clause. The full source code for the function will be provided below, but here’s how the functional should work:

  • Check to see if there’s a document associated with the post
  • Check to see if the text box used for tracking the file’s URL is empty
  • If a file exists and the text box is empty, delete the file and update the associated meta data

This should be straightforward: We’ve given each post a text element that contains the URL to the file. If the file URL is empty, it means the user has clicked on the ‘Delete File’ link and is requesting to delete the file. Here’s how we can achieve just that:

// Grab a reference to the file associated with this post
$doc = get_post_meta($id, 'wp_custom_attachment', true);

// Grab the value for the URL to the file stored in the text element
$delete_flag = get_post_meta($id, 'wp_custom_attachment_url', true);

// Determine if a file is associated with this post and if the delete flag has been set (by clearing out the input box)
if(strlen(trim($doc['url'])) > 0 && strlen(trim($delete_flag)) == 0) {

	// Attempt to remove the file. If deleting it fails, print a WordPress error.
	if(unlink($doc['file'])) {
		
		// Delete succeeded so reset the WordPress meta data
		update_post_meta($id, 'wp_custom_attachment', null);
		update_post_meta($id, 'wp_custom_attachment_url', '');
		
	} else {
		wp_die('There was an error trying to delete your file.');
	} // end if/el;se
	
} // end if

Once the file is deleted, note that we also have to update the post meta data by emptying out the attachment’s value as well as the attachment’s URL value. In the odd case that the file doesn’t delete, we’re displaying a simple error message. Advanced error handling is beyond the scope of this post.

As promised, here’s the full serialization function:

function save_custom_meta_data($id) {

	/* --- security verification --- */
	if(!wp_verify_nonce($_POST['wp_custom_attachment_nonce'], plugin_basename(__FILE__))) {
	  return $id;
	} // end if
	  
	if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
	  return $id;
	} // end if
	  
	if(!current_user_can('edit_page', $id)) {
		return $id;
   	} // end if
	/* - end security verification - */
	
	// Make sure the file array isn't empty
	if(!empty($_FILES['wp_custom_attachment']['name'])) {
		
		// Setup the array of supported file types. In this case, it's just PDF.
		$supported_types = array('application/pdf');
		
		// Get the file type of the upload
		$arr_file_type = wp_check_filetype(basename($_FILES['wp_custom_attachment']['name']));
		$uploaded_type = $arr_file_type['type'];
		
		// Check if the type is supported. If not, throw an error.
		if(in_array($uploaded_type, $supported_types)) {

			// Use the WordPress API to upload the file
			$upload = wp_upload_bits($_FILES['wp_custom_attachment']['name'], null, file_get_contents($_FILES['wp_custom_attachment']['tmp_name']));
	
			if(isset($upload['error']) && $upload['error'] != 0) {
				wp_die('There was an error uploading your file. The error is: ' . $upload['error']);
			} else {
				add_post_meta($id, 'wp_custom_attachment', $upload);
				update_post_meta($id, 'wp_custom_attachment', $upload);		
			} // end if/else

		} else {
			wp_die("The file type that you've uploaded is not a PDF.");
		} // end if/else
		
	} else {

		// Grab a reference to the file associated with this post
		$doc = get_post_meta($id, 'wp_custom_attachment', true);
		
		// Grab the value for the URL to the file stored in the text element
		$delete_flag = get_post_meta($id, 'wp_custom_attachment_url', true);
		
		// Determine if a file is associated with this post and if the delete flag has been set (by clearing out the input box)
		if(strlen(trim($doc['url'])) > 0 && strlen(trim($delete_flag)) == 0) {
		
			// Attempt to remove the file. If deleting it fails, print a WordPress error.
			if(unlink($doc['file'])) {
				
				// Delete succeeded so reset the WordPress meta data
				update_post_meta($id, 'wp_custom_attachment', null);
				update_post_meta($id, 'wp_custom_attachment_url', '');
				
			} else {
				wp_die('There was an error trying to delete your file.');
			} // end if/el;se
			
		} // end if

	} // end if/else
	
} // end save_custom_meta_data
add_action('save_post', 'save_custom_meta_data');

By now, you’ve got a fully functioning custom meta box. Give it a try.


Cleaning It Up

We’ve got one last minor change to make just to make our UI complete. Remember the text input that we added earlier in the tutorial that’s responsible for maintaining the file’s URL? We can mark that as hidden – there’s no reason the user needs to see it. The JavaScript source will still use it properly and its value will be read in the serialization function.

The final wp_custom_attachment function should look like this:

function wp_custom_attachment() {

	wp_nonce_field(plugin_basename(__FILE__), 'wp_custom_attachment_nonce');
	
	$html = '<p class="description">';
		$html .= 'Upload your PDF here.';
	$html .= '</p>';
	$html .= '<input type="file" id="wp_custom_attachment" name="wp_custom_attachment" value="" size="25" />';
	
	// Grab the array of file information currently associated with the post
	$doc = get_post_meta(get_the_ID(), 'wp_custom_attachment', true);
	
	// Create the input box and set the file's URL as the text element's value
	$html .= '<input type="hidden" id="wp_custom_attachment_url" name="wp_custom_attachment_url" value=" ' . $doc['url'] . '" size="30" />';
	
	// Display the 'Delete' option if a URL to a file exists
	if(strlen(trim($doc['url'])) > 0) {
		$html .= '<a href="javascript:;" id="wp_custom_attachment_delete">' . __('Delete File') . '</a>';
	} // end if
	
	echo $html;

} // end wp_custom_attachment

These two posts covered a lot of information. There are a number of canned solutions – be it plugins, themes, or other add-ons – available for integrating functionality like this, but part of being a good developer is knowing when to use a third-party solution and when to roll your own.

Additionally, if you’re working with WordPress in a professional capacity, then it’s important to understand the API. Hopefully this series has helped showcase much of what can be done by leveraging core functionality of WordPress.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.unsalkorkmaz.com Ünsal Korkmaz

    by far, you are best writer in tutsplus.com imho.

  • http://www.damiencarbery.com Damien Carbery

    The syntax plugin appears to have messed up your inline php code in the first two sections.

    • Brandon Jones

      Fixed :)

  • http://www.squareonemd.co.uk Elliott the web design guy

    Very handy, this would be great for repeat file uploads too, so you can add to a group of uploads for instance.

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Yep – exactly!

  • http://webtheme.ru MasterMarti

    If you could still count the number of downloads – would be an excellent replacement for various plug-ins.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Totally! This could be done pretty easily be using custom post meta, too.

      Just use the given post’s ID, give it a unique key, look it up and increment each time the download occurs :)

  • http://web-newz.com waqas

    Excellent Post! Keep it up

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Thanks :)!

  • http://www.jrwdev.com Jon Wadsworth

    Thanks for teaching this. It is really useful. I think you could even make it better by adding a link to the file that was uploaded just beneath the upload form. It would give the user a visual cue that the file they’ve uploaded is indeed attached. Then you’d just hide it when you click delete.

    The only other thing I’d like to see here is the incorporation of WordPress’ built in media uploader. That way, when a file is uploaded, it is also added to the media library and added as an attachment associated with the post (not to mention giving features like multiple uploads, progress bar, and gallery for tracking attachments).

    Code for adding a link to the file (add this code to the wp_custom_attachment function):
    // Display the file if there is one
    if(strlen(trim($doc['url'])) > 0) {
    $html .= ‘<p id=”custom_attachment_view”><a href=”‘ . $doc['url'] . ‘”>’ . get_the_title(get_the_ID()) . ‘</a></p>’;
    } // end if

    js code for hiding the link when delete is clicked (add this code to custom_attachment.js just below the other hide function):
    $(‘#custom_attachment_view’).hide();

    • http://tom.mcfarl.in Tom McFarlin
      Author

      Killer job adding that code!

      Also, the media uploader can be useful but it may be overkill in some instances. I think it depends on the kind of media you want to manage. It uses a slightly different API, too.

      Either way, I’m glad you found the tutorial useful!

  • http://www.BryanPatrick.com Bryan E Patrick

    My call to get_post_ID() within wp_custom_attachment() does not return a value. Hence I don’t get the url of the file to display to delete. The codex says get_post_ID() must be called within the loop. Is that happening? What am I missing?

    • http://tom.mcfarl.in Tom McFarlin
      Author

      There are a couple of ways to do this but it also depends on where you’re calling this code.

      Try this: Declare global $post then attempt to access $post->ID in your code.

  • http://8mediaeg.com 8MEDIA

    amazing post thanks very much for this information ,, but iam asking about max file size ,, can i set it?

    • http://tom.mcfarl.in Tom McFarlin
      Author

      This value is set in your PHP configuration. Load up PHP.ini and look for upload_max_filesize and post_max_size.

      Change those values accordingly.

  • http://logixstudios.com Brian

    While looking at the code and testing it I have found a major flaw in the code.

    // Grab the value for the URL to the file stored in the text element
    $delete_flag = get_post_meta($id, ‘wp_custom_attachment_url’, true);

    // Determine if a file is associated with this post and if the delete flag has been set (by clearing out the input box)
    if(strlen(trim($doc['url'])) > 0 && strlen(trim($delete_flag)) == 0) {

    // Attempt to remove the file. If deleting it fails, print a WordPress error.
    if(unlink($doc['file'])) {

    // Delete succeeded so reset the WordPress meta data
    update_post_meta($id, ‘wp_custom_attachment’, null);
    update_post_meta($id, ‘wp_custom_attachment_url’, ”);

    } else {
    wp_die(‘There was an error trying to delete your file.’);
    } // end if/el;se

    } // end if

    First off, you never set the update_post_meta() for wp_attachement_url. So that Key NEVER exists in the database, and since that will ALWAYS == 0, and because the file field will be empty when you update the post, that IF statement will ALWAYS return true and unlink the file and reset the post meta to NULL.

    The $delete_flag variable should be set to $_POST['wp_attachment_url'] to work properly. Since you want the value of the hidden field, not the “non-existant” meta key to trigger the file to be deleted.

    • http://tommcfarlin.com Tom McFarlin
      Author

      Brian – killer feedback! Thanks so much.

      This particular issue can be addressed by making the following change (I don’t have access to edit the post so I’ve gotta add it here in the comments):

      Locate the following two lines of code in the conditional in the save_custom_meta_data function:

      add_post_meta($id, ‘wp_custom_attachment’, $upload);
      update_post_meta($id, ‘wp_custom_attachment’, $upload);

      Add the following two lines directly below it (as you mentioned):

      add_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);
      update_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);

      This will populate the attachment URL field with the database and will ensure that the delete flag will always have a proper value.

      • Mike

        So we are going to have 4 lines in the conditional statement or we are replacing the original two with these new ones using $_POST?

        • http://tommcfarlin.com Tom McFarlin
          Author

          4 lines

  • http://www.multimediasky.com Ciprian

    Something is wrong with the delete part of the code, it doesn’t remove the file and it won’t allow me to update the post neither :(

    Every time i click update post i get the “There was an error trying to delete your file.” error.

    • Mike

      I am having this same issue.

    • http://tommcfarlin.com Tom McFarlin
      Author

      @Ciprian, @Mike: Take a look at the comment I just left in reply to Brian. Make that change.

      If it still doesn’t work after that, make sure that your browser isn’t displaying any JavaScript errors (some of the delete functionality requires the use of JavaScript).

      If that still isn’t the case, you may need to do a var_dump on $doc after retrieving it via get_post_meta.

      • Mike

        That didn’t work, I did a var_dump on $doc and I am getting a weird URL for the file in the array:

        ‘file’ => string ‘C:wampwwwprojectname/wp-content/uploads/2011/11/project.pdf’

        Could that have something to do with it?

        • http://tommcfarlin.com Tom McFarlin
          Author

          Gotcha – yeah, Windows files paths work differently than *nix-based file paths. PHP can’t find the file to delete because that’s an invalid path – the ‘\’ are getting stripped when saving the file.

          Try replacing the calls to $doc['url'] with $doc['file'] as that’ll return a fully qualified path, as well.

          I don’t have a Windows machine with me so that’s about the best I can do right now unless another reader can chime in.

          • Mike

            Something is seriously wrong, even when I try to just update a single post with an attachment without trying to delete a file I get the “There was an error trying to delete your file.” and the post update won’t work.

          • Mike

            Actually, let me rephrase that, the update goes through, but regardless it still displays the error message even when not trying to delete the file.

          • http://tommcfarlin.com Tom McFarlin
            Author

            The problem is with Windows paths. Slashes are being stripped out. Here’s what you need to do:

            Change the block of code that looks like this:

            add_post_meta($id, ‘wp_custom_attachment’, $upload);
            update_post_meta($id, ‘wp_custom_attachment’, $upload);

            add_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);
            update_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);

            To this:

            $upload['file'] = str_replace(‘\\’, ‘/’, $upload['file']);
            add_post_meta($id, ‘wp_custom_attachment’, $upload);
            update_post_meta($id, ‘wp_custom_attachment’, $upload);

            add_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);
            update_post_meta($id, ‘wp_custom_attachment_url’, $_POST['wp_custom_attachment_url']);

            Make this change and you’ll be good to go (though there’s a chance that the changes will work with newer posts only). Regardless, this has been tested and proven to work on Windows using XAMPP and WordPress 3.

          • Mike

            So I thought this may be an issue when testing locally in WAMP, I have since moved this to a live testing environment and started from scratch, I deleted all of the file records from the database, and deleted the files from the upload folder. Now I can upload and delete files without an error, but for some reason every time I try to update a post, it deletes the post attachment no matter what, even if I haven’t hit the Delete File link.

          • http://tommcfarlin.com Tom McFarlin
            Author

            You also have to update the input box that shows the custom attachment URL to use the str_replace behavior, as well.

            $doc = get_post_meta(get_the_ID(), ‘wp_custom_attachment’, true);
            $doc['file'] = str_replace(‘\\’, ‘/’, $doc['file']);

            Tested and works on Windows, too.

        • Mike

          Implemented this fix but still experiencing the same issue, the initial upload of the file works, but any time after that if I hit the Update button on the post, it also deletes the file even if I haven’t hit the Delete File link.

  • Mike

    Is anyone else having the issue where any time you hit Update afterward it just deletes the file?

  • Mike

    The problem is somewhere within this piece of code I believe, when I commented out this section and updated the page the file was not deleted, so something about this is causing the file to get deleted each time the page is updated:

    if(strlen(trim($doc['url'])) > 0 && strlen(trim($delete_flag)) === 0)
    {
    // Attempt to remove the file. If deleting it fails, print a WordPress error.
    if(unlink($doc['file']))
    {
    // Delete succeeded so reset the WordPress meta data
    update_post_meta($id, ‘wp_custom_attachment’, null);
    update_post_meta($id, ‘wp_custom_attachment_url’, ”);
    } else
    {
    wp_die(‘There was an error trying to delete your file.’);
    }
    }

  • Mike

    I changed $delete_flag to equal $_POST['wp_custom_attachment_url'] and that seems to have solved the problem.

    // Grab the value for the URL to the file stored in the text element
    $delete_flag = $_POST['wp_custom_attachment_url'];

    // Determine if a file is associated with this post and if the delete flag has been set (by clearing out the input box)
    if(strlen(trim($doc['url'])) > 0 && strlen(trim($delete_flag)) === 0)
    {
    // Attempt to remove the file. If deleting it fails, print a WordPress error.
    if(unlink($doc['file']))
    {
    // Delete succeeded so reset the WordPress meta data
    update_post_meta($id, ‘wp_custom_attachment’, null);
    update_post_meta($id, ‘wp_custom_attachment_url’, ”);
    } else
    {
    wp_die(‘There was an error trying to delete your file.’);
    }
    }

    • Sean

      Thanks Mike. I was having this exact same issue. Thanks for the correction. Greatly appreciated

  • Von Eric

    Please help, why i cant run this in WP 3.2.1 it doesnt validate if the file is empty or not pdf :( thanks in advance

  • ANNA

    I have a issue, when upload file, file duplicate, ex: file.pdf file1.pdf. please tell me how to fix !!!

  • http://www.vaultstudios.co.uk Martin Harvey

    Hi Tom,
    Another great tutorial, Thanks ;) Been meaning to get around to trying this one out and only just managed it. Works great for me even with a few little tweaks and changes to suit my needs, but i am experiencing the same as Anna.

    I seem to get a duplicate file uplaoded and then the post links to the duplicate and not the original. When you then delete the file it only removes the duplicate leaving another copy on the server. Anybody any ideas on wh ythis is or how to fix it?

    • http://www.vaultstudios.co.uk Martin Harvey

      OK, I sorted this out. Might not be the best way but it works for me. A duplicate of the file being attached is created because when a post/page is updated WP fires the save_post action twice, the first time to save a revision and the second time for the actual post. I added this code to my functions.php to turn revisions off:

      function no_revisions_please() {

      remove_post_type_support(‘post’, ‘revisions’);
      remove_post_type_support(‘page’, ‘revisions’);

      }

      add_action(‘admin_init’, ‘no_revisions_please’);

      You can also achieve the same thing by adding define(‘WP_POST_REVISIONS’, false); to wp_config.php.

      Ideally though, I’d like to do something similar to the DOING_AUTOSAVE conditional and return out of the save function if WordPress is saving the revision, if anybody has any ideas?

  • http://www.vaultstudios.co.uk Martin Harvey

    Ok here we go, add this code above the DOING_AUTOSAVE conditional in the save function

    if( wp_is_post_revision( $id ) ) return;

    This just detects if wp is trying to save the revision of the post, skips it, and then saves the actual post content and saves the meta box data without creating a duplicate file. Ignore the function to turn post revisions off, unless you really want to.

  • Pingback: Attaching Files with Custom Post Meta Boxes - Tom McFarlin

  • Simon

    Hi,

    How would you modify the code so that it would accept .doc and .docx files?

    Many thanks

    • http://littlewebgiants.com Melanie

      Hi Simon,

      Just add any extra file types you want to the supported_types array.

      Eg.

      $supported_types = array(‘application/pdf’, ‘application/msword’, ‘application/vnd.openxmlformats-officedocument.wordprocessingml.document’);

      will allow .pdf, .doc and .docx files. You can find a comprehensive list of file types here: http://www.freeformatter.com/mime-types-list.html

      Thanks for a great tutorial Tom!

      Mel

  • Pingback: HTML5, API's, List.js, Foundation, Git, and More - Tom McFarlin

  • http://www.pachakamaq.com Fliberty Pérez

    i need the custom_attachment.js file, i get one?. Excuse my ignorance please.

  • http://www.facebook.com/dulichintours Hiệp Lương

    Bạn đang tìm hiểu về Thủ tục xin Visa nhanh, Visa khẩn? Bạn muốn tìm một Dịch vụ Làm xinVisa nhanh, Visa khẩn có uy tín? Hãy đến với chúng tôi, chúng tôi sẽ tư vấn và hoàn tất các Thủ tục xin visa cho bạn. Dịch vụ xin Visa nhanh, Visa khẩn giá rẻ của Du lịch INTOUR đảm bảo làm thủ tục nhanh chóng, an toàn với chi phí dịch vụ thấp nhất..

    Dịch vụ xin Visa nhanh, Visa khẩn của Du lịch INTOUR sẽ đại diện giao dịch với cơ quan Nhà Nước để đẩy nhanh việc cấp xin Visa nhanh, Visa khẩncho khách hàng. Khách hàng có thể hoàn toàn yên tâm về giá cả và chất lượng khi đến với dịch vụ xin Visa nhanh, Visa khẩn giá rẻ của Du lịch INTOUR.

    http://dichvulamvisa24h.com/news/Visa-Chau-Au/

    http://dichvulamvisa24h.com/news/Visa-Chau-a/

  • http://www.facebook.com/dulichintours Hiệp Lương

    DU LỊCH NHA TRANG – KHÔNG CHỈ ĐẾN MỘT LẦN

    Là một trong những địa điểm du lịch nổi tiếng của cả nước cũng như thế giới. Bạn không thể nào đến đây một lần mà có thể tận hưởng được hết vẻ đẹp thiên nhiên , con người và ẩm thực Nha Trang, là một trong những người yêu biển Nha Trang , tôi xin chia sẽ bạn một vài địa điểm mà có thể mang đến cho bạn một chuyến du lịch biển ấn tượng và hoàn hảo.

    http://www.intour.com.vn/du-lich-nha-trang.html

    http://www.intour.com.vn/phu-quoc.html

    http://www.intour.com.vn/tour-du-lich-singapore.html

  • http://www.facebook.com/lucasvst Lucas Teles

    I’m really grateful for this code, but i’m having some issues, like, file dont delete, i receive an error (i’m on Windows and I’ve read about slashes), and errors with ‘illegal string offset’ when adding a new post. I really tried to read all the coments but they seem a bit disorganized at time. I just want to know if this code is updated, and if not, somebody can help-me with a updated version? I tried a lot of things but without success…I appreciate if anyone can help!

  • http://www.facebook.com/MaineventShow Damian Freeman

    hey guys is there a way to have this thing display the name of the file … i have a download counter im trying to intergrate with it and it needs me to call the file name not url to like in the count to the download

  • Simon Lockyer

    Any idea how to allow for vcard(.vcf) uploads? I have tried adding ‘text/x-vcard’, ‘application/vcard’ to the supported_types array? Cheers