This week whilst working on WooCommerce 2.0 beta I was faced with the task of rewriting the media unloaders to use the fancy new interface in 3.5 instead of the tired old thickbox modal windows used in earlier versions.
This was no easy task mainly due to the lack of documentation available for the new system, but I persevered and wanted to share my experiences in this post.
Adding an inline uploader to a field; the simplest solution

The simple solution is to simply trigger the bog standard uploader, much like was possible with the old thickbox media uploaders. This post pretty much explains how to do that.
This however was no good to me; it gives you a media uploader with a sidebar and other confusing elements not relevant when adding images to fields inline.
A better solution with a custom frame
To remove the unwanted elements and to simplify the uploader you can create a custom frame (this was inspired by the header image uploader in Twenty Twelve).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Uploading files | |
var file_frame; | |
jQuery('.upload_image_button').live('click', function( event ){ | |
event.preventDefault(); | |
// If the media frame already exists, reopen it. | |
if ( file_frame ) { | |
file_frame.open(); | |
return; | |
} | |
// Create the media frame. | |
file_frame = wp.media.frames.file_frame = wp.media({ | |
title: jQuery( this ).data( 'uploader_title' ), | |
button: { | |
text: jQuery( this ).data( 'uploader_button_text' ), | |
}, | |
multiple: false // Set to true to allow multiple files to be selected | |
}); | |
// When an image is selected, run a callback. | |
file_frame.on( 'select', function() { | |
// We set multiple to false so only get one image from the uploader | |
attachment = file_frame.state().get('selection').first().toJSON(); | |
// Do something with attachment.id and/or attachment.url here | |
}); | |
// Finally, open the modal | |
file_frame.open(); | |
}); |
This gives us a cut down version which allows upload, or selecting a file from the media library:
This also picks up the title and the button text from the button’s html (notice the data( ‘uploader_title’ ) and data( ‘uploader_button_text’ )) using data-uploader_title and data-uploader_button_text attributes respectively.
Handling multiple files
To handle multiple files set multiple to true:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Create the media frame. | |
file_frame = wp.media.frames.file_frame = wp.media({ | |
title: jQuery( this ).data( 'uploader_title' ), | |
button: { | |
text: jQuery( this ).data( 'uploader_button_text' ), | |
}, | |
multiple: true // Set to true to allow multiple files to be selected | |
}); |
Then you just need some tweaked handling on select:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// When an image is selected, run a callback. | |
file_frame.on( 'select', function() { | |
var selection = file_frame.state().get('selection'); | |
selection.map( function( attachment ) { | |
attachment = attachment.toJSON(); | |
// Do something with attachment.id and/or attachment.url here | |
}); | |
}); |
Passing data to the uploader script
The largest hurdle I faced was finding a way to pass data through to the upload script. I had two reasons for needing to do this:
- I needed to pass through a different post ID to attach the image to if uploading from an edit post page, but attaching to a different post (in this case a product variation in WooCommerce).
- I needed to pass through a custom variable so I could pick it up via the upload_dir hook to upload *some* files to different directories.
This was largely done through trial and error but I got there in the end (thanks Justin for the debug assistance) by setting the wp.media post ID and then re-setting this everytime we open the frame.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Uploading files | |
var file_frame; | |
var wp_media_post_id = wp.media.model.settings.post.id; // Store the old id | |
var set_to_post_id = 10; // Set this | |
jQuery('.upload_image_button').live('click', function( event ){ | |
event.preventDefault(); | |
// If the media frame already exists, reopen it. | |
if ( file_frame ) { | |
// Set the post ID to what we want | |
file_frame.uploader.uploader.param( 'post_id', set_to_post_id ); | |
// Open frame | |
file_frame.open(); | |
return; | |
} else { | |
// Set the wp.media post id so the uploader grabs the ID we want when initialised | |
wp.media.model.settings.post.id = set_to_post_id; | |
} | |
// Create the media frame. | |
file_frame = wp.media.frames.file_frame = wp.media({ | |
title: jQuery( this ).data( 'uploader_title' ), | |
button: { | |
text: jQuery( this ).data( 'uploader_button_text' ), | |
}, | |
multiple: false // Set to true to allow multiple files to be selected | |
}); | |
// When an image is selected, run a callback. | |
file_frame.on( 'select', function() { | |
// We set multiple to false so only get one image from the uploader | |
attachment = file_frame.state().get('selection').first().toJSON(); | |
// Do something with attachment.id and/or attachment.url here | |
// Restore the main post ID | |
wp.media.model.settings.post.id = wp_media_post_id; | |
}); | |
// Finally, open the modal | |
file_frame.open(); | |
}); | |
// Restore the main ID when the add media button is pressed | |
jQuery('a.add_media').on('click', function() { | |
wp.media.model.settings.post.id = wp_media_post_id; | |
}); |
One final note on enqueuing the scripts; if you use these on a page that doesn’t already load the media uploaders (e.g. a custom admin page) remember to call wp_enqueue_media(); This will get everything loaded into the page for you.
Overall, I’m pleased with the result and the new uploaders. I hope this post helps others implement them.