WordPress 2.7 Comments Enhancements

This post has been moved here: http://ottopress.com/2008/wordpress-2-7-comments-enhancements/

WordPress 2.7 includes a lot of new enhancements, but one of the big ones is the new comment functionality. Comments can be threaded, paged, etc. This is all built in, but unfortunately, your theme must support it. So, for theme authors, I’d suggest getting to work on making your themes compatible right away.

Read on if you’re a theme author…

Note: A lot of people seem to miss this key bit: Enhanced Comments are optional and default to off, even after you make these changes. You have to go to the Settings->Discussion panel to turn the features on.

Actually “compatible” is not quite the right term. Old themes will continue to work fine in WordPress 2.7. It’s just the threading and paging and javascript enhancements need the theme to support it. This is much the same as the sidebar widgets, the theme has to support it for the functionality to work. So this article is really not about 2.7 compatibility, it’s about 2.7 capability.

Note that this article will explain some of the changes needed to make themes capable of supporting the new comments functions, however there’s no substitute for the real thing. Install a local copy of WordPress trunk on your home machine (possibly using XAMPP) and test it there.

Also note that this is all based on the current state of WordPress trunk, and is subject to change before WordPress 2.7 is released. However, it’s probably not going to change all that much at this point.

How to create a 2.7 compatible comments.php

2.7 Detection

If you want your theme to be backward compatible as well, then there’s a simple way to do it. Just check for the wp_list_comments function, like so:

if (function_exists('wp_list_comments')) :
// new comments.php stuff
else :
// old comments.php stuff
endif;

While you could check for the version number of WordPress, this method is better because it simply looks for the actual function you’re going to use anyway. Never make assumptions based on version number.

One of the more interesting ways I’ve seen to use this is to have the “old comments” php in a separate file entirely, which is then included. This preserves backwards compatibility for your theme in a simple way. Here’s a quick example code for that approach:

<?php
add_filter('comments_template', 'legacy_comments');
function legacy_comments($file) {
	if ( !function_exists('wp_list_comments') )
		$file = TEMPLATEPATH . '/legacy.comments.php';
	return $file;
}
?>

Adding this code to a theme’s functions.php file will make the theme use the “legacy.comments.php” for older non-2.7 installations. That way, you can simply rename your old comments.php and then make a new one based on the new functionality. Clever.

Password Protection Check

Put this code at the top of your comments.php file. This is what lets it support the post password functionality. Note that this code is quite similar to the previous way that it was done (by checking the cookie directly), but now WordPress has a specific function to do it. You should use this function in case the functionality changes in the future, your code will be forward compatible:

if (!empty($_SERVER['SCRIPT_FILENAME']) && 'comments.php' == basename($_SERVER['SCRIPT_FILENAME']))
	die ('Please do not load this page directly. Thanks!');
if ( post_password_required() ) {
	echo 'This post is password protected. Enter the password to view comments.';
	return;
}
<h4>The Comments Loop</h4>
The Comments Loop used to look similar to this (much simplified from a real one):
if ($comments) :
<?php $comment_count = get_comment_count($post->ID); echo $comment_count['approved']; ?> Comments
<ul class="commentlist">
<?php foreach( $comments as $comment ) :
// stuff to display the comment in an LI here
endforeach;
?></ul>
<?php else :
if ('open' == $post-comment_status) :
	// If comments are open, but there are no comments.
else :
	// comments are closed
endif;
endif;

Basically, it went through the comments manually and output all the necessary pieces. Easy, but very manual. This also had the problem of being very inconsistent and hard to manage for your theme’s users, especially if you heavily customized it.

The new comments loop is much simpler:

<?php if ( have_comments() ) : ?>
<h4 id="comments"><?php comments_number('No Comments', 'One Comment', '% Comments' );?></h4>

<ul class="commentlist">
<?php wp_list_comments(); ?>
</ul>

<div class="navigation">
<div class="alignleft">< ?php previous_comments_link() ?></div>
<div class="alignright">< ?php next_comments_link() ?></div>
</div>
<?php else : // this is displayed if there are no comments so far ?>
	<?php if ('open' == $post->comment_status) :
		// If comments are open, but there are no comments.
	else : // comments are closed
	endif;
endif;
?>

That new one is, in fact, a complete comments loop. No simplification at all. Unless you want something displayed for “no comments” or “comments closed”, of course. I don’t have anything showing there.

There are three important pieces to note here:

  • The have_comments() function replaces the check on the global $comments variable.
  • The wp_list_comments() function now outputs all the comments. It does threading, the classes, everything new.
  • There’s a new navigation section to do comment paging.

The Power of Javascript

To support the new Javascript functionality with comment threading, some minor bits of code are needed:

First, in the header.php, add this line immediately before the call to wp_head():

if ( is_singular() ) wp_enqueue_script( 'comment-reply' );

That code adds the comment-reply javascript to the single post pages, letting the comment reply links work correctly. WordPress specifically does NOT do this itself, for the reason that use of this script requires certain naming conventions and parameters in the comment form, which you’ll have to add.

So, your comment form has a new parameter that you have to add:

<?php comment_id_fields(); ?>

This adds a bit of code to your form which makes it display two hidden inputs: comment_post_ID and comment_parent. Your form probably had the comment_post_ID before, so you need to remove it. The comment_parent is there for the javascript, so that replies to comments get threaded properly.

Also, your comment textarea MUST have an id=”comment”. The javascript expects it for focus purposes. If you used anything else, change it. Note that because of this, no other element on your page can have the “comment” ID.

Finally, the entire comment form MUST be surrounded by a DIV with an id=”respond”. In some previous themes (including the default ones), there would be an anchor tag like this:

<a id="respond"></a>

This was there to allow the link from the front page to go directly to the respond section when there were no comments already. That still happens, but now there’s a double purpose. The javascript moves the comment form to where the reply link is, so instead of it being an anchor, it needs to be a DIV that surrounds the comment form.

So, remove that anchor, and add a DIV with an id=”respond” around the entire comment form. The link from the front page still works this way with all modern browsers, and the javascript can now move the form around on the page as needed.

Next, you can replace the call to your normal “Leave a Comment” text with something like this:

<h3><?php comment_form_title(); ?></h3>

This makes a comment form title of “Leave a Reply” which will change to “Leave a Reply to Whoever” when somebody is replying directly to another person. You can customize this, if you like, with two parameters, like so:

<?php comment_form_title( 'Leave a Reply', 'Leave a Reply to %s' ); ?>

The %s will be replaced with the person’s name. This will only happen when the javascript isn’t working and the reply links have to cause a page refresh. So it’s usually not worth customizing much. Still, not everybody runs javascript and so this is nice to let them know who they are replying to.

Finally, you’ll notice that when somebody clicks “reply” and the comment form appears there, maybe they decide to cancel instead. So, that cancel link needs to be in your respond section. Here’s the code to do that, just put it right below your “leave a message” header in the comment form area:

<div id="cancel-comment-reply">
	<small><?php cancel_comment_reply_link() ?></small></div>

That’s pretty much it for making the AJAX work. With this, the new features on the Settings->Discussion panel will work. Obviously, you can modify this somewhat as needed for your theme, these are just general principles that you’ll need to use.

Styling

Now that you have it working, there’s plenty of new styling you can add to comments. The new comments loop automatically puts every comment into an LI tag, and threads them as well, with embedded UL/LI tags. It also adds a ton of classes on all these LIs which surround every comment in this fashion:

  • comment, trackback, pingback classes get added depending on the type of the comment.
  • byuser gets added if the comment is by a registered user of the site.
  • comment-author-authorname gets added for specific registered users.
  • bypostauthor gets added if the comment is by the author of the post the comment is attached to.
  • odd and even classes are added to odd and even numbered comments
  • alt is added to every other comment
  • thread-odd, thread-even, and thread-alt classes are the same as the odd/even/alt classes, but these only apply to the top level of each set of comments and replies
  • depth-1 is added to the top level comments, depth-2 to the next level, and so on.

What’s more, a comment_class filter is provided to allow you to add your own classes. Here’s an example of that. This example function adds a microid to every comment with the microid for the comment authors given URL and email address. This sort of thing could be done in a plugin or a theme’s functions.php file, whatever.

// add a microid to all the comments
function comment_add_microid($classes) {
	$c_email=get_comment_author_email();
	$c_url=get_comment_author_url();
	if (!empty($c_email) && !empty($c_url)) {
		$microid = 'microid-mailto+http:sha1:' . sha1(sha1('mailto:'.$c_email).sha1($c_url));
		$classes[] = $microid;
	}
	return $classes;
}
add_filter('comment_class','comment_add_microid');

Simple and effective. It just adds the class to the given array of classes and lets the comment display functions take care of the rest.

And there you have it. It’s not hard to support the new functions. And if you need to customize your theme’s comments section even more, wp_list_comments() supports a number of parameters. Most of this is not documented yet, because WordPress 2.7 is not out until November. However, the code is relatively straightforward, and anybody with a good understanding of WordPress should be able to work it out.

Additional: A lot of people keep asking me for a full-fledged example. Really, I recommend that you examine the comments.php file in the default theme in the 2.7 beta versions. However, the actual comments.php file I’m using on this site can be found here: http://ottodestruct.com/comments.phps, if it helps you any. It has the code I’ve described in this article, pretty much verbatim. The only additions to it are a couple of extra options on the wp_list_comments() call, such as avatar_size and reply_text.

403 thoughts on “WordPress 2.7 Comments Enhancements”

  1. To be honest, it WAS much simpler to take your code and copy & paste it in. :P But thank you SO MUCH for this tutorial. I did START just following along… ;P

    (Btw, it might be more clear to provided the entire “< ?php…” part in that header “if_singular” line b/c I’m a dummy and just put that line in by itself, so it printed at the top of my site!)

    Thank you thank you thank you!

    1. WordPress does not have email reply functionality yet, but there are plugins that can do that sort of thing.

      The 2.7 update only adds threading and paging of comments.

  2. Thanks a ton I finally did it after a lot of struggle thanks to your site! Could u pls provide some info on how to get the indentation and shading of the nested comments… also wondering if there’s any way we could add a subject to the first comment.

  3. Thanks for the tutorial, especially with all the ajax-ifying. I’m so close I can taste it, but I have a question about styling. I set my css to give post author comments a different background and now any comment that replies to mine inherits my background. I notice that you aren’t having that problem, but I can’t figure out why!

    Best example here. Wendy’s reply at 12:39pm should revert to the original background color, but instead it’s kept mine.

    If it helps, I totally stole the comments section of the default theme. My css here.

    Help?

    1. The basic problem is that you’re not resetting the background color. Children inherit traits from their parents. In your case, the comment below yours is inheriting the .bypostauthor traits.

      You may want to set a background-color default, to something like “li.bypostauthor li”, in order to reset the background color.

      1. That did it! I had to specify a little more because of the alternating color, but once I did that for “.thread-alt li.bypostauthor li” AND “.thread-even li.bypostauthor li” everything fell into place.

        Thanks a bunch!

        1. Instead of using alt and even, I’d say to use odd and even. The alt is the same as odd, currently, but with various page and comment numbering settings, that could change. Whereas odd and even will always alternate between each other.

  4. Hi Otto,

    Just a quick note to add, if you can stand some shameless plugin plugging…

    If you’d like to display a number along each comment, to help readers identify at a glance which way the discussion is going, a new plugin can provide accurate numbering regardless of whether you’re displaying comments in ascending or descending date order, with or without paging, and with or without threading:

    Greg’s Threaded Comment Numbering

    All the best,
    Greg

  5. Thanks a lot for this great tutorial!

    I’ve successfully edited the WP theme I was using to support the new 2.7+ comment features! :)

    regards from Argentina!
    Buk

  6. Hi Otto. First of all I have to say this tutorial save my life. Just use your code, tweaked it a little bit and works grate.

    However, I’m having a little problem. You can see it Here: http://img24.imageshack.us/img24/9431/picture1nd6.png

    What happens is that when I try to respond to the last comment, the reply doesn’t nest!…I can reply all comments before the last one… don’t know what I’m doing wrong.

    Checked and ReChecked the code, but can’t find the answer. Do you have any idea what can it be? I’m sure missing something.

  7. Otto: Thanks for this tutorial. It’s really helpful.

    I have one question that I was hoping you would be able to answer. I’ve got threading up and running, but I’m running up against a problem with the final level of nesting. There’s no sign of the “Comments won’t nest below this level” language or the “Reply here” link that should drop a comment in at the same level but below the most recent one. Example is here: http://test.secondavenuesagas.com/2007/11/15/new-aside-post-on-top/comment-page-1/#comment-30

    Anything you know about working around that problem?

    1. I’m not sure what you mean, exactly. There is no “comments won’t nest” language at the last level. The only difference on the bottom level is that the Reply link stops showing up. That’s it. Nothing more happens.

      If somebody wants to respond on the same level as a comment, then they need to reply to the parent comment. Examine how the Digg commenting system works. Same principle.

    2. I’m wondering if there was ever a solution found for Ben’s questions re: adding “comments won’t nest below this level” text on the final comment allowed in a nested sequence.

      1. No, there isn’t. When the max depth is exceeded, get_comment_reply_link doesn’t return anything. You’d have to modify the get_comment_reply_link() function in comment-template.php.

  8. hi Otto,

    Good article. I’ve done what you explain for my WP2.7 comments but I have one problem. How can I stop the URL changing from:

    http://www.myblog.com/content/the-post-name

    to:

    http://www.myblog.com/content/the-post-name/comment-page-1#comment-305

    Everytime a new comment is added? This messes up my post page in the theme and requires the user to have to go back to the original post page. This did not happen prior to the upgrade to WP2.7

    Thx for any help you can give with this.

    1. The “comment-page-1” is the result of enabling comment paging. If you were to go to an earlier page of comments on my site (click the Older Comments link), then you’d see that as well. That’s not a bug, that’s how it goes between pages.

      If you don’t want paging, turn it off.

      1. Hi Otto,

        Thx for the reply

        I did do as you said, now I have the URL, similar to yours i.e.

        http://www.myblog.com/content/#comment-305

        But the problem still is there. I get this page which is not the full post – only the title, odd side bar and the comments for the article, including the new one added.

        Any ideas why this is happening?

        1. I tried it with the default WordPress 2.7 theme, it happens in the default theme too! So, now I’m confused. Is this a WordPress 2.7 problem? Should I re-upgrade?

          1. Hi Otto,

            Found out the problem – It was the Search Unleashed plugin. So, not the theme or config of WordPress 2.7!

            Thx for your help.

  9. Otto, you are the man and the only reason I ever got this whole system to work in the first place. That being said, I just autoupgraded to 2.7.1 and now my comments no longer honor the pagination breaks. The older comments have the “older comments” link but the newer ones are well over the 30 comment break and they are not paginating. Any ideas why upgrading to 2.7.1 might change the behavior?

    When you specify a break for paginating… does it count only first level comments or all comments regardless of level?

    1. Only top level comments count for pagination. And a few bugs regarding this were fixed in 2.7.1, so yes, you may notice the behavior changes somewhat.

  10. Hi. All I want to be able to do is using the function “reply” in comments and get the threaded comments capability but i dont know how to, and this is all greek to me.. someone please help me?

  11. hey man! maybe you could gimme a hand! is there anyway to mess around with comments layout? on the older “foreach”, it was easy. if you check out this link -> http://migre.me/2Ed <- you’ll see a couple comments, already using the wp_list_comments() and nesting working. BUT… you can see, for example, the number 2. completely out of place. Messing around with firebug I found out that a div style=”clear:both;” would do the trick… but I have no idea where to do it!

    1. Why not find the appropriate section to clear and then add the clear:both rule to your stylesheet instead? No need to add any HTML markup.

      Where at in the comments are you adding this div with a clear:both style, anyway?

  12. I may have missed some part of it, but I’d like to know where I can edit the comment layout… like, I’m having some problems on some posts and comments. one example I have is this: http://www.proveisso.net/2008/08/filme-vegas

    the second comment is appearing on the side of my reply to the first comment, and not below. a simples div style=clear:both I guess could take care of it, but I don’t know where to put it =( any ideas?

  13. Thank you very much je te remercie du fond du coeur pour ce tutoriel !
    Bonne continuation, good luck ;)

  14. I like the code generated by default by 2.7’s commenting system, with one exception. It adds ‘your comment is awaiting moderation’ in a bare tag, so if you want to style it you need to style all em tags in all comments, which I don’t want to do… Is there an easy way to just add a class to this one tag without re-writing the entire comment template from scratch?

    any help is GREATLY appreciated

    brent
    @
    mimoYmima.com

    1. it stripped a little of my html in the comment above, I was trying to say it puts that moderation text in a bare -em- tag.

      brent
      @
      mimoymima.com

      1. Use CSS Selectors to only reference the direct descendant em tag, instead of all child em tags.

        Example:
        [div class=comment]
        [em] Awaiting moderation [/em]
        [p class=comment-text]
        Comment text here [em] Highlighted text [/em]
        [/p]
        [/div]

        CSS to reference the first em without touching the second one would look like this:

        .comment > em { color:red; }

        The greater than mark makes it only refer to direct children instead of all children. CSS Selectors are super-powerful, but very few people use them, for some reason.

        1. I’ve used this before on stuff that enhanced the look of my page but not with things that were breaking the layout because I’m pretty sure it doesn’t work with ie6… I’ll have to run a test to be sure. The easier fix would be for the WP team to just throw a class on it. Seeing as some of the other divs in the comments code have something like 5 classes strung together I’d have thought this would have had at least one…

          brent
          @
          mimoymima.com

          1. Honestly, I’m dropping IE6 compatibility in my day-to-day work. With IE8 out already, and IE 7 having been around for yonks, there’s no reason to maintain compatibility with a browser that doesn’t even fully support CSS.

          2. I wish I could just disregard it, unfortunately I think there are still a good amount of clueless people out there using it :( … it’s tempting though

          3. I just noticed that your comments are showing up without me refreshing the page (I think) do you have a plugin to make that work or is that standard in wp2.7?

    1. Just use the built in styles. Look above where I talk about odd, even, alt, and thread-odd, thread-even, and thread-alt. All you have to do is assign the styles you want to those classes in your CSS file.

    2. never mind – i figured it out. wow, they really revamped this comments stuff in 2.7! it even applies a lot more css classes in the comments themselves – no additional foreach statements to style alternating comments. thanks anyway

  15. Pingback: Blog Redesign

Comments are closed.