Threaded Comments
Threaded Comments module for ExpressionEngine 2.x enables comment threads (nested comments, comment on comment) for channel entries.
You can have unlimited depth of comment nesting.
The module will respect all commentings settings you have. It contains all extension hooks that first-party comments module does, so all of your extensions will work.
The syntax of the module has some differences from EE Comment module. Make sure you read these docs.
Installation
Place the directory contained in zip into your /system/expressionengine/third_party directory. Then go to 'Modules' section in your Control Panel and perform installation.
Comment submission form
The form is almost like the form for comments module with some little additions (parent_id, notify_thread)
{exp:threaded_comments:form entry_id="{segment_3}" parent_id="{segment_4}"}
{if error}
<span style="color: red;">{error_text}</span>
{if:else}
{if logged_out}
<p class="input">
<label for="comment-author">Name</label>
<input type="text" name="name" id="comment-author" value="Name" size="22" tabindex="1" />
<small>Required</small>
</p>
<p class="input">
<label for="comment-email">E-mail</label>
<input type="text" name="email" id="comment-email" value="Email" size="22" tabindex="2" />
<small>Required</small>
</p>
{/if}
<p>
<textarea name="comment" id="comment-comment" cols="22" rows="5" tabindex="4">Message</textarea>
</p>
{if logged_out}
<p class="input">
<label for="save_info"><input type="checkbox" id="save_info" name="save_info" value="yes" {save_info} /> Remember my personal information</label>
</p>
{/if}
<p class="input">
<label for="notify_me"><input type="checkbox" id="notify_me" name="notify_me" value="yes" {notify_me} /> Notify me of follow-up comments?</label>
</p>
{if captcha}{captcha}{/if}
<div class="comment-notify-submit">
<p class="comment-submit"><button type="submit" name="submit" value="submit" id="comment-submit" tabindex="5" ><span>Submit</span></button></p>
<div class="clear"></div>
</div>
{/if}
{/exp:threaded_comments:form}
Parameters
- entry_id - entry_id of the channel entry to be commented. If omited, will use url_title parameter or will try to guess entry_id from URL
- url_title - url_title of the channel entry to be commented. If omited, will try to guess entry_id from URL
- channel - in addition to url_title, you can specify channel name
- return - template or URL to return after comment has been submitted. Defaults to current page.
While entry_id and url_title are optional, it is strongly recommended that you use them and don't rely on url auto-guessing.
Form fields
- name
- location
- url
- comment
- captcha
- save_info - remember commenter's info
- notify_me - notify about new comments
- notify_thread - notify about new replies to this comment
- parent_id - hidden field to hold ID of parent comment
It is recommended that you change the value of parent_id dynamically with JavaScript instead of creating new form for each comment reply link.
NOTE: Notification about new replies to comments will work only if you do not moderate your comments. If you have set them to moderate first, the users will receieve notification about all new comments.
Conditional variables
- if logged_in - check whether user is logged in
- if logged_out - check whether user is logged out
- if error - check if there are any errors to display (like expired commenting)
- if captcha - check whether CAPTCHA should be displayed and checked
Single variables
- error_text - error message (if any)
- captcha - CAPTCHA image
Displaying the comments
The syntax of exp:threaded_comments:display tag is a bit different from what you are used to, so make sure you get familiar with it.
{exp:threaded_comments:display entry_id="6"}
<ul>
{comments}
{thread_start}
<ul>
{/thread_start}
<li>
{comment_id}--{comment}--{url_as_author}--{comment_date format="%Y-%m-%d"}
</li>
{thread_end}
</ul>
{/thread_end}
{/comments}
</ul>
{/exp:threaded_comments:display}
See also the alternative nesting method below.
General usage concepts
Any content within exp:threaded_comments:display tag pair will be displayed only once (except what is wrapped into tag pairs, see below). This is the main difference with comments module.
Pagination will be displayed in the place where you insert the tags. If you need it twice, place the tags twice. You don't need {paginate} tag pair.
The 'per page' (limit) parameter for pagination describes how many zero-level comment per page you want to have (i.e. comments that are replies to entry, not to other comments). So the actual number of comments displayed on page can be different. The comments thread will never be splitted between pages.
The actual comments loop should be surrounded with {comments} tag pair. It's content will be repeated as many times as many comments you have. You can add CSS classes to comments of different level by making use of {level} variable. You can also check for its value to use totally different HTML for comments of different level.
To add HTML wrapping when the thread is going a level deeper, use {thread_start} and {thread_end} tag pairs. They should be placed within {comments} loop. All variables available within {comments} are for your use there as well.
Parameters
- entry_id - entry_id of the channel entry to be commented. If omited, will use url_title parameter or will try to guess entry_id from URL
- entry_status - display comments only if entry has certain status (by default displays comments to all entries, even closed)
- url_title - url_title of the channel entry to be commented. If omited, will try to guess entry_id from URL
- channel - in addition to url_title, you can specify channel name
- limit - number of zero-level comments to display per page
- paginate_base - template to use as 'base' for building pagination links (defaults to current page)
- orderby - field to order by. Possible values: 'date' (default), 'email', 'location', 'name', 'url'.
- sort - sorting direction. Possible values: 'asc' (default), 'desc'
Global conditionals
- if logged_in - check whether user is logged in
- if logged_out - check whether user is logged out
- if no_results - no comments to display
- if pagination - check whether there is need to display pagination links
- if next_page - check whether there is next page
- if previous_page - check whether there is previous page
Pagination
- pagination_links - display auto-built pagination links
- current_page - the number of current page
- total_pages - total pages
- prev_link - link to previous page
- next_link - link to next page
- if previous_page - check whether there is previous page
Variables
Most of variables available in first-party Comment module are available in Threaded Comments as well.
Additionally (outside of {comments} loop) you have:
- total_threads - the total number of threads (or root comments)
Comments loop
Surrounded with {comments} tag pair
Conditional variables
- if signature_image
- if avatar
- if photo
- if is_ignored
- if has_replies
Single variables
All variables available within first-party comments module (including date variables) are available here as well. Check out
Additionally available:
- count_root - same as {count}, but counts only root comments
- absolute_count_root - same as {absolute_count}, but counts only root comments
Nesting comments
To nest comments, you'll need to use {thread_start} and {thread_end} tag pairs. Anything within those tag pairs will be displayed in the beginning and in the end of 'branch', respectively.
Note that the thread_start & thread_end content will not be wrapped around top-level comments.
Additionally, you can make use of these special variables:
- level - the nestedness level. Comments that are replies to entry have 0 level, replies to them level 1 etc.
- parent_id - id of comment to which this one is reply
- root_id - the id of 0-level ancestor comment of this thread
- if has_replies - check whether comment has replies
Example:
{comments}
{thread_start}<ul class="cmt_lvl_{level}">{/thread_start}
<li>{comment}</li>
{thread_end}</ul>{/thread_end}
{/comments}
</ul>
Alternative nesting method
available since v.2.1
While the method suggested above would work for almost any markup, it will generate invalid html. If you are concerned about that, you should use alternative nesting method. It is making use of 3 tag pairs: {thread_open}, {thread_close} and {thread_container_close}.
{thread_open} tag pair contents will be placed once and should contain the markup that is addded before child comment.
{thread_close} tag pair contents should contain the "closing" markup that is addded after all child comments are displayed. It will be repeated as many times as many child comments you have.
{thread_container_close} tag pair contents will be placed once and should contain the markup to close the root comment container. It will be also use to close comment container if the comment is 'root' and has no nested comments.
Note that there is no {thread_container_open} tag pair, as container opening markup is usually the same and is repeated for each nested comment. If you need it to be different, you can make check for comment level ({if level==0} etc.)
Additionally, you can make use of these special variables (same as above):
- level - the nestedness level. Comments that are replies to entry have 0 level, replies to them level 1 etc.
- parent_id - id of comment to which this one is reply
- root_id - the id of 0-level ancestor comment of this thread
- if has_replies - check whether comment has replies
Example:
{comments}
<li> {comment}
{thread_open}<ul class="cmt_lvl_{level}">{/thread_open}
{thread_close}</li></ul>{/thread_close}
{thread_container_close}</li>{/thread_container_close}
{/comments}
</ul>
Reply-to form using jQuery
It's recommended to use javascript for setting proper parent_id in comment form. Here's sample code that will move and modify the form.
{!-- do not forget to include jQuery library --}
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
{exp:threaded_comments:display}
<ul>
{comments}
{thread_start}
<ul>
{/thread_start}
<li>
<div class="comment-text">{comment}</div>
<p>{url_as_author} --- <a href="javascript:void(0)" class="reply" rel="{comment_id}">Reply to this comment</a> <a href="javascript:void(0)" class="quote reply" rel="{comment_id}">Quote and reply</a> </p>
</li>
{thread_end}
</ul>
{/thread_end}
{/comments}
</ul>
{/exp:threaded_comments:display}
<p><a href="javascript:void(0)" class="reply" rel="0">Reply to entry</a></p>
{exp:threaded_comments:form}
{if logged_out}
<p>
<input type="text" name="name" value="Name" />
</p>
<p>
<input type="text" name="email" value="Email" />
</p>
{/if}
<p>
<textarea name="comment" id="comment-comment" cols="22" rows="5" tabindex="4">Message</textarea>
</p>
<p><input type="submit" name="submit" value="submit" /></p>
{/exp:threaded_comments:form}
{!-- this code will set proper parent_id and move the form --}
<style type="text/css">
#comment_form {display: none;}
</style>
<script type="text/javascript">
$(document).ready(function(){
$('.reply').click(function() {
$('#comment_form input[name=parent_id]').val($(this).attr('rel'));
$('#comment_form').insertAfter( // Insert the comment form after...
$(this)
.parent() // The containing p tag
);
$('#comment_form').show();
});
$('.quote').click(function() {
$('#comment_form textarea[name=comment]').val('[quote]'+
$(this).parent().parent().find('.comment-text').text()+
'[/quote]'
);
});
});
</script>
You might also want to move the form around, that can also be done with jQuery.