You should be familiar with custom fields in WordPress. We use them on a post or a page to add extra data. In WordPress attachments are also saved as posts, so custom fields are available for them too.
Today we'll see how we can add some custom fields so that attachments can carry more information than just the default data.
What We'll Do
First of all, we are going to create a plugin to handle the attachments custom fields. It will get a set of options, bake them so they become part of the form when we edit an attachment, and save them into the database.
For this, we will use two WordPress hooks:
-
attachment_fields_to_edit
to handle the edit form -
attachment_fields_to_save
to save the custom fields
1. Create the Plugin
I will pass quickly on this part as it's not the main purpose of this tutorial.
Create a new folder in the plugins directory (wp-content/plugins/media-fields/ for example) and put a file (named plugin.php) inside. Let's also put a file called custom_media_fields.php which will hold our options.
This is what your plugin.php file should look like at first:
/* Plugin Name: Wptuts+ Custom Media Fields Plugin URI: Description: Create attachments custom fields Version: 0.1 Author: Guillaume Voisin Author URI: http://wp.tutsplus.com/author/guillaumevoisin License: GPL2 */ require_once( plugin_dir_path( __FILE__ ) . '/custom_media_fields.php' ); Class Wptuts_Custom_Media_Fields { private $media_fields = array(); function __construct( $fields ) { } public function applyFilter( $form_fields, $post = null ) { } function saveFields( $post, $attachment ) { } } $cmf = new Wptuts_Custom_Media_Fields( $attchments_options );
This is the base we'll populate in the following sections. For now, let's define our set of options.
2. Define Our Options
In the other file, let's add some options to enhance the attachment edit form. We'll consider, for this tutorial, options to improve images. For instance, we'll add copyright, author description, watermark, rating, and image disposition fields.
$themename = "twentytwelve"; $attchments_options = array( 'image_copyright' => array( 'label' => __( 'Image copyright', $themename ), 'input' => 'text', 'helps' => __( 'If your image is protected by copyrights', $themename ), 'application' => 'image', 'exclusions' => array( 'audio', 'video' ), 'required' => true, 'error_text' => __( 'Copyright field required', $themename ) ), 'image_author_desc' => array( 'label' => __( 'Image author description', $themename ), 'input' => 'textarea', 'application' => 'image', 'exclusions' => array( 'audio', 'video' ), ), 'image_watermark' => array( 'label' => __( 'Image watermark', $themename ), 'input' => 'checkbox', 'application' => 'image', 'exclusions' => array( 'audio', 'video' ) ), 'image_stars' => array( 'label' => __( 'Image rating', $themename ), 'input' => 'radio', 'options' => array( '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4 ), 'application' => 'image', 'exclusions' => array( 'audio', 'video' ) ), 'image_disposition' => array( 'label' => __( 'Image disposition', $themename ), 'input' => 'select', 'options' => array( 'portrait' => __( 'portrtait', $themename ), 'landscape' => __( 'landscape', $themename ) ), 'application' => 'image', 'exclusions' => array( 'audio', 'video' ) ) );
It is basically an associative array which contains these parameters:
-
label
- the field name that will be displayed -
input
- the input type (e.g text, select, radio, ...) -
helps
- information to help the user filling in the field -
application
- which attchment mime type to apply -
exclusions
- which attchment mime type to exclude -
required
- is the field required? (defaultfalse
) -
error_text
- optional field to describe the error (if required is set totrue
) -
options
- optional field for radio and select types -
show_in_modal
- whether to show the field in modal or not (defaulttrue
) -
show_in_edit
- whether to show the field in classic edit view or not (defaulttrue
) -
extra_rows
- additional rows to display content (within the same "tr
" tag) -
tr
- additional rows ("tr
" tag)
The highlitghted options represent options we will manually deal with whereas others are default ones WordPress will process automatically.
As we are dealing with images, we set the application
parameter to "image
". It will actually apply to all kinds of images whose mime type starts with "image
" such as image/jpeg
, image/png
and so on. You could exlude the gif
mime type by setting it in the exclusions field for example.
Now our options are set, let's dig into the hooks.
3. The Hooks
As mentionned earlier, we'll deal with two hooks.
We bind our two functions to those hooks in the constructor method.
function __construct( $fields ) { $this->media_fields = $fields; add_filter( 'attachment_fields_to_edit', array( $this, 'applyFilter' ), 11, 2 ); add_filter( 'attachment_fields_to_save', array( $this, 'saveFields' ), 11, 2 ); }
Now let's see those hooks in detail.
attachment_fields_to_edit
It has two parameters:
-
$form_fields
- An array of fields contained in the attachment edit form -
$post
- Object which represents the attachment itself
We will use the $form_fields
parameter to merge our own fields and check each one of them against attachment requirements (mime type for instance).
public function applyFilter( $form_fields, $post = null ) { // If our fields array is not empty if ( ! empty( $this->media_fields ) ) { // We browse our set of options foreach ( $this->media_fields as $field => $values ) { // If the field matches the current attachment mime type // and is not one of the exclusions if ( preg_match( "/" . $values['application'] . "/", $post->post_mime_type) && ! in_array( $post->post_mime_type, $values['exclusions'] ) ) { // We get the already saved field meta value $meta = get_post_meta( $post->ID, '_' . $field, true ); // Define the input type to 'text' by default $values['input'] = 'text'; // And set it to the field before building it $values['value'] = $meta; // We add our field into the $form_fields array $form_fields[$field] = $values; } } } // We return the completed $form_fields array return $form_fields; }
At this step, you should have your attachment edit form enhanced with the new fields we've added. But they will look like text inputs. We now have to consider different kinds of inputs (radio, checkbox, etc...).
So let's edit our function to handle this. Replace the $values['input'] = 'text';
with the following code:
switch ( $values['input'] ) { default: case 'text': $values['input'] = 'text'; break; case 'textarea': $values['input'] = 'textarea'; break; case 'select': // Select type doesn't exist, so we will create the html manually // For this, we have to set the input type to 'html' $values['input'] = 'html'; // Create the select element with the right name (matches the one that wordpress creates for custom fields) $html = '<select name="attachments[' . $post->ID . '][' . $field . ']">'; // If options array is passed if ( isset( $values['options'] ) ) { // Browse and add the options foreach ( $values['options'] as $k => $v ) { // Set the option selected or not if ( $meta == $k ) $selected = ' selected="selected"'; else $selected = ''; $html .= '<option' . $selected . ' value="' . $k . '">' . $v . '</option>'; } } $html .= '</select>'; // Set the html content $values['html'] = $html; break; case 'checkbox': // Checkbox type doesn't exist either $values['input'] = 'html'; // Set the checkbox checked or not if ( $meta == 'on' ) $checked = ' checked="checked"'; else $checked = ''; $html = '<input' . $checked . ' type="checkbox" name="attachments[' . $post->ID . '][' . $field . ']" id="attachments-' . $post->ID . '-' . $field . '" />'; $values['html'] = $html; break; case 'radio': // radio type doesn't exist either $values['input'] = 'html'; $html = ''; if ( ! empty( $values['options'] ) ) { $i = 0; foreach ( $values['options'] as $k => $v ) { if ( $meta == $k ) $checked = ' checked="checked"'; else $checked = ''; $html .= '<input' . $checked . ' value="' . $k . '" type="radio" name="attachments[' . $post->ID . '][' . $field . ']" id="' . sanitize_key( $field . '_' . $post->ID . '_' . $i ) . '" /> <label for="' . sanitize_key( $field . '_' . $post->ID . '_' . $i ) . '">' . $v . '</label><br />'; $i++; } } $values['html'] = $html; break; }
Now, we can build common HTML elements. Let's check out our attachment edit form. It should look just like this:
The custom fields, depending on whether you set their modal options to true or not, will also appear in the media modal form when you edit a post.
Now our fields are displayed in our attachment edit form, we have to save them in the database. For this, we are going to use the second hook.
attachment_fields_to_save
This hook also has two parameters:
-
$post
-array
which represents the attachment entity -
$attachment
- contains all fields attached to the attachment post
Now, let's fill the function saveFields
we left in the previous section.
function saveFields( $post, $attachment ) { // If our fields array is not empty if ( ! empty( $this->media_fields ) ) { // Browser those fields foreach ( $this->media_fields as $field => $values ) { // If this field has been submitted (is present in the $attachment variable) if ( isset( $attachment[$field] ) ) { // If submitted field is empty // We add errors to the post object with the "error_text" parameter we set in the options if ( strlen( trim( $attachment[$field] ) ) == 0 ) $post['errors'][$field]['errors'][] = __( $values['error_text'] ); // Otherwise we update the custom field else update_post_meta( $post['ID'], '_' . $field, $attachment[$field] ); } // Otherwise, we delete it if it already existed else { delete_post_meta( $post['ID'], $field ); } } } return $post; }
Ok, now our custom fields are saved into the database and will be available for the front-end.
- Please be careful when manipulating the post parameter in both hooks. It is an
object
in the first one and anarray
in the second one. -
Tip: the
update_post_meta
will create the meta if it doesn't exist. -
Tip: We prefix the custom field key with an underscore "
_
" so that they won't be listed in the custom fields metaboxes on post edit pages.
Error Considerations
As of version 3.5, it seems that errors still don't appear on attachment edit forms. I tried to investigate the core code, and despite the fact it should have been fixed (http://core.trac.wordpress.org/ticket/13810), it seems not.
For the ajax save process, it is certain it's not done yet as mentionned in the ajax-actions.php
file:
$errors = $post['errors']; // @todo return me and display me!
So right now, errors are not going to work properly, but the code is done so that when those bugs are fixed, it'll work.
Front End
To use those custom fields in your templates, you just have to retrieve post metas the same way you would for regular posts. Don't forget to add the "_
" prefix to the custom fields' keys.
For instance, you could do like so:
echo "<ul>"; echo " <li><strong>Copyright</strong>: " . get_post_meta( get_the_ID(), '_image_copyright', true ) . "</li>"; echo " <li><strong>Rating</strong>: " . get_post_meta( get_the_ID(), '_image_stars', true ) . "</li>"; echo " <li><strong>Author description</strong>: " . get_post_meta( get_the_ID(), '_image_author_desc', true ) . "</li>"; echo " <li><strong>Image disposition</strong>: " . get_post_meta( get_the_ID(), '_image_disposition', true ) . "</li>"; echo " <li><strong>Watermark?</strong> " . ( get_post_meta( get_the_ID(), '_image_watermark', true ) == "on" ? "yes" : "no" ) . "</li>"; echo "</ul>";
Go Further
There are several points of improvements depending on your needs:
- You could have your settings in the database so it becomes more flexible to add, edit, and remove them
- You could have a default value that is set for all new attachments when a value is not set
- You should set some style for the modal form so it displays the custom fields properly
Conclusion
Don't hesitate to share with us your ideas on how to improve this plugin and what you would expect from such functionality.
Comments