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(); |
Midori says
I love you! More seriously, this is EXACTLY what I had been looking for. Thank you so much! Now, if I could bother you for how to make the first example multilingual ? (adding photo credit, how to make it so that label and helps can be automatically translated within the plugin I am creating? )
Bill Erickson says
Use the following format for the labels:
__( 'label', 'my-plugin-name' )
The first parameter is the label itself, and the second is the unique textdomain you’re using for your plugin. Here’s the first example using this: https://gist.github.com/1398077
Read this article for more information.
Midori says
Ooooh, I am thick. I should have known. Thank you very much!
Paul says
is it possible to use these fields in a meta_query?
Bill Erickson says
Yes it is. The last code example in the post shows it being used in a meta_query.
Paul says
thanks Bill, what I mean is can I do something like:
“fetch all posts that have this value for the attachment custom field”
on a custom post archive for example.
it’s getting late here…sorry
Bill Erickson says
Hmm, I don’t think that’s possible with a simple query. Here’s how I’d try doing it:
– Query for all attachment posts that have the specific custom field you want (last example in the post).
– Loop through the posts, adding the post_parent’s to an array if not already there
– Do a query for those posts
Something like this (completely untested): https://gist.github.com/1403244
Paul says
thanks for taking the time to post that code Bill. That may work!
Would be cool though to be able to query directly the attachments meta values from the parent post.
Bill Erickson says
It would be, but they are stored as separate posts. Attachments are posts, as are the main posts. The metadata of the attachment doesn’t contain any link to the main post. If you’re good with SQL you might be able to write a direct SQL query that can accomplish this, but I much prefer using WP Queries.
thankewe says
Absolutely brilliant stuff here, thanks so much.
Been looking for the ability to add form fields like this right in functions.php without having to edit the core.
Followup question…any idea how I might go about adding a button to the same form area?
I’d like to add some buttons that wouldn’t even need values to be saved…they would instead receive functionality via jQuery to perform tasks.
Bill Erickson says
You can add any HTML you want using the ‘html’ field type. That’s what I did with the second code example above.
thankewe says
I don’t know you…but I love you.
haha Thanks a ton man!
Gregory Schultz says
This is exactly what I’ve been looking for and thanks for putting this on the Internet. However, I’m not an expert on coding but I do know how to do basic coding and I’m stumped on how to display the custom field on my site. I added the ‘photo credit’ to my functions.php and I was able to add data to my images. I just need help on displaying the custom field on my site.
I’m using this to display image attachments on my site:
‘id); $args = array( ‘post_type’ => ‘attachment’,’post_status’ => null,’post_parent’ => $post->ID,’include’ => $thumb_id);
$thumb_images = get_posts($args);
foreach ($thumb_images as $thumb_image) {
if($thumb_image->tqmcf_disable) echo ”,’Image: ‘ . $thumb_image->tqmcf_disable . ”;
}
?>’
Thank you for putting this online and I appreciate any help in helping me,
Gregory S.
Bill Erickson says
It’s stored as postmeta, so to get the photographer name inside your loop, use something like this:
echo get_post_meta( $thumb_image->ID, 'be_photographer_name', true );
You might also look into using WP_Query for doing your queries rather than get_posts
Gregory Schultz says
Huzzah! I finally got it to work.
One final question. How do you only display the custom field if data has been entered? I’ve tried doing an ‘if’ statement and it didn’t work. This is what I have so far:
‘ get_the_ID(), ‘post_type’ => ‘attachment’, ‘post_mime_type’ =>’image’) );
foreach ( $attachments as $attachment_id => $attachment ) {
if (get_post_meta($attachment_id, ‘photo_credits’, true)) echo ”,’Image: ‘ . get_post_meta($attachment_id, ‘photo_credits’, true) . ”;
} ?>’
I appreciate the help,
Gregory S.
Bill Erickson says
You might try something like this ( https://gist.github.com/1483786 ) checking to see if it’s not empty rather than just true.
Gregory Schultz says
You rock! Thanks!
DeluxZ says
Thanks for this great tutorial. I have a small problem. I’m using a plugin called simple-portfolio. I want to add some custom fields when I create a project in this plugin I can add images. I want to add some custom fields to these images. I made a functions.php file in my theme folder with your functions from this post. The problem is the media uploader doesn’t save the fields. When I go to the Media plugin from WordPress and edit an image I added in the plugin it DOES save these fields. Any simple solution or should I contact the developer of the plugin?
Bill Erickson says
I’ve never used that plugin before so I can’t comment on why it isn’t working. It’s best to contact the developer of the plugin
Daris Fox says
Additional bit of code to wrap around the add fields section, so these fields only apply to images:
// for audio files
if( substr($post->post_mime_type, 0, 5) == ‘image’ ){
// Create data fields for images below.
}
Just tidies up the code and confusing the user with unnecessary fields when dealing with other media types.
One observation if you view the media library you will see that the URL uploader help text doesn’t have a br tag leaving additional fields below it out of alignment. Going to create a bug report about it, just don’t know how to fix it in the meantime.
Thanks for the code, it’s exactly what I was looking for a models ‘folio site.
Daris Fox says
Fixed the broken layout, it comes down to the CSS class: .media-item .describe th
It’s set to 140px which is too narrow for long label names resulting in a broken layout, over-riding the class with a width of 180px resolves the problem.on the media page, seems the iframe isn’t affected at least with the label names I’m using,
weston deboer says
Worked great! I have been looking for something like this for ages.
Rilwis says
That’s a cool tip. The way WP handles fields as an array is cool. I haven’t known about this before. Thank you!
Cor van Noorloos says
Hello Bill,
As I’m currently using Genesis for only a small set of projects of mine, I’m not entirely sure if it would be appropriate to ask this here.
Would it be possible to also extend the WP default settings using this tutorial?
Lets say I’d like to hide the media ‘Attachment Post URL’, and make the ‘None’ and ‘File URL’ button into a checkbox/radio button (which reads in my case, modal window true/false).
A small mockup: http://d.pr/JpH1 and https://gist.github.com/1817917
Bill Erickson says
Yep, that should definitely be doable.
Tom says
How can I save an array of checkboxes? I’m trying to tag WordPress Users in individual post-attachments.
https://gist.github.com/1957528