This post has been marked as old. The code might no longer work. Comments have been disabled as this is no longer maintained.
In WordPress 3.5, the media gallery was completely rebuilt, and now the method described below doesn't work. Here's a tutorial that might help.
Images in WordPress can have as much metadata (data about the image) as you want. WordPress stores images as posts in the “attachment” post type, so all the standard WordPress functions you’re used to using with posts also work with images. For example, captions are stored as excerpts.
If you want to collect additional information on a post you typically use a metabox. For images, you can add your custom fields directly to the media uploader.
There’s two filters we’ll use: attachment_fields_to_edit
and attachment_fields_to_save
.
I’m going to provide two examples that I use often: adding a “Photo credit” section, and adding an “Include in Rotator” selection.
Adding Photo Credit
You’ll often need to provide attribution to the source of images you use on your website. Rather than sticking it at the top or bottom of the post, we can add that information as photo metadata and then include it in the theme.
I’m going to add two text fields, Photographer Name and Photographer URL.
<?php | |
/** | |
* Add Photographer Name and URL fields to media uploader | |
* | |
* @param $form_fields array, fields to include in attachment form | |
* @param $post object, attachment record in database | |
* @return $form_fields, modified form fields | |
*/ | |
function be_attachment_field_credit( $form_fields, $post ) { | |
$form_fields['be-photographer-name'] = array( | |
'label' => 'Photographer Name', | |
'input' => 'text', | |
'value' => get_post_meta( $post->ID, 'be_photographer_name', true ), | |
'helps' => 'If provided, photo credit will be displayed', | |
); | |
$form_fields['be-photographer-url'] = array( | |
'label' => 'Photographer URL', | |
'input' => 'text', | |
'value' => get_post_meta( $post->ID, 'be_photographer_url', true ), | |
'helps' => 'If provided, photographer name will link here', | |
); | |
return $form_fields; | |
} | |
add_filter( 'attachment_fields_to_edit', 'be_attachment_field_credit', 10, 2 ); | |
/** | |
* Save values of Photographer Name and URL in media uploader | |
* | |
* @param $post array, the post data for database | |
* @param $attachment array, attachment fields from $_POST form | |
* @return $post array, modified post data | |
*/ | |
function be_attachment_field_credit_save( $post, $attachment ) { | |
if( isset( $attachment['be-photographer-name'] ) ) | |
update_post_meta( $post['ID'], 'be_photographer_name', $attachment['be-photographer-name'] ); | |
if( isset( $attachment['be-photographer-url'] ) ) | |
update_post_meta( $post['ID'], 'be_photographer_url', $attachment['be-photographer-url'] ); | |
return $post; | |
} | |
add_filter( 'attachment_fields_to_save', 'be_attachment_field_credit_save', 10, 2 ); |
In the first function we used a simple array to specify the field’s Label, input type, value, and help text. In the second function, we check to see if a value has been set for those fields, and if so we update the post metadata.
To access this information in our theme, we simply have to get the post meta for the thumbnail. Assuming we want it for the featured image, we’d do something like this: get_post_meta( get_post_thumbnail_id(), 'be_photographer_name', true );
Include in Rotator
Adding text fields is rather easy. What if you want something more custom? If you use the ‘html’ input type, you can output whatever HTML you’d like.
I love using the built-in Gallery feature in WordPress for managing image rotators on specific posts or pages. The problem is that all images are included in the gallery. So I add an “Include in Rotator” option when editing the image, and then I can query for all attached images that were marked “Yes”.
For more examples of creating html inputs for the media gallery, look in /wp-admin/includes/media.php.
<?php | |
/** | |
* Add "Include in Rotator" option to media uploader | |
* Limited to Home page | |
* | |
* @param $form_fields array, fields to include in attachment form | |
* @param $post object, attachment record in database | |
* @return $form_fields, modified form fields | |
*/ | |
function be_attachment_field_rotator( $form_fields, $post ) { | |
// // Only show on front page | |
if( ! ( $post->post_parent == get_option( 'page_on_front' ) ) ) | |
return $form_fields; | |
// Set up options | |
$options = array( '1' => 'Yes', '0' => 'No' ); | |
// Get currently selected value | |
$selected = get_post_meta( $post->ID, 'be_rotator_include', true ); | |
// If no selected value, default to 'No' | |
if( !isset( $selected ) ) | |
$selected = '0'; | |
// Display each option | |
foreach ( $options as $value => $label ) { | |
$checked = ''; | |
$css_id = 'rotator-include-option-' . $value; | |
if ( $selected == $value ) { | |
$checked = " checked='checked'"; | |
} | |
$html = "<div class='rotator-include-option'><input type='radio' name='attachments[$post->ID][be-rotator-include]' id='{$css_id}' value='{$value}'$checked />"; | |
$html .= "<label for='{$css_id}'>$label</label>"; | |
$html .= '</div>'; | |
$out[] = $html; | |
} | |
// Construct the form field | |
$form_fields['be-include-rotator'] = array( | |
'label' => 'Include in Rotator', | |
'input' => 'html', | |
'html' => join("\n", $out), | |
); | |
// Return all form fields | |
return $form_fields; | |
} | |
add_filter( 'attachment_fields_to_edit', 'be_attachment_field_rotator', 10, 2 ); | |
/** | |
* Save value of "Include in Rotator" selection in media uploader | |
* | |
* @param $post array, the post data for database | |
* @param $attachment array, attachment fields from $_POST form | |
* @return $post array, modified post data | |
*/ | |
function be_attachment_field_rotator_save( $post, $attachment ) { | |
if( isset( $attachment['be-rotator-include'] ) ) | |
update_post_meta( $post['ID'], 'be_rotator_include', $attachment['be-rotator-include'] ); | |
return $post; | |
} | |
add_filter( 'attachment_fields_to_save', 'be_attachment_field_rotator_save', 10, 2 ); |
Then when I want to get all the attached images that are going in the rotator, I run something like this:
<?php | |
// Get all images attached to post that have "Include in Rotator" marked as "Yes" | |
global $post; | |
$args = array( | |
'post_parent' => $post->ID, | |
'post_type' => 'attachment', | |
'post_mime_type' => 'image', | |
'post_status' => 'inherit', | |
'posts_per_page' => '-1', | |
'order' => 'ASC', | |
'orderby' => 'menu_order', | |
'meta_query' => array( | |
array( | |
'key' => 'be_rotator_include', | |
'value' => '1' | |
) | |
) | |
); | |
$images = new WP_Query( $args ); | |
if( !$images->have_posts() ) | |
return; | |
while( $images->have_posts() ): $images->the_post(); global $post; | |
// Echo image url | |
$image = wp_get_attachment_image_src( $post->ID, 'be_featured' ); | |
echo $image[0] . '<br />'; | |
endwhile; wp_reset_query(); |
AJ Clarke says
This is quite awesome. One thing I noticed though is the data wasn’t being saved automatically when images were uploaded (and I needed images to default to yes and display), so what I did instead was pull the meta using:
https://gist.github.com/2396076
That way I could just test using:
if($be_rotator_include !=’1′) { //do something awesome }
Anyway. Awesome stuff. Thanks!
Bill Erickson says
Another approach is to limit your query results to those matching the meta query: https://www.billerickson.net/code/get-attached-images/
AJ Clarke says
The reason that didn’t work for me is because the meta data doesn’t get saved until the user goes into the gallery and saves the image – when I just upload an image to a post $be_rotator_include returns NULL until actually going to the gallery and clicking save changes. I needed all images to show up automatically unless the value was set to “no”.
I’ve tried using meta_compare and relations and nothing…any ideas? thanks!
Bill Erickson says
You could hook onto the save_post hook, check the post meta, and if it’s empty save it with whatever you’d want. Or you can do it your way.
I usually take the opposite approach where I only want images marked “Yes” showing up. If they upload an image, I want them to actually check “include in rotator” before it shows up.
AJ Clarke says
Thanks for the insanely fast reply. Yes I ended up just creating a small update_post_meta function to loop through any images already on the site then hook into save_post.
Didn’t know if there was a better/quicker way using the meta_query to check if the attachment had a meta value or not, but I guess you can’t check it this way…
smattiza says
Bill, I hate to post what is likely a very easy/dumb question but I am new to WP and trying to get a credit to display under images in posts. I added your code to the functions.php but do not know where to add the code (echo) to display the credit for images on single posts and cat indexes, etc. I would greatly appreciate any help. thanks!
Bill Erickson says
Is this for the featured image, or for normal images inserted in the post? I don’t this technique can be used for the latter, but to include it on the featured image look in your template file (probably single.php) for where the featured image is being inserted. You’ll see
the_post_thumbnail()
orget_the_post_thumbnail()
. Place yourget_post_meta()
function below this.Jon Brown says
I’m trying to figure out a way to get credits to automatically display on images inserted into the content body… I’m a bit stumped. I looked at a pair of plugins that purport to do this, but both seem to inject the credits into shortcode at the time the photo is inserted…
For reference:
http://wordpress.org/extend/plugins/media-credit/ <- wraps image in shortcode on insertion.
http://wordpress.org/extend/plugins/image-credit/ <- never got it even slightly working
This all leads me to believe there isn't a good way to do this at the time the post is rendered, and just to make sure I'm not totally missing something, I'm thinking there is no processing that happens inside the_content, hence no hooks… and the only available filter would be on the_content which would make for an ugly task if one tried to find the img tags, then the credits associated with them based on ??? and then injecting html around them…
Bill Erickson says
Correct, there’s no easy way to apply this to images inserted in the post. You’d need to use the_content filter to run a regular expression to find all images.
Jon Brown says
Thanks Bill… getting off topic, but this got me thinking… wouldn’t an “WP image object” be kind of cool? Something that one could insert into the post body but that one would continue to have full programatic control over similar to featured images. It could be prototyped with shortcodes providing the hook in point I suppose, but I’m thinking of it as a core feature… Just a day dream… that level of core hackery is at the limits of my imagination, and well beyond my skills, just and interesting idea for now.
Moshe says
Thanks for the great article. This is exactly what I was looking for.
One question – is it possible to have the data from the extra fields inserted into the editor when one clicks on Insert Into Post? If so, how does one do this?
Thanks in advance for any help you can offer.
Bill Erickson says
I don’t know how to modify what is sent to the editor when you click “Insert”.
Jon Brown says
Moshe – I was looking for the same thing, the closest thing I found was http://wordpress.org/extend/plugins/media-credit/ which inserts a metafield value into a custom caption short code at the time of insertion.
Moshe says
Thanks, I’ll check that out.
Bob says
Very nice tip! Thanks for sharing.
Just a question: is there a way to only display the custom field only when a certain page template is selected ?
Thanks
Bill Erickson says
Yes you can! In the code towards the top you’ll see a part that says “only show on front page”. Replace that with this: https://gist.github.com/2710347
Change ‘template-products.php’ with whatever template you want.
Bob says
Thanks for your prompt reply. Works perfect!
Thank you again Bill.
Drew says
Great tutorial. I was wondering how I could use the hidden custom field feature to do this.
I was expecting to just replace “be_photographer_url” with “_be_photographer_url” but then it actually doesn’t work. Is their a way to use this with those hidden fields?
In case you don’t know what I mean by hidden fields, it’s the ability to save to a custom meta field, but then not have that custom field appear in the “Custom Field” drop down list.
Thanks,
Drew
Bill Erickson says
Since these fields are for media, not posts, there isn’t a Custom Fields box that you need to hide the fields from. If you want to add a field without having an input, just don’t add the input field.
Drew says
Thanks Bill, I think I’m confusing you. A created a question here that explains it better:
http://stackoverflow.com/questions/10984582/wordpress-hidden-custom-meta-field-for-images
When you set a key to have the underscore in front, it hides it from that custom fields drop down. Even though the above example is for images, it still shows up. See the screenshot in the above Stack overflow question.
Thanks!
Bill Erickson says
As I said, in your code your are specifically telling WordPress to make a visible text field in the media uploader ( input => text ).
I don’t know the use of a hidden meta field for media, but if you wanted to add a meta field called ‘be_show_this’ with a value of ‘true’ to every file uploaded, add this to your theme’s functions.php file: https://gist.github.com/2924155
kristarella says
Nice one! Great post and good topic.
(Sorry, I usually don’t leave short random comments, but the fact that I am says how much I like this post!)
Ami says
This is pretty darn awesome and works perfectly. Thank you! Any thoughts on how to go about putting in functionality to bulk edit these fields in the media uploader or in the media library?
Bill Erickson says
I don’t believe there’s a way to bulk edit media items, but since improving the media editor is on the scope for WordPress 3.5, you might try creating a trac ticket and seeing if it makes it.
David Radovanovic says
Bill, could this code be used to add tags to media? I haven’t had much luck with the two plugins that are available. Thanks.
Bill Erickson says
It can be used to add a text field to the media screen (post meta on the attachment), but I’m not sure how it work work for a taxonomy.
Cyrille says
Hi Bill,
Works like a charm! Thank you for sharing it.
Just one question though: I want the images to be included by default in the rotator so I’ve put $selected = ‘1’;. and I’m using this code for blog posts. The issue is that if I have previously uploaded images in older posts before adding the code for the “Include in Rotator” option the “be_rotator_include” field seems to be set to null which results in the images not being included in the rotator. The only solution I’ve found so far is to resave the changes for the images. Is there a way to query for the images which “be_rotator_include” field is set to either 1 or null?
Thank you.
Bill Erickson says
This is actually a feature of WordPress 3.5. I needed this for a project as well, so I’m running WordPress trunk on that project.
Here’s an example of how I’m using it to show all posts that don’t have values for two fields: https://gist.github.com/3788219