Add A Meta Box In WordPress

Creating the options pages is something we’ve touched upon previously here on [wp], and whilst developing my soon-to-be-released WordPress [theme], Biblioteca, I wanted to add some options to the post writing page, and in this post I’m going to be sharing how I did it.

in-post-download

How the in-post options work

Basically, we’re going to be adding some boxes to the post and page writing pages that give our users a number of options, including customising the <title> tag, the meta description and the URL of a featured image:

These options get displayed immediately below the post/page writing area, and the user can enter text. This text, when the post is saved gets saved as custom fields. For example, on this post I’ll enter some text “Hello. This is a test.”  in the “Image” field. The results are below:

So that proves it works! Let’s get straight into implementing it. First off, all credits go to Justin Tadlock – code comes more or less straight out of Hybrid.

Making the in post options

The first thing you need to do in order to make your own in post options is to open up your functions.php file. This is the file that gives you a lot of power, but can also totally wreck your theme – power comes with responsibility! Always have a backup readily to hand.

The first code to add is the following, which does the job of adding the box to the admin menu:

<?php
/**
Made with the help of a tutorial at WPShout.com => https://wpshout.com.

Courtesy of the Hybrid theme - themehybrid.com

/* Add a new meta box to the admin menu. */
	add_action( 'admin_menu', 'hybrid_create_meta_box' );

/* Saves the meta box data. */
	add_action( 'save_post', 'hybrid_save_meta_data' );

/**
 * Function for adding meta boxes to the admin.
 * Separate the post and page meta boxes.

function hybrid_create_meta_box() {
	global $theme_name;

	add_meta_box( 'post-meta-boxes', __('Post options'), 'post_meta_boxes', 'post', 'normal', 'high' );
	add_meta_box( 'page-meta-boxes', __('Post options'), 'page_meta_boxes', 'page', 'normal', 'high' );
}

With that added, the next step is to start an array which will be the text used by the options page. The ‘name’ is the name of the custom field that will be generated by the options. Next is the text displayed on the right and finally the ‘type’ is what type of box the theme is to display; either the large textarea or the smaller single line text:

function hybrid_post_meta_boxes() {

	/* Array of the meta box options. */
	$meta_boxes = array(
		'title' => array( 'name' => 'Title', 'title' => __('Title', 'hybrid'), 'type' => 'text' ),
		'description' => array( 'name' => 'Description', 'title' => __('Description', 'hybrid'), 'type' => 'textarea' ),
		'image' => array( 'name' => 'Image', 'title' => __('Image:', 'hybrid'), 'type' => 'text' ),
		'featured' => array( 'name' => 'Featured', 'title' => __('Featured img:', 'hybrid'), 'type' => 'text' ),

	);

	return apply_filters( 'hybrid_post_meta_boxes', $meta_boxes );
}

You’ll notice the last bit of code is for pages only, where no featured image option is added.

Next, the code that displays the boxes and formats them:

function post_meta_boxes() {
	global $post;
	$meta_boxes = hybrid_post_meta_boxes(); ?>

	<table class="form-table">
	<?php foreach ( $meta_boxes as $meta ) :

		$value = get_post_meta( $post->ID, $meta['name'], true );

		if ( $meta['type'] == 'text' )
			get_meta_text_input( $meta, $value );
		elseif ( $meta['type'] == 'textarea' )
			get_meta_textarea( $meta, $value );
		elseif ( $meta['type'] == 'select' )
			get_meta_select( $meta, $value );

	endforeach; ?>
	</table>
<?php
}

/**
 * Displays meta boxes on the Write Page panel.  Loops
 * through each meta box in the $meta_boxes variable.
 * Gets array from hybrid_page_meta_boxes()
 *
 * @since 0.3
 */
function page_meta_boxes() {
	global $post;
	$meta_boxes = hybrid_page_meta_boxes(); ?>

	<table class="form-table">
	<?php foreach ( $meta_boxes as $meta ) :

		$value = stripslashes( get_post_meta( $post->ID, $meta['name'], true ) );

		if ( $meta['type'] == 'text' )
			get_meta_text_input( $meta, $value );
		elseif ( $meta['type'] == 'textarea' )
			get_meta_textarea( $meta, $value );
		elseif ( $meta['type'] == 'select' )
			get_meta_select( $meta, $value );

	endforeach; ?>
	</table>
<?php
}

/**
 * Outputs a text input box with arguments from the
 * parameters.  Used for both the post/page meta boxes.
 *
 * @since 0.3
 * @param array $args
 * @param array string|bool $value
 */
function get_meta_text_input( $args = array(), $value = false ) {

	extract( $args ); ?>

	<tr>
		<th style="width:10%;">
			<label for="<?php echo $name; ?>"><?php echo $title; ?></label>
		</th>
		<td>
			<input type="text" name="<?php echo $name; ?>" id="<?php echo $name; ?>" value="<?php echo wp_specialchars( $value, 1 ); ?>" size="30" tabindex="30" style="width: 97%;" />
			<input type="hidden" name="<?php echo $name; ?>_noncename" id="<?php echo $name; ?>_noncename" value="<?php echo wp_create_nonce( plugin_basename( __FILE__ ) ); ?>" />
		</td>
	</tr>
	<?php
}

/**
 * Outputs a select box with arguments from the
 * parameters.  Used for both the post/page meta boxes.
 *
 * @since 0.3
 * @param array $args
 * @param array string|bool $value
 */
function get_meta_select( $args = array(), $value = false ) {

	extract( $args ); ?>

	<tr>
		<th style="width:10%;">
			<label for="<?php echo $name; ?>"><?php echo $title; ?></label>
		</th>
		<td>
			<select name="<?php echo $name; ?>" id="<?php echo $name; ?>">
			<?php foreach ( $options as $option ) : ?>
				<option <?php if ( htmlentities( $value, ENT_QUOTES ) == $option ) echo ' selected="selected"'; ?>>
					<?php echo $option; ?>
				</option>
			<?php endforeach; ?>
			</select>
			<input type="hidden" name="<?php echo $name; ?>_noncename" id="<?php echo $name; ?>_noncename" value="<?php echo wp_create_nonce( plugin_basename( __FILE__ ) ); ?>" />
		</td>
	</tr>
	<?php
}

/**
 * Outputs a textarea with arguments from the
 * parameters.  Used for both the post/page meta boxes.
 *
 * @since 0.3
 * @param array $args
 * @param array string|bool $value
 */
function get_meta_textarea( $args = array(), $value = false ) {

	extract( $args ); ?>

	<tr>
		<th style="width:10%;">
			<label for="<?php echo $name; ?>"><?php echo $title; ?></label>
		</th>
		<td>
			<textarea name="<?php echo $name; ?>" id="<?php echo $name; ?>" cols="60" rows="4" tabindex="30" style="width: 97%;"><?php echo wp_specialchars( $value, 1 ); ?></textarea>
			<input type="hidden" name="<?php echo $name; ?>_noncename" id="<?php echo $name; ?>_noncename" value="<?php echo wp_create_nonce( plugin_basename( __FILE__ ) ); ?>" />
		</td>
	</tr>
	<?php
}

And finally, saving the options entered as custom fields:

/**
 * Loops through each meta box's set of variables.
 * Saves them to the database as custom fields.
 *
 * @since 0.3
 * @param int $post_id
 */
function hybrid_save_meta_data( $post_id ) {
	global $post;

	if ( 'page' == $_POST['post_type'] )
		$meta_boxes = array_merge( hybrid_page_meta_boxes() );
	else
		$meta_boxes = array_merge( hybrid_post_meta_boxes() );

	foreach ( $meta_boxes as $meta_box ) :

		if ( !wp_verify_nonce( $_POST[$meta_box['name'] . '_noncename'], plugin_basename( __FILE__ ) ) )
			return $post_id;

		if ( 'page' == $_POST['post_type'] && !current_user_can( 'edit_page', $post_id ) )
			return $post_id;

		elseif ( 'post' == $_POST['post_type'] && !current_user_can( 'edit_post', $post_id ) )
			return $post_id;

		$data = stripslashes( $_POST[$meta_box['name']] );

		if ( get_post_meta( $post_id, $meta_box['name'] ) == '' )
			add_post_meta( $post_id, $meta_box['name'], $data, true );

		elseif ( $data != get_post_meta( $post_id, $meta_box['name'], true ) )
			update_post_meta( $post_id, $meta_box['name'], $data );

		elseif ( $data == '' )
			delete_post_meta( $post_id, $meta_box['name'], get_post_meta( $post_id, $meta_box['name'], true ) );

	endforeach;
}
?>

And we’re done creating the options! Next, we’re going to be implementing these options.

Giving the user the option to change the title tag without a plugin

The first field/option we offered was “Title”. Obviously we’re going to use this as the <title> tag for our post. How? As you may recall, the text input into the boxes gets spitted out as a custom field, so we can display the “Title” as the title tag with the following (open up the header.php):

<title><?php echo get_post_meta($post->ID, "Title", true); ?></title>

But that would be a seriously bad idea. What if it’s the homepage?! What if there is no custom field?! The folllowing is much better; it should be used in conjunction with your current SEO’d setup, will only display on posts and pages and has an option for if no custom field exists:

<?php if ( is_single() || is_page() ) { ?><?php $title = get_post_meta($post->ID, 'Title', true);  if ($title) { ?>
<?php echo get_post_meta($post->ID, "Title", true); ?> | <?php bloginfo('name'); ?>
<?php } else { ?>
<?php wp_title(''); ?> | <?php bloginfo('name'); ?>
<?php } ?>
<?php } ?>

Giving the user the option to change the meta description without a plugin

The second field was ‘Description’. This is your meta description, an important part of your SEO strategy. Similar code to the above will display the content of the box, but with a fall back for if no custom field exists:

lt;?php if (is_single() || is_page() ) : if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<meta name="description" content="<?php $description = get_post_meta($post->ID, 'Description', true);  if ($description) { ?><?php echo get_post_meta($post->ID, "Description", true); ?>
<?php } else { ?><?php the_excerpt_rss(); ?><?php } ?>" />
<?php endwhile; endif; elseif(is_home()) : ?>
<meta name="description" content="<?php bloginfo('description'); ?>" />
<?php endif; ?>

Displaying images

The final field was the URL for a ‘featured’ image. We can integrate this into our WordPress theme with the following:

<?php $postimageurl = get_post_meta($post->ID, 'Image', true); if ($postimageurl) { ?>
<img src="<?php bloginfo('template_url'); ?>/scripts/timthumb.php?src=<?php echo get_post_meta($post->ID, "Image", true); ?>&h=250&w=250&zc=1" alt=""> 
<?php }else { ?> 
<img src="<?php bloginfo('template_url'); ?>/images/noimage.jpg" alt="No image available" />
<?php } ?>

Concluding

With that, we’re finished creating our in post options! The download for the code is available at the top of the post. This is a brilliant, simple and effective way to improve your WordPress theme.

If you have enjoyed this post, please take a second to save it to your favourite social bookmarking site with the links below and even [s] or [t]! Any questions, suggestions or queries, please do leave a comment.