Advertisement

WordPress: Beginner to Master, Part 5

by

Throughout this six-part beginner-to-master series, we'll be using the advanced features of WordPress to create our own portfolio & blog, complete with an options page, multiple styles and support for the new WordPress 2.7 features. Today, we'll deal with the comments, the search, and the archive.

This part is mainly about fixing up the extra parts we haven't yet got to. In the first section, we create the Comments template – with support for the new features in WP 2.7, while also accomodating for older versions.

In the second section, we create the archive and search templates.


Also available in this series:

  1. WordPress: Beginner to Master, Part 1
  2. WordPress: Beginner to Master, Part 2
  3. WordPress: Beginner to Master, Part 3
  4. WordPress: Beginner to Master, Part 4
  5. WordPress: Beginner to Master, Part 5
  6. WordPress: Beginner to Master, Part 6

Jump to a Section


Comments

WordPress 2.7 introduced several new features for the comments section, including threaded comments, paged comments, alternating styles and streamlined code. However these functions won't work correctly in older versions of WordPress. To combat this, we will be using a WordPress Filter so that older versions will use one comments file, and newer versions another.


Threaded comments in WP 2.7

Add the following code to the bottom of functions.php:

 
// If using 2.6 or below, use legacy comments display 
add_filter( 'comments_template', 'legacy_comments' ); 
function legacy_comments( $file ) { 
    if ( !function_exists('wp_list_comments') ) 
        $file = TEMPLATEPATH . '/comments.legacy.php'; 
    return $file; 
}

The wp_list_comments() function is a new addition to WordPress 2.7; so we are checking whether it exists using the function_exists() PHP function. If it doesn't, a WordPress filter is added to redirect to the comments.legacy.php file when the comments template is requested.

All newer WordPress versions will thus continue using comments.php (since they contain the wp_list_comments() function.)

Comments for WP 2.7+

Create a new file named comments.php. Inside, start with:

 
<?php 
if ( have_comments() ) : ?> 
 
    <h4 id="comments"> 
        <?php comments_number('No Comments', 'One Comment', '% Comments' ); ?> 
    </h4> 
 
    <ol class="commentlist"> 
        <?php wp_list_comments(); ?> 
    </ol> 
 
    <div class="comms-navigation"> 
        <div class="alignleft"><?php previous_comments_link() ?></div> 
        <div class="alignright"><?php next_comments_link() ?></div> 
    </div>

If the current post has comments assigned to it, the number of comments is output into a title tag using the comments_number() function. The comments are then displayed in an ordered list using the new wp_list_comments() function.

Each comment will be displayed using a built-in template which is easy to style in various ways, however if you need more control over how each comment looks, refer to this page on the WordPress Codex.

The comment navigation tags are also new to 2.7 and allow 'pagination' between a set number of comments (just like you have between blog posts).

In the next code, a message is displayed if comments are closed for the post:

 
<?php 
else : // no comments so far 
 
    if ('open' == $post->comment_status) : 
        // If comments are open, but there are no comments. 
    else : 
        echo"<p>Comments are closed on this post.</p>"; 
    endif; 
 
endif; 
?>

Next task is to display the actual comment form:


 
<?php if ('open' == $post->comment_status) : ?> 
 
    <div id="respond"> 
    <h4><?php comment_form_title( 'Leave a Reply', 'Leave a Reply to %s' ); ?></h4> 
     
    <div class="cancel-comment-reply"> 
        <small><?php cancel_comment_reply_link(); ?></small> 
    </div> 
     
    <?php if ( get_option('comment_registration') && !$user_ID ) : ?> 
        <p>You must be <a href="<?php echo get_option('siteurl'); ?> 
/wp-login.php?redirect_to=<?php echo urlencode(get_permalink()); ?>">logged in</a> to post a comment.</p> 
    <?php else : ?>

If comments are set to open, a 'Leave a Reply' title is displayed using the new comment_form_title() function. If the user is replying to a comment, the %s in 'Leave a Reply to %s' is replaced with the author name of the comment they're replying to.

A cancel reply link is also displayed if this is a reply using cancel_comment_reply_link().

Finally, if only logged-in users can comment (and the user isn't logged in), an error message is displayed pointing to the login page.

If registration isn't required (or if the user is logged in):

 
<form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post" id="commentform"> 
 
<?php if ( $user_ID ) : ?> 
 
    <p>Logged in as <a href="<?php echo get_option('siteurl'); ?>/wp-admin/profile.php"> 
<?php echo $user_identity; ?></a>. <a href="<?php echo wp_logout_url(get_permalink()); ?>"  
title="Log out of this account">Log out &raquo;</a></p> 
 
<?php else : ?> 
 
    <p><label for="author">Name:</label> <input type="text" name="author" id="author"  
value="<?php echo $comment_author; ?>" size="22" tabindex="1" /></p> 
 
    <p><label for="email">Email:</label> <input type="text" name="email" id="email"  
value="<?php echo $comment_author_email; ?>" size="22" tabindex="2" /></p> 
 
    <p><label for="url">Website:</label> <input type="text" name="url" id="url"  
value="<?php echo $comment_author_url; ?>" size="22" tabindex="3" /></p> 
 
<?php endif; ?>

The comment form is opened. If the user is logged in, a "Logged in as ... Log out?" message is displayed – otherwise the 'Name', 'Email', and 'Website' fields are displayed.

Finally, the 'Message' textarea and Submit buttons are displayed, the form closed and the if statements closed:

 
    <p><label for="comment">Comment:</label> 
      <textarea name="comment" id="comment" cols="70" rows="10" tabindex="4"></textarea></p> 
 
    <p><label for="submit">&nbsp;</label> 
      <input name="submit" type="submit" id="submit" tabindex="5" value="Submit Comment" /> 
    <?php comment_id_fields(); ?> 
    </p> 
    <?php do_action('comment_form', $post->ID); ?> 
 
    </form> 
 
<?php endif; // If registration required and not logged in ?> 
</div><!--/respond--> 
 
<?php endif; // if you delete this the sky will fall on your head ?>

comment_id_fields() outputs the current post's ID in a hidden field. In WP 2.6- you had to make this manually, but now it's required to use this function.

In the Dashboard, go to Settings -> Discussion and tick the 'Enable Threaded Comments' and 'Break Comments into Pages' options. Then preview the comment template by going to any blog post:



Styling Comments

Add to style.css:

 
/* Comments */ 
h4#comments { 
	clear: both; 
	margin: 45px 0 5px 0; 
	} 
 
.commentlist li { 
	font-size: inherit; 
} 
 
.commentlist li .avatar {  
	float: right; 
	} 
 
.commentlist cite { 
	font-weight: bold; 
	font-style: normal; 
	font-size: 0.95em; 
	} 
	 
.commentlist p { 
	font-size: 0.8em; 
	font-weight: normal; 
	line-height: 1.5em; 
	margin: 10px 5px 10px 0; 
	text-transform: none; 
	} 
 
.commentmetadata { 
	display: block; 
	font-size: 0.8em; 
	font-weight: normal; 
	line-height: 1.1em; 
	margin: 0; 
	} 
	 
.commentlist .reply { 
	font-size: 0.8em; 
	font-weight: bold; 
	} 
 
.commentlist { 
	margin: 0 0 20px 0; 
	} 
 
.commentlist li { 
	margin: 15px 0 10px; 
	padding: 10px; 
	list-style: none; 
	} 
	 
.commentlist li ul li {  
	margin-right: -5px; 
	margin-left: 10px; 
	} 
 
.comms-navigation, .navigation { 
	clear: both; 
	display: block; 
	margin-bottom: 20px; 
	overflow: hidden; 
	} 
 
.children { 
	padding: 0; 
	} 
 
.nocomments { 
	text-align: center; 
	margin: 0; 
	padding: 0; 
	} 
 
 
/* Comment Form */ 
#respond h4 { 
	clear: both; 
	margin: 45px 0 10px 0; 
	} 
 
form p { 
	padding-bottom: 10px; 
	margin: 5px 0; 
	} 
	 
form p label { 
	display: inline-block; 
	margin-right: 10px; 
	text-align: right; 
	width: 5em; 
	} 
	 
form p label[for="comment"] { 
	vertical-align: top; 
	} 
	 
input, textarea { 
	padding: 3px; 
	} 
	 
textarea { 
	font-family: Arial, Helvetica, sans-serif; 
	font-size: 0.9em; 
	overflow: auto; 
	} 
	 
input#submit { 
	padding: 5px 3px; 
	}

And the following into deepblue.css:

 
/* Comment List */ 
.commentlist li .avatar {  
	border: 2px solid #f2e9ea; 
	} 
 
.commentlist cite a:link, .commentlist cite a:visited { 
	color: #333; 
	} 
	 
.commentlist .commentmetadata a:link:first-child, .commentlist .commentmetadata a:visited:first-child { 
	color: #333; 
	} 
 
.thread-alt { 
	background-color: #F6F6EC; 
	} 
	 
.thread-even { 
	background-color: #F9F9F3; 
	} 
	 
.depth-1 { 
	border: 1px solid #E8E3C8; 
	} 
 
.even, .alt { 
	border-left: 1px solid #E8E3C8; 
	} 
 
input, textarea { 
	background-color: #F6F6EC; 
	border: 1px solid #E8E3C8; 
	} 
	 
input:focus, textarea:focus { 
	border: 1px solid #dad4b6; 
	}

Note that 2.7 automatically adds special class names like alt, even, children, bypostauthor, thread-alt, thread-even etc. where necessary to allow for styling every-other comment a different colour to increase readability in long threads. See the WordPress Codex for more information on this.

Inside header.php, directly before <?php wp_head(); ?> insert:

 
<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>

This loads a built-in Javascript file which can 'move' the comment form when you click a Reply link – thus the page doesn't need to reload:


Click 'Reply' and the form moves to your current position:


Comments for WP 2.6

I'm not going to explain any of this code, due to it being out-dated and is only being included for the few people not using WP 2.7. But I have added in a $counter so alternating comments can be styled differently, as they are in 2.7.

Create a new file named comments.legacy.php and copy in the following code. If you are using WP 2.6, the layout will be exactly the same as it is in 2.7 if threaded-comments is disabled.

 
<?php // Do not delete these lines 
	if ('comments.php' == basename($_SERVER['SCRIPT_FILENAME'])) 
	die ('Please do not load this page directly. Thanks!'); 
if (!empty($post->post_password)) { // if there's a password 
	if ($_COOKIE['wp-postpass_' . COOKIEHASH] != $post->post_password) {  // and it doesn't match the cookie 
	?> 
<p class="nocomments">This post is password protected. Enter the password to view comments.</p> 
<?php 
	return; 
	} 
	} 
?> 
<!-- You can start editing here. --> 
<?php if ($comments) : ?> 
	<h4 id="comment"><?php comments_number('No Comments', 'Comments (1)', 'Comments (%)' );?></h4> 
<ol class="commentlist"> 
<?php $counter = 0; 
foreach ($comments as $comment) : 
$counter++; ?> 
 
 
<li id="comment-<?php comment_ID(); ?>" class="comment <?php if($counter == 1) { ?>thread-even depth-1 <?php } else { ?>thread-alt depth-1 <?php $counter = 0; } ?>"> 
<div id="div-comment-<?php comment_ID(); ?>"> 
<div class="comment-author vcard"> 
	<?php echo get_avatar( $comment, 32 ); ?> 
	<cite class="fn"><?php comment_author_link() ?></cite> 
	<span class="says">says:</span> 
	</div> 
<div class="comment-meta commentmetadata"> 
	<a href="<?php the_permalink(); ?>#comment-<?php comment_ID(); ?>"><?php comment_date('F d, Y'); echo" at "; comment_date('g:i a'); ?></a> 
	<?php edit_comment_link('(Edit)','',''); ?> 
	</div> 
<?php comment_text(); 
if ($comment->comment_approved == '0') : 
	echo'<em>Your comment is awaiting moderation.</em>'; 
	endif; ?> 
</div>  
</li> 
	 
	 
<?php endforeach; /* end for each comment */ ?> 
</ol> 
<?php else : // this is displayed if there are no comments so far 
if ('open' == $post->comment_status) : ?> 
	<!-- If comments are open, but there are no comments. --> 
<?php else : // comments are closed ?> 
	<!-- If comments are closed. --> 
	<p>Comments are closed.</p> 
<?php endif; 
	endif; 
	?> 
 
<?php 
	/* COMMENT FORM */ 
	if ('open' == $post->comment_status) : ?> 
	<h4 class="comms">Leave a Reply</h4> 
<?php if ( get_option('comment_registration') && !$user_ID ) : ?> 
	<p>You must be <a href="<?php echo get_option('siteurl'); ?>/wp-login.php?redirect_to=<?php echo urlencode(get_permalink()); ?>">logged in</a> to post a comment.</p> 
	<?php else : ?> 
<form action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post" id="commentform"> 
<?php if ( $user_ID ) : ?> 
<p>Logged in as <a href="<?php echo get_option('siteurl'); ?>/wp-admin/profile.php"><?php echo $user_identity; ?></a>. <a href="<?php echo get_option('siteurl'); ?>/wp-login.php?action=logout" title="Log out of this account">Logout &raquo;</a></p> 
<?php else : ?> 
<p><label for="author">Name:</label> <input type="text" name="author" id="author" value="<?php echo $comment_author; ?>" size="22" tabindex="1" /></p> 
<p><label for="email">Email:</label> <input type="text" name="email" id="email" value="<?php echo $comment_author_email; ?>" size="22" tabindex="2" /></p> 
<p><label for="url">Website:</label> <input type="text" name="url" id="url" value="<?php echo $comment_author_url; ?>" size="22" tabindex="3" /></p> 
<?php endif; ?> 
<p><label for="comment">Comment:</label> <textarea name="comment" id="comment" cols="70" rows="10" tabindex="4"></textarea></p> 
<p><label for="submit">&nbsp;</label> <input name="submit" type="submit" id="submit" tabindex="5" value="Submit Comment" /> 
	<input type="hidden" name="comment_post_ID" value="<?php echo $id; ?>" /> 
</p> 
	<?php do_action('comment_form', $post->ID); ?> 
</form> 
<?php endif; // If registration required and not logged in ?> 
<?php endif; // if you delete this the sky will fall on your head ?>

Download the source files for this post if you want this file properly spaced & formatted to make it easier for editing.


Comments display in WP 2.6 or below

Note: If you would like to learn more about the old comments loop, check out "Unraveling the Secrets of WordPress' Comments.php File" by Gilles Maes.


The Archive

The archive.php template is used to display any sort of archive for the blog – eg. categories, months, tags etc.


Create a new file named archive.php and start with the following:

 
<?php 
if (have_posts()) : 
 
    $post = $posts[0]; // Hack. Set $post so that the_date() works. 
 
    /* Portfolio category? */ 
    if(is_category("$ts_portfolio_cat")) { 
        require(TEMPLATEPATH . '/page-portfolio.php'); 
        exit; 
    } else { 
        get_header(); 
        echo'<div id="mainarea">'; 
    } ?>

As you can see, if the archive is for the Portfolio category, the page will load the page-portfolio.php template. exit; ensures that no more code is executed after that line if the portfolio template is used.

Next:

 
<?php /* If this is a category archive */ if (is_category()) { ?> 
    <h4>Archive for the &#8216;<?php single_cat_title(); ?>&#8217; Category</h4> 
 
<?php /* If this is a tag archive */ } elseif( is_tag() ) { ?> 
    <h4>Posts Tagged &#8216;<?php single_tag_title(); ?>&#8217;</h4> 
 
<?php /* If this is a daily archive */ } elseif (is_day()) { ?> 
    <h4>Archive for <?php the_time('F jS, Y'); ?></h4> 
 
<?php /* If this is a monthly archive */ } elseif (is_month()) { ?> 
    <h4>Archive for <?php the_time('F, Y'); ?></h4> 
 
<?php /* If this is a yearly archive */ } elseif (is_year()) { ?> 
    <h4>Archive for <?php the_time('Y'); ?></h4> 
 
<?php /* If this is an author archive */ } elseif (is_author()) { ?> 
    <h4>Author Archive</h4> 
 
<?php /* If this is a paged archive */ } elseif (isset($_GET['paged']) && !empty($_GET['paged'])) { ?> 
    <h4>Blog Archives</h4> 
<?php 
} 
 
 
while (have_posts()) : the_post(); ?>

Each section will display a slightly different title depending on what the archive is for. For example, if it is an archive for the 'General' category the title would say "Archive for the ‘General’ Category"; or a monthly archive for January 2009 would be "Archive for January, 2008".

On the last line, the loop is opened.

Next:

 
<div id="post-<?php the_ID(); ?>" class="blogpost"> 
    <h2> 
        <a href="<?php the_permalink(); ?>" title="Continue Reading &quot;<?php the_title(); ?>&quot;"> 
        <?php the_title(); ?></a> 
    </h2> 
    <ul class="meta"> 
        <li><?php the_category(', ') ?></li> 
        <li><a href="<?php the_permalink() ?>#comments"> 
          <?php comments_number('No Comments', '1 Comment', '% Comments'); ?></a></li> 
        <li><?php the_time('F jS') ?></li> 
    </ul> 
    <?php the_content(); ?> 
</div> 
 
<?php     
endwhile;

The above code is the normal code for a blog post (as we've used several times on the blog page, single pages etc.) On the last line, the loop is closed.

If the archive doesn't exist:

 
# Archive doesn't exist: 
else : 
 
    get_header(); ?> 
    <div id="mainarea"> 
    <h2>Not Found</h2> 
    <?php include (TEMPLATEPATH . '/searchform.php'); 
 
endif; ?>

A simple 'Not Found' header is displayed, and the searchform.php file is included (which we'll create next). You should probably include more information on this page to make it more user-friendly.

Finally, include the pagination links, the sidebar & footer:

 
<div class="navigation"> 
    <div class="alignleft"><?php next_posts_link('&laquo; Older Entries') ?></div> 
    <div class="alignright"><?php previous_posts_link('Newer Entries &raquo;') ?></div> 
</div> 
 
<?php 
get_sidebar(); 
get_footer(); ?>

Now try out your archive page by clicking one of the links in a Category/Archives/Tags widgets. But if you go to an archive which doesn't exist (eg. a category ID which doesn't exist), you will be greeted with the following error page because the searchform.php file doesn't exist.


Search Form

This file really couldn't get any simpler – just a search form. Add the following into a new file named searchform.php:

 
<form method="get" id="searchform" action="<?php bloginfo('url'); ?>/"> 
    <label class="hidden" for="s"><?php _e('Search for:'); ?></label> 
    <input type="text" value="<?php the_search_query(); ?>" name="s" id="s" /> 
    <input type="submit" id="searchsubmit" value="Search" /> 
</form>

Try going to an inexistent archive again, and the search form will appear. It uses the same styling as we used on the sidebar search form so no additional styling is required.



Search Results page

The final template file required is search.php which displays the results from a search. For the most part, this is an ordinary loop, however since Portfolio items can show up in the results, they need to be displayed differently from blog posts.


Create the search.php file and start with the following:

 
<?php get_header(); ?> 
<div id="mainarea"> 
 
<?php if (have_posts()) : ?> 
 
    <h4>Search Results</h4> 
 
    <?php while (have_posts()) : the_post(); ?>

The loop is opened, and then is an if statement to display a portfolio item:

 
<?php 
// Portfolio 
if(in_category("$ts_portfolio_cat")) { 
 
    $preview = get_post_meta($post->ID, 'preview',true); 
    $date = get_post_meta($post->ID, 'date',true); 
    $client = get_post_meta($post->ID, 'client',true); 
    $link = get_post_meta($post->ID, 'link',true); ?> 
 
    <div class="work worksearch"> 
	 
        <?php if($preview) { ?> 
            <a href="<?php the_permalink(); ?>"><img src="<?php bloginfo('template_directory'); ?>/inc/thumb.php?src=<?php echo $preview; ?>&w=670&h=320&zc=1&q=100" alt="<?php the_title(); ?>" /> 
            </a> 
        <?php } ?> 
 
        <p> 
            <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>  
            <?php if($date) { echo"<span>($date)</span>"; } ?> 
        </p> 
 
    </div>

If the retrieved post is from the portfolio category, the post will be displayed using the layout above – to appear the same as the portfolio items. Otherwise…

 
<?php 
// Blog Post 
} else { ?> 
 
<div id="post-<?php the_ID(); ?>" class="blogpost"> 
    <h2> 
        <a href="<?php the_permalink(); ?>" title="Continue Reading &quot;<?php the_title(); ?>&quot;"> 
        <?php the_title(); ?></a> 
    </h2> 
    <ul class="meta"> 
        <li><?php the_category(', ') ?></li> 
        <li><a href="<?php the_permalink() ?>#comments"> 
          <?php comments_number('No Comments', '1 Comment', '% Comments'); ?></a></li> 
        <li><?php the_time('F jS') ?></li> 
    </ul> 
    <?php the_content(); ?> 
</div> 
 
<?php  
} 
   
endwhile; 
?>

The normal blog post layout is used.

Finally, an error message with a search form is displayed if no results were returned, the pagination tags are used and the sidebar & footer files included:

 
<?php 
# No posts found 
else : ?> 
 
    <h2>No posts found. Try a different search?</h2> 
    <?php include (TEMPLATEPATH . '/searchform.php'); 
 
endif; ?> 
 
<div class="navigation"> 
    <div class="alignleft"><?php next_posts_link('&laquo; Older Entries') ?></div> 
    <div class="alignright"><?php previous_posts_link('Newer Entries &raquo;') ?></div> 
</div> 
 
 
<?php 
get_sidebar(); 
get_footer(); ?>

Just add the following CSS into style.css and we're done here:

 
.worksearch { 
    clear: both; 
    margin: 0 0 45px 0; 
    padding: 0 0 40px 0; 
    width: 690px; 
}

Summary

Tomorrow is the final day of the series. (Part 6) We'll be ironing out some bugs with the devil (IE6), and finishing the series by creating the RedSpace colour scheme for Innovation!!

Advertisement