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

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:

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.';
<h4>The Comments Loop</h4>
The Comments Loop used to look similar to this (much simplified from a real one):
[php]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
<?php else :
if ('open' == $post-comment_status) :
	// If comments are open, but there are no comments.
else :
	// comments are closed

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(); ?>

<div class="navigation">
<div class="alignleft">< ?php previous_comments_link() ?></div>
<div class="alignright">< ?php next_comments_link() ?></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

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.


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) {
	if (!empty($c_email) && !empty($c_url)) {
		$microid = 'microid-mailto+http:sha1:' . sha1(sha1('mailto:'.$c_email).sha1($c_url));
		$classes[] = $microid;
	return $classes;

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. I noticed that when you hit reply it opens the comment form right below the post your are replying to. I do not think this is the default behavior, how did you accomplish this?

    1. That is the default behavior, if you add the wp_enqueue_script line to the header.php file, as I describe in the article above.

      1. It should not be rendered in the source. It’s a PHP function, it should cause a script tag to be rendered. Specifically, a script tag to wp-includes/js/comment-reply.js.

        If you’re seeing wp_enqueue_script in the source, then you need to wrap that stuff in PHP tags.

          1. Have you noticed any issues with it in 2.7? His page doesn’t say that it works in 2.7 officially although it seems fine judging by your site.

          2. The plugin works fine in 2.7. Most plugins that don’t do anything complicated tend to work in many versions of WordPress for a long time.

        1. Everything seems to work fine except for the following:

          php comment_form_title( ‘Leave a Comment’, ‘Leave a Reply to %s’ )

          For some reason all it ever says is “leave a comment” even though I am replying to a person and that reply is correctly nested upon submit. Any ideas? You’re the man, I dont know what people would be doing without this article.

          1. The “Leave a Reply” does not change when the comment-reply javascript is used. It only changes when that is broken and the page has to refresh in order to make the comment reply work. That way you know who you’re replying to even when the form can’t move directly underneath them dynamically.

        1. Because the javascript is looking for that cancel link in order to add code to it, and if it doesn’t find the link, then the javascript stops processing properly.

  2. Great article!

    I was stuck for most of two days, though, even though i was doing everything 100% exactly precisely as you laid it out… until i realized that threaded comments seems to be OFF by default. D’oh!

  3. Hi Otto!

    Unfortunatly the line

    add_filter(‘comments_template’, ‘legacy_comments’);

    in functions.php produces this error:

    Warning: Cannot modify header information – headers already sent by (output started at D:\serverweb\qumranblog\wp-content\themes\parrocchialagaccio\functions.php:8) in D:\serverweb\qumranblog\wp-includes\pluggable.php on line 850

    Any idea how to resolve it?

    1. You have some blank lines or something at the end of your functions.php file. According to that error message, line 8 produces output.

    1. Styling them is easy and I explain some of the built in classes that get added above. It’s really just a matter of adding styles to your theme’s style.css file. For example, to make my own comments blue here, I use this in my stylesheet:

      li.bypostauthor { background-color: #87cefa; }


    1. Actually, no, those two lines are correct and supposed to be there for my case. The corresponding “if” statements that they are ending are:
      if ('open' == $post-> comment_status) :
      if ( get_option('comment_registration') && !$user_ID )

      1. Sorry about the last comment. It didn’t come through correctly at all…

        The problem isn’t with the statements. its with the closing tags. The problem is that you put the last closing at the END of the file. It needs to be in the middle of the last two statements.

        Otherwise the does not correctly close out in certain situations. Nest your code and you’ll see what I mean…

      2. Okay so apparently I don’t know what I’m doing at all by putting the code tags in this site cause every time I do nothing comes up…

        I’ll try again. Basically the last closing div in your script needs to be in between the two endif statements. Otherwise validation errors are caused and the div id=response element is not closed out correctly in some situation.

        1. Ahh, I see what you’re getting at. Yes, that would be invalid, but only if I required comment_registration.

          Also, don’t use code tags to surround html code. Use pre tags. Code tags just make text fixed width, pre tags prevent html processing as well.

  4. I followed your instructions but I still have old fashioned not-threaded no-reply comments. I don’t know where I missed something.

    To start, I took the kubrick theme’s comments.php as a work-basis. I just translated it. And I add the little line in the header.php.

    It still doesn’t work !

  5. Is there a way to restrict who can reply? Say I want to allow registered users to reply to the post or to comments made by the post author – but not to comments made by other registered users. Would also need to allow the post author to reply to anything. I know, it’s a stretch.

  6. another thing for the wish list – an additional class added to a comment based on the type of wp registration such that we can style differently for administrators, authors, users, etc.

  7. 2.7 is awesome, if I understand correctly is gonna be easier to have different styles for comments posted by admin, authors, guest etc. right? If so is there a list of classes to look at or we can define them ourselves, or both?

    1. It is easier, and you can do both I think.

      The whole of the last section, under “Styling” covers exactly this.

  8. How I go about inserting this code:

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

    in my theme’s header.php file?

  9. Nice article, thanks. Any idea what’s going on with the comments_popup link? It doesn’t work anymore after upgrading from 2.2 to 2.7. Not just with my theme, but even the default Kubrick theme. Pressing the popup brings up a window that just loads the permalink to the specific post. Anybody figured this out, and how to fix it yet? Or is this a bug?

  10. I was having trouble getting the move comment form via Javascript to work and it turns out to be a problem related to HTML markup. You did mention about some of the requirements such as the required “respond” id but you forgot to add in some extra information about other markups that is used by the script, namely “div-comment-id”. I didn’t have a div container with that ID due to custom comment callback and it is causing the script to fail without giving any errors. By going through the JS source I am able to find out that div-comment-ID is part of the requirement for things to work. Without this div container the script will return prematurely without giving any errors, making it really hard to debug.

    To get the div container with comment id, one would add the following to their markup:

    <div id="div-comment-<?php comment_ID(); ?>">

    I suggest you put a section in your article listing all markup used by the Javascript as it will be helpful for other developers that opted for custom callback for comment listing.

    Other than that, thanks for writing such informative article. The problem I mentioned is the only one that is causing me headaches while following your article.

  11. Thanks for the tutorial. I seem to be hitting a snag and if you have any idea (although I certainly do not expect it), I would be grateful. I have inserted/replace all the necessary code and the comment box “moves” correctly if I hit ‘reply’ to a comment. However, there is no Cancel link showing up.

  12. hi im not good with javascript and i was wondering if you could help me use the style from my old 2.6 site.

    basically, it would be (author), on (date) said:

    (comment goes here)

    author should be bold and not italicized (unlike the default one).

    really would appreciate any help. thanks! kudos for the great work you’ve done with threaded comments btw.

  13. What happens with the MicroID for unverified users(commenters)? Section 5 of the MicroID spec says, “An Issuer MUST NOT generate a MicroID until it has verified that the Individual or Service Provider has control over a given EntityURI. Methods for such verification are out of scope for this specification and may vary according to local service policies and the URI scheme in question.” and it looks like this generates a MicroID for all comments. It appears that generating them for verified WP Users or OpenIDs would be ok, but generating them for anonymous, ie, unverfied users is not.

    1. That really doesn’t make any sense, because it makes MicroID’s more than a little pointless. But then again, the MicroID spec keeps changing anyway. I mean, it also says that a MicroID must be created by an Issuer, not an individual, and yet they have a generator on their own page: http://microid.org/ . Anyway, I’m not an “issuer”.

      MicroID has many possible purposes. If you know my email address and, say, my Digg profile page (back when Digg had MicroIDs on them), then you can verify that that is my account by computing the MicroID with that Profile URL and my email address and comparing the two.

      With the MicroID in a comment like this, you can verify what email address somebody used, in order to see if it’s the correct one for the given URL. Like if somebody put in a URL they don’t control, and a fake email address, then the MicroIDs would not match, and so it’s provably not from that person. On the other hand, this use of the MicroID does not verify authorship, since, like you say, you can use any email address. It’s just a simple way to disprove false authorship.

      1. I’m having trouble understanding that. Unless you verify control over the email address (the resource) that you’re computing a MicroID for, then this doesn’t seem prove or disprove anything other than it’s possible for anyone to put anyone’s email address into a comment and have a MicroID computed for it. It wouldn’t prove or disprove that the persons to whom that address and URL belongs had anything to do with the comment left behind somewhere. Rather than verified addresses making MicroID pointless, it seems they’re pointless without them.

        In the example you cite above, it would indeed prove that that is your Digg account, but only because the email address is known to have been verified by Digg when you created your Digg account. You clicked on the confirmation link, which validated your e-mail address. Any assertion based on that address associated with Digg is of a validated address. Someone who had control over that address opened that Digg account. If there were a MicroID on my comment here, it would it would only prove that if you run the same email address and the same URL through the same formula, that you’re going to get the same result every time. You’ve gained nothing over just having had my email and URL to begin with.

        But with all this having now been said, I don’t see a MicroID on my previous comment here, which suggests that you’re not issuing them for unverified users after all. I do see one on yours.

        Yours: <li class=”comment byuser comment-author-otto bypostauthor even depth-2 microid-mailto+http:sha1:23e9670868f6d2b16fe1f6b3db80c8f0423a082a” id=”comment-6619″>

        Mine: <li class=”comment odd alt thread-odd thread-alt depth-1 parent” id=”comment-6617″>

        I guess I have my answer now.

        So… anyway, I also want to thank you for explaining the enhancements in 2.7 as I’m trying to bring the old Benevolence theme forward.

        1. No Ron, you didn’t get a MicroID because you didn’t put in a URL for me to calculate one with. I am giving MicroID’s to every commenter here, or rather, computing the MicroID based on their own inputs.

          And it doesn’t *prove* anything, it *disproves* something.

          Let’s say somebody comes here and puts in your URL and name and some fake email address (perhaps because they don’t know yours). With the MicroID, you can see that, without seeing their email address, and prove that it wasn’t your email address used to make that comment. In other words, it lets you falsify the anonymous comment, if you know the email address of the real person who controls that URL.

          You’re correct, it proves nothing, but it can disprove a false comment if the commenter didn’t use the correct information.

          1. Ah yes. (I didn’t leave a URL). Not with you on there being any ability to disprove without anything proven to base it off of though. I can leave a comment somewhere with your email and your Digg URL, and if it generates a MicroID, it’s going to match, thus seemingly proving that you did leave the comment, since no effort was made to validate control. You can assert that you didn’t leave the comment, but there’s nothing of value upon which you can stake that claim.

            At the same time, I could leave a comment with valid information and disown it when in fact I made it, because again, there’s been no effort to validate the resource. Such MicroIDs have no credibility upon which they can disprove anything.

            If this is how MicroID is supposed to work, it’s completely worthless.

          2. No, you’re still missing the point. If you use my email and URL on some other site, then it doesn’t *prove* anything at all, whether MicroIDs are there or not. However, if you use my URL but some fake email, then that means that I can easily *disprove* that it was me. Get it? A legit MicroID proves nothing, because anybody can generate it, but an illegitimate one can disprove a thing.

            That’s how MicroID works for the unauthenticated side of things. But it does have other uses, of course, mostly like you claim, for cases where the email is verified. However, that’s of little or no use to blogs..

            The main reason I want them tied to comments is for search reasons. If there was a MicroID search engine, then every comment I leave on sites could be found by searching for that MicroID. I could tie my online activity together that way.

  14. Cool info on the comments file. Does this apply to the latest release of wordpress 2.7 too? Are there any other changes made since.

Comments are closed.