This article details one method of creating a custom WordPress post type with the ability to upload and attach images using the built-in WordPress Media browser. This piece does not cover all the details and nuances of creating a custom post type, as this is covered well elsewhere.
In addition to highlighting the most important methods and code required, I have attached a fully functional demo plugin which you may feel free to download and use as a starting point or reference. Also note that the code within this example is borrowed and modified from my favorite ecommerce platform: WooCommerce.
The Goal
My goal tonight was to create a custom post type and add a meta box to display, and allow admin users to select and attach an image using the WordPress media box:
I want the selected image to be displayed in a custom meta box, similar to the Post Featured Image box, and I want it to be dead simple for you to get this working in your own project.
The Code
Skipping the boilerplate code required to register the custom post type (which I’m calling “book”), set up the custom meta box, etc (which you can review in the demo plugin), the first piece of code to examine is the image display/upload/remove links:
global $post; $image_id = get_post_meta( $post->ID, '_image_id', true ); $image_src = wp_get_attachment_url( $image_id ); ?> <img id="book_image" src="<?php echo $image_src ?>" style="max-width:100%;" /> <input type="hidden" name="upload_image_id" id="upload_image_id" value="<?php echo $image_id; ?>" /> <p> <a title="<?php esc_attr_e( 'Set book image' ) ?>" href="#" id="set-book-image"><?php _e( 'Set book image' ) ?></a> <a title="<?php esc_attr_e( 'Remove book image' ) ?>" href="#" id="remove-book-image" style="<?php echo ( ! $image_id ? 'display:none;' : '' ); ?>"><?php _e( 'Remove book image' ) ?></a> </p>
This snippet pulls the attached image (if any) from the post, displays it, and creates the “Set” (or upload) and “Remove” links. Also note the hidden input field which used to persist the attached image id to the post meta.
Next we hook up the upload link:
window.send_to_editor_default = window.send_to_editor; $('#set-book-image').click(function(){ // replace the default send_to_editor handler function with our own window.send_to_editor = window.attach_image; tb_show('', 'media-upload.php?post_id=<?php echo $post->ID ?>&type=image&TB_iframe=true'); return false; });
The above javascript displays the WordPress media browser in the thickbox overlay when the “Set book image” link is clicked. With this and the previous piece of code the media browser will actually display and function as normal, until that you go to “Use this image” at which point the media box will go blank and not much else will happen. The reason for this is that when you choose an image from the media selector box it performs an asynchronous post to media-upload.php
which returns a piece of Javascript similar to the following:
var win = window.dialogArguments || opener || parent || top; win.send_to_editor('<a href="http://127.0.0.1/wp-content/uploads/2012/07/dracula.jpg"><img src="http://127.0.0.1/wp-content/uploads/2012/07/dracula.jpg" alt="" title="dracula" width="400" height="617" class="aligncenter size-full wp-image-1095" /></a>');
That call to win.send_to_editor()
is sort of like invoking a callback or handler function, so we need to provide the function to do the image handling. That is the purpose of this line from above window.send_to_editor = window.attach_image;
, so our next step is to provide that send_to_editor
function, which in this example I’ve declared as attach_image()
:
window.attach_image = function(html) { $('body').append('<div id="temp_image">' + html + '</div>'); var img = $('#temp_image').find('img'); imgurl = img.attr('src'); imgclass = img.attr('class'); imgid = parseInt(imgclass.replace(/D/g, ''), 10); $('#upload_image_id').val(imgid); $('#remove-book-image').show(); $('img#book_image').attr('src', imgurl); try{tb_remove();}catch(e){}; $('#temp_image').remove(); window.send_to_editor = window.send_to_editor_default; }
This function creates a temporary img tag with the selected image, so as to more easily parse out the url and id, which are then used to display the selected image in our meta box, and set the image id to that hidden form field so we can save it permanently to the post meta. Finally the function performs a little clean up by closing the media browser, removing that temporary image tag, and restoring the original send_to_editor
handler.
That covers the most important and difficult pieces of code, all that remains is hooking up the “Remove” link with something like:
$('#remove-book-image').click(function() { $('#upload_image_id').val(''); $('img').attr('src', ''); $(this).hide(); return false; });
And finally saving that image id to the post:
function book_meta_boxes_save( $post_id, $post ) { if ( empty( $post_id ) || empty( $post ) || empty( $_POST ) ) return; if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; if ( is_int( wp_is_post_revision( $post ) ) ) return; if ( is_int( wp_is_post_autosave( $post ) ) ) return; if ( ! current_user_can( 'edit_post', $post_id ) ) return; if ( $post->post_type != 'book' ) return; update_post_meta( $post_id, '_image_id', $_POST['upload_image_id'] ); } add_action( 'save_post', 'book_meta_boxes_save', 1, 2 );
Demo Plugin
By now hopefully you have a solid understanding of how to make use of the media browser to attach an image to a custom post type. If you’d like a fully functional example to play around with and extend for your own uses, feel free to download and make use of my demo plugin:
Great tutorial thanks! I got it working on the backend perfectly, but now I don’t really know how to display the image on the front-end. Can you give me the code to do that please? I tried:
ID, ‘image_src’, true); ?>
and
ID, ‘image_id’, true); ?>
and none of them worked.
Thanks!
This code doesn’t work. I can’t figure out why, but the image Id goes into the database as NaN which means it can’t be called for display in the loop.
I think part of your example code got cut off, but once you get the image id, you should be able to use the wordpress image functions, like
wp_get_attachment_url()
for instancewould you have any ideas as to why when i run the script, the Image Id that is stored in $image_id goes in to the database as NaN. when I go to the database and change it manually to the correct id, then the image displays in my post. Otherwise, i jut get a blank image holder. You help would be appreciated as i found the code to be useful other than this which i can’t seem to solve.
You’ll have to trace the script through and use console.log to find out why it’s not getting the proper value.
usage in theme template??
example:
???
thank you
Hello, first of all, the tip has helped me a lot.
Well, I wonder how can I put in categories plugin and how I visualize in my template, the book by category?
thank you
hey claudio, I think that’s a bigger/more complicated question than I could really hope to answer in the blog comments
Hi,
can you please me, I want create custom post type plugin with my custom UI for front user. to add posts and edit posts, is there any tutorials on this please provide links.
thanks
Hey, here’s a great starting point: http://codex.wordpress.org/Post_Types Also a number of resources on that page, should have everything you need to get going if you’re comfortable with PHP
Hi,
Thanks for your reply. I am a PHP developer and i created few small plugin using custom table. but this time i need to make WP custom post type plugin with my own UI like “CollabPress plugin” this plugin has project creation functionality from dashboard side and front end user can also create project.
Finally i need to create post type plugin with own UI to submit post data from dashboard side and as well as fronted also. could please snuggest best reference tutorials for creation this plugin.
thanks
There’s something wrong with your code. My image gets added to the database with value NaN. This means it cannot be called when you go back to change the image for the post. The problem is with the script:
imgid = parseInt(imgclass.replace(/D/g, ”), 10);
in the media uploader. The class cannot return an integer and therefore it returns the default NaN. I swapped this out with:
imgid = ID ?>;
which returns the id for the post, but not the id for the image. I still havn’t figured out a solution to the problem and any help would be much appreciated.
As for Rodri’s problem, I think it will persist all the time the image ID is not being properly stored in the DB, but when it is then Justin’s suggestion will work.
hey there thanks a lot for that great tutorial.
when i test it local than works everything fine, but as soon i want to use your code live i get this error.
Fatal error: Cannot redeclare class Custom_Post_Type_Image_Upload in
…./wp-content/plugins/custom_post_image_upload.php on line 15
can you maybe help me there?
silly me…. had the same class already in functions.php
I’m trying to add an image upload to a second Custom Post Type. I got your plugin working perfectly for the first one, but can’t figure out how to do it for another one. Let me know what to do please.
Thank you.
Hmm, honestly I can’t imagine why this wouldn’t work for a second post type, assuming that everything is named correctly anyhow
Hey Justin Stern !
please tell my. How to get image from this post type display in to front-end. I’m install plugin and activeted on back-end successfully, everything work fine in the back-end but i can’t out put display image in front-end.
please help me.
thank you !
Hi,
when I try to include this code in my plugin, i get the media box, but it doesn’t show the insert button.
I am only capable of browsing through all media…
I am using wordpress 3.8…also horizontal scrollbar is missing on the popup window..
Any solution??
Thanks
Not sure, it may well be broken with the new media browser recently introduced into WordPress, I really haven’t had a chance to go back and revisit this solution with it. Sorry about that!
I’ve tested this straight up in WP 3.8 and it’s working fine.
To answer some questions about how to use this in a loop – a simple integration is :
<?php
if ( get_post_meta( get_the_ID(), '_image_id', true ) ) :
$image = get_post_meta( get_the_ID(), '_image_id', true );
echo '’ ;
endif;
?>
Chris.
Seems like HTML from my echo is stripped out by your comments system.
try:
http://pastebin.com/feBhNDQ9
Thanks for the tip!
Was working fine, until I recently updated to the latest version of wordpress. Can’t seem to figure out why it isn’t working anymore 🙁
Hey Mile, good point, actually I need to update this article to reflect some changes that were made to the media browser in WP 3.5. For the meantime, you can follow this article to get the media browser working with the current version of WordPress: http://codestag.com/how-to-use-wordpress-3-5-media-uploader-in-theme-options/
Hi There. Is is possible to have more than just one image upload?
Regards
Sure, WooCommerce does this for product variations which is where I’d look at for some good sample code 🙂
how to delete image form galery..?
Hey there, you just open the gallery and hover on the image – click the “X” as described here.
For the problems with ID being NaN replace:
imgid = parseInt(imgclass.replace(/D/g, ”), 10);
with:
imgid = parseInt(imgclass.replace(/\D/g, ”), 10);
(notice the backslash)
Fixed it for me. Actually it’s strange it ever worked: “\D” is just the short-form of [^0-9].
The “,10)” (second parameter of parseInt) can also be omitted, it’s clear that we use the decimal system.
Hello, I just wonder how to simply add a new field for an URL ? Is that possible without changing the image upload feature ?