13 - Implementing Blog Comments

In this chapter we’ll implement comments on all of our single-entry blog content types.

 
Download the EE Code for 13 - Implementing Blog Comments

Comments - Friend or Foe?
Ah comments. So nice when they happen, and work well, and lead to better understanding or engaging conversations.  And yet, such a pain when they become a spam magnet or are abused by people who aren’t really your target audience. A few years ago comments were a no-brainer on any blog I implemented. These days the decision isn’t quite so obvious as commenting traffic seems down, spam has increased, and overall I just don’t feel like I get the same value from having comments on a site as I used to.

But we’re not here to think so grandly about such things, we are here to learn ExpressionEngine, right?  So I’ll leave you to decide if comments will be valuable on your projects and work you through the implementation of them on our Portfolio site.

Native or Disqus?
These days the main choice with comments is whether to stay all-native with EE or use the popular Disqus commenting tool. I’ve yet to use Disqus, but the functional advantages include easy implementation, users stay logged in across sites, threaded comments, image embedding, etc. The marketing materials on the Disqus site have some pretty impressive stories, from clients reporting higher engagement levels and lowered spam.

The disadvantages that appear to me are that Disqus requires Javascript to load, comments are stored offsite in a 3rd party database rather than along with the entry in our EE database (which raises ownership issues), and it seems like any search engine juice comments have for the entries they are associated with is minimized.

I haven’t used it, but there is a 3rd party add-on that appears to help mitigate those concerns. CX Disqus Comments syncs Discus comments to EE comments and provides a way to fallback to the EE comment form if Javascript isn’t enabled in the users browser.

That approach sounds more reasonable, but still would require that we implement native comments. Let’s work through that implementation and I’ll decide later if I also want to cover the Discus implementation.

Implementing ExpressionEngine Comments
There are 3 basic steps to implementing comments in ExpressionEngine:

  • Configuring the Channels
  • Implementing the Comment Submission Form
  • Displaying Already-Posted Comments

While those steps will get us to the point of allowing comments on a ExpressionEngine based site, we shouldn’t stop there. Comments will be a spam target, so we’ll also look at implementing some protection in that area.

Configuring the Channels
There are a number of comment-related settings in ExpressionEngine. The majority of these are set per-channel. In the Control Panel navigate to Admin > Channels, find your Blog-Audio channel and click the “Edit Preferences” link.  Scroll down a ways and you should find the Comment Posting Preferences.

For our purposes the defaults are all fine - the only one to mention is CAPTCHA. That might look like a good option to prevent spam, but we’re going to go a different route so I’ll leave that set to no. If you do choose some different options here just remember to apply those same choices to all your blogs so they behave consistently.

Implementing the Comment Submission Form
The basic task at hand here is to merge together the ExpressionEngine comment submission form tags with the HTML and CSS of the Folder template theme. You can grab some sample EE code from the docs to start with.

I chose to do some simplification by removing:

  • The logged in/out logic (since we won’t be taking member registrations)
  • The ‘Cancel Reply’ functionality (being convinced no one uses that really)
  • The EE ‘Remember Me?’ option
  • The ‘Notify Me’ option
  • The comment preview option
  • The list of acceptable HTML

Here’s my resulting form code:

<!-- Respond -->                
<
div id="respond">
<
h3 id="reply-title">Leave a Reply</h3>

{exp:comment:form form_id="commentform"}

<class="comment-notes">Your email address will not be publishedRequired fields are marked <span class="required">*</span></p>

<
class="comment-form-author">
<
label for="name">Name<span class="required">*</span></label
<
input type="text" name="name" value="{name}" size="30" aria-required="true"/>
</
p>

<
class="comment-form-email">
<
label for="email">Email<span class="required">*</span></label>
<
input type="text" name="email" value="{email}" size="30" aria-required="true" />
</
p>

<
class="comment-form-comment">
<
label for="comment">Comment</label>
<
textarea name="comment" cols="45" rows="8" aria-required="true">{comment}</textarea>
</
p>    



<
class="form-submit">
<
input type="submit" name="submit" id="submit" value="Post Comment" />
</
p>

{/exp:comment:form}
</div>
<!-- 
ENDS Respond --> 

You can see that I left in the EE variables for name and email address - this will only really work for the site owner since those variables will only return content if you are logged into EE. However it’s easy enough to do and should speed any replies by the site owner to commenters.

Displaying Already-Posted Comments
Much like how you merged together the Folder Template’s HTML & CSS with the EE tags above it’s that sort of process for implementing the already-posted comments.

Here’s the code I ended up with:

{exp:comment:entries sort="asc" limit="100"}

{if count
==1} {!-- This way we wont open the div or unordered list unless there are comments to display --}
    
<!-- comments list -->
    <
div id="comments-wrap">
    <
h4 class="heading">{total_results} Comments</h4>
    <
ol class="commentlist">
{/if}

<li class="comment even thread-even depth-1" id="li-comment-{count}">
<
div id="comment-{count}" class="comment-body cf">
<
img src="{exp:ce_gravatar:single email="{email}"}" alt="{name}" class='avatar avatar-35 photo' height='35' width='35'/>
<
div class="comment-author vcard">{name}</div>
<
div class="comment-meta commentmetadata">
    <
span class="comment-date">{comment_date format="%Y %m %d"}</span>
</
div>
<
div class="comment-inner">
    
{comment}
</div>
</
div>
</
li>

{if count==total_results} {!--close the list after the last comment --}
    
</ol>
    </
div>
{/if}

{
/exp:comment:entries}
<!-- ENDS comments list --> 

As you can see I again chose to simplify a few things:

  • Nested comments - the Folder template provided for these but EE doesn’t do them natively so I just used the top-level HTML/CSS.
  • Date formatting - I choose to just display the date of the comment rather than the “6 days ago” text.

No Comments? No HTML!
Our site should also handle the case for when there are no comments gracefully. I did this by using the two conditionals at the top and bottom of the comment:entries loop. Basically if there are no comments to display neither the containing div or ordered list will be returned.  This will keep the spacing night and tight with the comment submission form and the content entry.

Refactoring the Parent Templates
I mentioned in the previous chapter that there might be an issue trying to run the comment:entries loop within the channel:entries loop that returns the parent content entry.  That did indeed prove to be an issue and I had to slightly refactor each of the blog single-entry templates.

What I saw was that the conditionals using the count and total_results variables in my comment:entries loops weren’t working, because those variables also exist in the channel:entries loop that the comment:entries were being nested into.  Without un-nesting the two loops the conditionals would not work as EE gets confused over what is being counted - entries or comments.

The blog/image template, for example, was:

{exp:channel:entries channel="blog_image" limit="1" disable="pagination"}    

    {embed
="embeds/header" my_body_class="single" my_active_nav="blog" my_page_title="Blog | Image | {title}"}                
    
<!-- masthead -->
    <
div class="masthead cf">
        
A Blog Image
    
</div>
    <!-- 
ENDS masthead -->    

    <!-- 
posts list -->
    <
div id="posts-list" class="cf">            
        
{snp_blog_image}                
        {snp_blog_comments}
    
</div>
    <!-- 
ENDS posts list -->

    
{snp_blog_sidebar}
    {snp_footer}
{
/exp:channel:entries} 

And I had to change it to be:

{exp:channel:entries channel="blog_image" limit="1" disable="pagination"}    

    {embed
="embeds/header" my_body_class="single" my_active_nav="blog" my_page_title="Blog | Image | {title}"}                

    
<!-- masthead -->
    <
div class="masthead cf">
        
A Blog Image
    
</div>
    <!-- 
ENDS masthead -->    

    <!-- 
posts list -->
    <
div id="posts-list" class="cf">            
        
{snp_blog_image}    

{
/exp:channel:entries}

        {snp_blog_comments}

    
</div>

    <!-- 
ENDS posts list -->

    
{snp_blog_sidebar}
    {snp_footer} 

You can see I just had to move the closing channel:entries tag up so that it ends its processing before we try to display comments using snp_blog_comments.  This change needs to happen in all of the blog single entry templates that use the snp_blog_comments Snippet (I’ll include refactored templates in the Companion Files).

Gravatars
The Folder Template’s design for displaying comments includes a Globally Recognized Avatar - or gravatar.  Basically EE needs to take the commenters email address and check the Gravatar service to see if the user has created a Gravatar.  The nice thing is that there are several 3rd party add-ons available do this. I chose CE Gravatar from CausingEffect. It’s free and available for download from the CE website.

Download CE Gravatar and follow the installation instructions (make sure to note that you need to create the folder that the plugin needs within the expressionengine/third_party folder, most add-ons give you this folder and you just drag it in).

The template code was drop-dead simple and again it’s a mix of the code EE (and the add-on) needs and the CSS hooks that the design wants:

<img src="{exp:ce_gravatar:single email="{email}"}" alt="{name}" class='avatar avatar-35 photo' height='35' width='35'/> 

With that in place you should have users’ Gravatars getting pulled in, or a default Gravatar icon if not.

Spam Prevention
With the fun and interesting parts of our comments done we can now tackle the drudgery - spam prevention. EE has some preventative measures in its native featureset, and those are covered in the EE documentation.

Unfortunately I can speak from experience and say that EE’s native spam prevention features are not enough. I’m going to recommend the use of two 3rd party add-ons instead:

  • Hon-ee Pot Captcha by Trevor Davis
    Hone-ee Pot adds a field to the comment form that is invisible to the human eye. If it gets filled in (assuming the comment form is being filled in by a bot) the comment is not allowed through.
  • Low No Spam by Lodewijk Schutte
    Low’s No Spam analyizes the comments that make it through the Hon-ee Pot, and uses Akismet or Typepad’s anti-spam to flag spam comments. It removes the comment from the ExpressionEngine notification system (so you don’t get emails about spam comments) and gathers them all up in one interface where you can easily delete them and add the IP addresses to EE’s blacklist.

Hon-ee Pot
I downloaded and installed per the directions on Devot-ee - the only change I made was applying the custom CSS to the parent paragraph rather than a list item per the Folder front-end code. I also changed the default field name just as an added measure of security

Here is the code I added to the Comment Submission Form:

<class="screen-reader">
<
label for="portfolio-honeepot">Dont put anything here</label>
<
input type="text" name="portfolio-honeepot" id="portfolio-honeepot" />                    
</
p

I added the sample CSS from the Devot-ee page to the end of the Folder Templates style.css (I’ll include the updated CSS with the Companion Files for this chapter).

Low No Spam
Since there is no template code changes required to use Low’s No Spam extension I’ll leave you to follow his excellent documentation to install the add-on and get it configured.

All That Remains
With the changes covered in this chapter you should have a working comment system on all of your blog content types, and you should be relatively protected against spam.

You’d think we were finally done with the blog, right? Alas, no, there is one more thing to cover. The Template design indicates that each post should also be able to be tagged.  While EE doesn’t have a native tagging ability there are a few add-ons out there that will let us add tagging to each individual post. We’ll cover that in the next chapter.

 

Category Navigation

<< Previous Entry   

Next Entry >>

 

Previous Comments

Picture of Rick

by Rick

Date: Wednesday, January 23rd, 2013
Comment: #1

A couple of things that I hope you get a chance to cover:

1. The date format for the sidebar Archives is different on the index page versus the single-entry page. Can’t figure out why.

2. If I wanted to do a “recent comments” in the sidebar how would that work with each comment potentially coming from a different channel? I tried using the sample code from EE and it did not work.

3. Instead of, or in addition to the tags you plan to cover, how about a way to list the categories a post belongs to?

I’ve learned a huge amount from this tutorial and really appreciate the time and effort you’ve put into this.

Picture of Boyink

by Boyink

Date: Wednesday, January 23rd, 2013
Comment: #2

Hi Rick -

Thanks for the comment. I’m in the midst of a revision to my book so this has taken a back seat for a bit. On #3 check out the {categories}{/categories} looping pair for the channel entries tag. I didn’t use it because the sample HTML didn’t have a need for it.

Picture of Rick

by Rick

Date: Thursday, January 24th, 2013
Comment: #3

#3: that worked great, thanks

Picture of machine vision lenses

by machine vision lenses

Date: Wednesday, July 29th, 2015
Comment: #4

Thanks for this article.I like this post.

Add Your Comment

Commenting is not available in this channel entry.

Unless otherwise stated all content is © Michael Boyink of Train-ee.com & Boyink Interactive. Please don't steal - I've got kids to feed...

dy>