Welcome to part two of my how to creating a WordPress theme.
In the last part I showed you what files you’re required to have for WordPress to recognise your theme, the optional files WordPress uses for different types of posts and pages, how to split up your code between multiple files, and started going through the many, many template tags that you will want to use within your own theme. I left off at adding in the tags to footer.php for showing the sidebar, and performing the wp_footer hook.
I forgot to mention last post that, if you want to show a sidebar in your theme, you need to have another file called “sidebar.php” which contains the code for showing the user’s custom sidebar, or a standard sidebar if the user hasn’t set anything. WordPress then uses this file when you call get_sidebar to display the sidebar. An example of this file is:
<div class="sidebar">
<ul>
<?php
if(!function_exists('dynamic_sidebar') || !dynamic_sidebar()) {
?>
<li><h2><?php _e('Meta'); ?></h2>
<ul>
<?php wp_register(); ?>
<li><?php wp_loginout(); ?></li>
<li><a href="http://validator.w3.org/check/referer" title="This page validates as XHTML 1.0 Transitional">Valid <abbr title="eXtensible HyperText Markup Language">XHTML</abbr></a></li>
<li><a href="http://gmpg.org/xfn/"><abbr title="XHTML Friends Network">XFN</abbr></a></li>
<li><a href="http://wordpress.org/" title="Powered by WordPress, state-of-the-art semantic personal publishing platform.">WordPress</a></li>
<?php wp_meta(); ?>
</ul>
</li>
<li><h2>Archives</h2>
<ul>
<?php wp_get_archives(array('type' => 'monthly')); ?>
</ul>
</li>
<?php
}
?>
</ul>
</div> |
<div class="sidebar">
<ul>
<?php
if(!function_exists('dynamic_sidebar') || !dynamic_sidebar()) {
?>
<li><h2><?php _e('Meta'); ?></h2>
<ul>
<?php wp_register(); ?>
<li><?php wp_loginout(); ?></li>
<li><a href="http://validator.w3.org/check/referer" title="This page validates as XHTML 1.0 Transitional">Valid <abbr title="eXtensible HyperText Markup Language">XHTML</abbr></a></li>
<li><a href="http://gmpg.org/xfn/"><abbr title="XHTML Friends Network">XFN</abbr></a></li>
<li><a href="http://wordpress.org/" title="Powered by WordPress, state-of-the-art semantic personal publishing platform.">WordPress</a></li>
<?php wp_meta(); ?>
</ul>
</li>
<li><h2>Archives</h2>
<ul>
<?php wp_get_archives(array('type' => 'monthly')); ?>
</ul>
</li>
<?php
}
?>
</ul>
</div>
As you can see, the file first attempts to print out the user’s set sidebar (using dynamic_sidebar). In older versions of WordPress this function didn’t exist, so we need to test to make sure we’re using a version with dynamic sidebar support. If there is support, and the user has set up the sidebar with some widgets, dynamic_sidebar will return true to say a sidebar has been output. If it returns false, then a standard sidebar is displayed (most of the HTML in the above example).
The last two files we need to create (as part of this tutorial) are templates/singular.php and templates/multiple.php. Both these files can contain similar code. Sometimes, developers will usually just use the one “template” (and just include all their code in index.php). I personally can find that a little bit confusing, so split up the different types of post and page templates to make them more understandable. In both files, the major concept you’ll need to know about is “The Loop” (as the WordPress development team put it). This loop consists of two main parts:
- has_post()
- the_post()
Number one is the loop end check function, and is probably how WordPress developers came up with the name “The Loop” (most of the time you will use it as while(has_post()) in your code). This just checks if there’s another post available to be output. To actually move onto the next post (that has_post is saying can be displayed), you need to call the_post (number two above). This changes a global variable called $post to the next post in the list, and advances the system’s internal pointer to the next available post.
In my themes, I don’t worry about using while(has_post()) in templates/singular.php, since there is only one post to show. An example of a singular template page is as follows:
<?php
get_header();
the_post();
?>
<div <?php post_class(); ?>>
<h2 class="post-title"><a title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
<div class="post-meta"><?php
the_time(get_option('date_format'));
if(is_single()){
the_author_posts_link();
echo ' posted in ';
the_category(', ');
}else{
echo 'Posted by ';
the_author_posts_link();
}
if(comments_open()){
echo ' | ';
comments_popup_link();
}
edit_post_link('Edit', ' | ');
?></div>
<div class="entry"><?php
the_content('Read more...');
$args = array(
'before' => '<div class="post-pages">Pages:',
'after' => '</div>',
'nextpagelink' => 'Next Page',
'previouspagelink' => 'Previous Page'
);
wp_link_pages($args);
?></div>
<?php
if(is_single()){
?><div class="pagenavi">
<span class="previous-page"><?php
previous_post_link('%link', '« %title');
?></span><span class="next-page"><?php
next_post_link('%link', '%title »');
?></span>
</div>
<?php
}
comments_template('', true);
?>
</div>
<?php
get_footer();
?> |
<?php
get_header();
the_post();
?>
<div <?php post_class(); ?>>
<h2 class="post-title"><a title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
<div class="post-meta"><?php
the_time(get_option('date_format'));
if(is_single()){
the_author_posts_link();
echo ' posted in ';
the_category(', ');
}else{
echo 'Posted by ';
the_author_posts_link();
}
if(comments_open()){
echo ' | ';
comments_popup_link();
}
edit_post_link('Edit', ' | ');
?></div>
<div class="entry"><?php
the_content('Read more...');
$args = array(
'before' => '<div class="post-pages">Pages:',
'after' => '</div>',
'nextpagelink' => 'Next Page',
'previouspagelink' => 'Previous Page'
);
wp_link_pages($args);
?></div>
<?php
if(is_single()){
?><div class="pagenavi">
<span class="previous-page"><?php
previous_post_link('%link', '« %title');
?></span><span class="next-page"><?php
next_post_link('%link', '%title »');
?></span>
</div>
<?php
}
comments_template('', true);
?>
</div>
<?php
get_footer();
?>
At the top and bottom you can see get_header() and get_footer(). These two functions tell WordPress that we want the header.php and footer.php files created in part 1 to be included at those two places respectively. The second function call in this template is the_post, which (as I stated earlier) advances WordPress’ internal pointer to the next post, and sets $post to the current post. The rest of the code should be self-explanatory – post_class prints the class attribute for the post/page wrapper, the_title prints the post or title, the_time prints the current time (same type of syntax as php’s date function), the_content prints out the post or page content, etc.
Some functions that may not seem that intuitive may be “is_single”, “wp_link_pages”, and “*_post_link” (* being previous or next). The first one, is_single, is used for checking whether the current thing being looked at as part of the loop is a regular blog post or not. If it is, is_single will return true. The second function prints out page links for posts or pages that are longer than a single page long. Finally, *_post_link prints out a link to the previous or next post (respectively), allowing visitors to navigate between posts without having to go back to the user’s posts list.
The final part of the page that should be pointed out is comments_template. This function includes your theme’s comments.php file (yes, another file). I will show an example of this one at the end of this post. The second parameter of comments_template indicates whether WordPress should separate comments out by type (regular comments, pingbacks and trackbacks).
Now for an example of templates/multiple.php:
<?php
get_header();
if(have_posts()){
while(have_posts()){
the_post();
?>
<div <?php post_class(); ?>>
<h2 class="post-title"><a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
<div class="post-meta"><?php
the_time(get_option('date_format'));
if(is_single()){
the_author_posts_link();
echo ' posted in ';
the_category(', ');
}else{
echo 'Posted by ';
the_author_posts_link();
}
if(comments_open()){
echo ' | ';
comments_popup_link();
}
edit_post_link('Edit', ' | ');
?></div>
<div class="entry"><?php
the_post_thumbnail();
the_content('Read more...');
?></div>
</div>
<?php
}
?>
<div class="pagenavi">
<span class="previous-page"><?php
next_posts_link('« Older Posts');
?></span><span class="next-page"><?php
previous_posts_link('Newer Posts »');
?></span>
</div>
<?php
}else{
?>
<div class="page">
<div class="entry"><?php
printf('No posts were found. Why not <a title="Go Back" href="%1$s">go back to the last page</a>?', $_SERVER['HTTP_REFERER']);
?></div>
</div>
<?php
}
get_footer();
?> |
<?php
get_header();
if(have_posts()){
while(have_posts()){
the_post();
?>
<div <?php post_class(); ?>>
<h2 class="post-title"><a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2>
<div class="post-meta"><?php
the_time(get_option('date_format'));
if(is_single()){
the_author_posts_link();
echo ' posted in ';
the_category(', ');
}else{
echo 'Posted by ';
the_author_posts_link();
}
if(comments_open()){
echo ' | ';
comments_popup_link();
}
edit_post_link('Edit', ' | ');
?></div>
<div class="entry"><?php
the_post_thumbnail();
the_content('Read more...');
?></div>
</div>
<?php
}
?>
<div class="pagenavi">
<span class="previous-page"><?php
next_posts_link('« Older Posts');
?></span><span class="next-page"><?php
previous_posts_link('Newer Posts »');
?></span>
</div>
<?php
}else{
?>
<div class="page">
<div class="entry"><?php
printf('No posts were found. Why not <a title="Go Back" href="%1$s">go back to the last page</a>?', $_SERVER['HTTP_REFERER']);
?></div>
</div>
<?php
}
get_footer();
?>
As you can see, this file is pretty much the same as templates/singular.php. The only major differences are the use of an actual loop (while(has_post()) as well as the_post()), using the_permalink() in the link element for the title, showing post thumbnails (with the_post_thumbnail()), not including post pages for longer posts, using *_posts_link for jumping between pages, and not including comments_template. You’ll also notice the use of an if/else around The Loop. This is in case there are no posts available for the archive being displayed.
You may be wondering why the previous page uses next_posts_link, and the next uses previous_posts_link. This is because WordPress puts older posts on higher numbered pages, and names those higher numbers as “next”. So, for my themes (and this example), I’ve chosen to flip them to make it look a bit better on the archive pages.
The final template file to populate is comments.php. This one is fairly short if you use WordPress’ default output features and form. I’ve recently started using this in my own themes:
<div class="comments">
<?php
comment_form('comment_notes_after=');
if(have_comments()){
?>
<div class="comment-list" id="comments">
<h3>Previous Comments</h3>
<?php
wp_list_comments('style=div&max-depth=3');
if(paginate_comments_links('echo=0')){
?>
<div class="commentnavi">
<?php paginate_comments_links(); ?>
</div>
<?php
}
?>
</div>
<?php
}
?>
</div> |
<div class="comments">
<?php
comment_form('comment_notes_after=');
if(have_comments()){
?>
<div class="comment-list" id="comments">
<h3>Previous Comments</h3>
<?php
wp_list_comments('style=div&max-depth=3');
if(paginate_comments_links('echo=0')){
?>
<div class="commentnavi">
<?php paginate_comments_links(); ?>
</div>
<?php
}
?>
</div>
<?php
}
?>
</div>
This example first outputs the comments form (without any notes, given by the parameter), Then checks if there’s any comments to print before printing a title and using the standard WordPress comments output mechanism (wp_list_comments). I then check if there’s any page links (using the function that outputs them, since there’s no other way to check), and output them if there are more pages.
Now to get the theme working properly, my example (and most of my themes) gets index.php to include templates/singular.php, and all archive type pages to include templates/multiple.php. Once all these files are done, the theme should appear and look pretty close to how you designed it. You may need to tinker with your styles a little, and add in any classes that WordPress itself uses.
You’re pretty close to finished now. There is one more file that needs to be created if you are using the sidebar and want to allow a custom navigation. Since this post is already pretty long, I will leave that for another post in another couple of days time.
If you have any problems up to this point, please leave a comment.
Robert