The Ultimate WordPress Theme Tutorial (7)

The WordPress Theme Single Post, Post Attachment, & 404 Templates

Posted on  by Ian Stewart

You’ve built an index of all your posts, now you need to create a template to frame each piece of content (or missing content) on it’s own.

The Template for Templates

The structure of single.php (and almost all the other templates we’ll be creating) is largely the same as index.php. In fact you can think of it as our template-template.

<?php get_header(); ?>
		<div id="container">
			<div id="content">
				<div id="nav-above">
				</div><!-- #nav-above -->
				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
				</div><!-- #post-<?php the_ID(); ?> -->
				<div id="nav-below">
				</div><!-- #nav-below -->
			</div><!-- #content -->
		</div><!-- #container -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>

But there are going to be some notable differences. Starting with the_post() andcomments_template().
We’ll be calling the_post() near the top of our page just after the opening of the content div and just before the navigation. We won’t need a loop in this template as WordPress knows just what post we’re looking at thanks to the_permalink().
And since this is a single post, we’ll need the comments_template(). And because we’ll be separating our comments and trackbacks when we come to coding up comments.php, we need to call it just like so:

<?php comments_template('', true); ?>

comments_template() will need to go just before the close of the #content div right after the navigation.

Single Post Navigation

Instead of using the poorly named next_posts_link() and previous_posts_link()we’ll be using the mostly accurately named previous_post_link() andnext_post_link(). They do just what you think they do.

				<div id="nav-above">
					<div><?php previous_post_link( '%link', '<span>&laquo;</span> %title' ) ?></div>
					<div><?php next_post_link( '%link', '%title <span>&raquo;</span>' ) ?></div>
				</div><!-- #nav-above -->
				<div id="nav-below">
					<div><?php previous_post_link( '%link', '<span>&laquo;</span> %title' ) ?></div>
					<div><?php next_post_link( '%link', '%title <span>&raquo;</span>' ) ?></div>
				</div><!-- #nav-below -->

Single Post Titles

If you remember from our header.php lesson, we used a dynamic IF statement to clear the way for our Single post titles to take precedence on the page in the eyes of screen readers. We take advantage of this in this and all the rest of our Theme Templates by wrapping the title in and h1 tag.

					<h1><?php the_title(); ?></h1>

You’ll notice that our post title code is a little simpler too. The benefit of not having to link to anything now.

Single Post Entry Utility Links

The entry utility is … complicated. Here I think you’ll see the benefit of getting something right once and standing on the shoulders of others.
Before we take a look at the code we should think about why it is complicated. Because of the way comments work in WordPress we need to account for a few different scenarios: Open comments and trackbacks; only trackbacks open; only comments open; comments and trackbacks closed. And that means … what looks like a mess of IF statements.
It can be daunting. The code is commented but remember to look for the blocks of IF and ELSEIF statements and you’ll be fine.
We also want to print a link to our permalink here for bookmarking purposes and the RSS for this particular single post—useful for tracking developing conversations.

					<div>
					<?php printf( __( 'This entry was posted in %1$s%2$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>. Follow any comments here with the <a href="%5$s" title="Comments RSS to %4$s" rel="alternate" type="application/rss+xml">RSS feed for this post</a>.', 'your-theme' ),
						get_the_category_list(', '),
						get_the_tag_list( __( ' and tagged ', 'your-theme' ), ', ', '' ),
						get_permalink(),
						the_title_attribute('echo=0'),
						comments_rss() ) ?>
<?php if ( ('open' == $post->comment_status) && ('open' == $post->ping_status) ) : // Comments and trackbacks open ?>
						<?php printf( __( '<a href="#respond" title="Post a comment">Post a comment</a> or leave a trackback: <a href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( !('open' == $post->comment_status) && ('open' == $post->ping_status) ) : // Only trackbacks open ?>
						<?php printf( __( 'Comments are closed, but you can leave a trackback: <a href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( ('open' == $post->comment_status) && !('open' == $post->ping_status) ) : // Only comments open ?>
						<?php _e( 'Trackbacks are closed, but you can <a href="#respond" title="Post a comment">post a comment</a>.', 'your-theme' ) ?>
<?php elseif ( !('open' == $post->comment_status) && !('open' == $post->ping_status) ) : // Comments and trackbacks closed ?>
						<?php _e( 'Both comments and trackbacks are currently closed.', 'your-theme' ) ?>
<?php endif; ?>
<?php edit_post_link( __( 'Edit', 'your-theme' ), "nttttt<span class="edit-link">", "</span>" ) ?>
					</div><!-- .entry-utility -->

That wasn’t so bad was it?

Single Post Content

Unlike index.phpsingle.php content is pretty simple. Just one plain function call followed by wp_link_pages().

<?php the_content(); ?>
<?php wp_link_pages('before=<div>' . __( 'Pages:', 'your-theme' ) . '&after=</div>') ?>

Post Attachments

Not a lot of people use post attachments but they’re kinda interesting. When you add an image to your post you’re actually attaching it to the post. And, of course, you can attach more than just images. We’re going to make an attachment.php template but you can, if you like, adapt it further to cover other types of attachments like video, audio, and applications, by making video.phpaudio.php, and application.php templates. There’s lots of different ways to be creative with attachment templates and WordPress.
The easiest way to proceed here is by copying single.php, renaming it attachment.php, and making the following changes.
First of all, delete the top navigation. We won’t need it at all here. Replace it with a page title that links back to your parent post.

				<h1><a href="<?php echo get_permalink($post->post_parent) ?>" title="<?php printf( __( 'Return to %s', 'your-theme' ), wp_specialchars( get_the_title($post->post_parent), 1 ) ) ?>" rev="attachment"><span>&laquo; </span><?php echo get_the_title($post->post_parent) ?></a></h1>

Since the page title is now wrapped in h1 tags that means our post title should be wrapped in h2 tags.

					<h2><?php the_title(); ?></h2>

Now, because our attachment template needs to actually show the attachment, our content needs to reflect that. And since most attachments are going to be images, we’ll want to check for that and cover that scenario with an IF statement.

					<div>
						<div>
<?php if ( wp_attachment_is_image( $post->id ) ) : $att_image = wp_get_attachment_image_src( $post->id, "medium"); ?>
						<p><a href="<?php echo wp_get_attachment_url($post->id); ?>" title="<?php the_title(); ?>" rel="attachment"><img src="<?php echo $att_image[0];?>" width="<?php echo $att_image[1];?>" height="<?php echo $att_image[2];?>"  alt="<?php $post->post_excerpt; ?>" /></a>
						</p>
<?php else : ?>
						<a href="<?php echo wp_get_attachment_url($post->ID) ?>" title="<?php echo wp_specialchars( get_the_title($post->ID), 1 ) ?>" rel="attachment"><?php echo basename($post->guid) ?></a>
<?php endif; ?>
						</div>
						<div><?php if ( !empty($post->post_excerpt) ) the_excerpt() ?></div>
<?php the_content( __( 'Continue reading <span>&raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div>' . __( 'Pages:', 'your-theme' ) . '&after=</div>') ?>
					</div><!-- .entry-content -->

Delete the bottom navigation from what was once your old single.php, and you’re done your attachment.php Template.

The 404 Template

A 404 Error is the server code for, “I can’t find this page” and it’s an event you need to take care of in your WordPress Themes. What happens when a link to your blog has a post url typed incorrectly? Or you unpublish a blog post? Your server coughs up a 404 error.
Luckily, WordPress has a template for that. It’s called, 404.php. The technique I stick with for 404 Templates is pretty straightforward but it works. Apologize and include a search form. There might be more creative solutions but none that get out of your visitor’s way faster.
Go back to your template-template above, drop the navigation and add something like this to the content.

				<div id="post-0">
					<h1><?php _e( 'Not Found', 'your-theme' ); ?></h1>
					<div>
						<p><?php _e( 'Apologies, but we were unable to find what you were looking for. Perhaps searching will help.', 'your-theme' ); ?></p>
	<?php get_search_form(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-0 -->

How To Create a WordPress Theme

This post is part of a WordPress Themes Tutorial that will show you how to create a powerful WordPress Theme from scratch. Read it from the beginning and code yourself up something awesome.