Advertisement
Creative Coding

How to Add Custom Fields to Attachments

by

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:


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? (default false)
  • error_text - optional field to describe the error (if required is set to true)
  • options - optional field for radio and select types
  • show_in_modal - whether to show the field in modal or not (default true)
  • show_in_edit - whether to show the field in classic edit view or not (default true)
  • 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:

  1. $form_fields - An array of fields contained in the attachment edit form
  2. $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:

Attachment edit form with custom fields
Attachment edit form with custom fields

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.

Custom fields in modal

Custom fields in modal

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:

  1. $post - array which represents the attachment entity
  2. $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 an array 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>";
Show custom fields on front-end

Show custom fields on front-end

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.

Related Posts
  • Code
    Theme Development
    Custom Controls in the Theme CustomizerTheme customizer custom control 400
    In the last article, we explored the advanced controls available in the Theme Customizer, and how to implement them. We’re going to look at how to create our own custom control, allowing you to choose which Category of Posts are displayed on the home page. To get started, download version 0.6.0 of our Theme Customizer Example.Read More…
  • Code
    WordPress
    Mastering WordPress Meta Data: Querying Posts and Users by Meta DataMetadata
    So far in this series you've learned how to access WordPress meta data, and work with the arrays in which they are returned. We don't just add custom fields to WordPress posts so we can display this information, but it also so we can sort by it. Now that you know how to retrieve and display meta data, it's time to learn how to customize the WordPress loop to return only posts with specific meta values.Read More…
  • Code
    WordPress
    Mastering WordPress Meta Data: Working With LoopsMetadata
    In the first two parts of this series, we covered what meta data is in WordPress and how to work with the arrays that are typically returned. Now that you've learned to do the detective work necessary to find the structure of an array, it's time to learn to use loops to automate the process of outputting an array. Once you learn this important skill you will never have to write repetitive HTML markup again.Read More…
  • Code
    WordPress
    Mastering WordPress Meta Data: Understanding and Using ArraysMetadata
    In the first part of this series, we covered what WordPress meta data is, how it can be retrieved, and the various data types (such as objects or arrays) in which it can be returned. Now it's time to learn about the different types of arrays. When you write an array manually you know what its structure is an what the name of each index is. But when you are building arrays by querying a database, you are going to need to do some detective work to determine the structure of the data returned and the names of the indexes.Read More…
  • Code
    Plugins
    How to Create a WordPress Avatar Management Plugin from Scratch: Finishing TouchesPreview
    Avatar Manager for WordPress is a sweet and simple plugin for storing avatars locally and more. Easily. Enhance your WordPress website by letting your users choose between using Gravatar or a self-hosted avatar image right from their profile screen. Improved workflow, on-demand image generation and custom user permissions under a native interface. Say hello to the Avatar Manager plugin.Read More…
  • Code
    Creative Coding
    Utilizing Custom Fields to Create Review BoxesReview boxes thumb
    Reviews are perhaps one of the greatest powers of blogging in terms of authority. When done properly (with hard work and consistent information), review blogs are arguably the most profitable category in the blogosphere. But every blog has to offer a solid design in their posts, including reviews. In this post, we're going to talk about how to build the perfect review box, since review boxes are probably the first part readers check before reading a review.Read More…