Drupal 6 Limiting threaded comment depth to one

By shane
Thu, 2011-09-01 09:16
3 comments

Share with Others

I recently wanted to create a comment system that was threaded but only allowed one level of replies. Basically I wanted something like this:


-Comment 1
---Reply 1-1
---Reply 1-2
-Comment 2
---Reply 2-1
---Reply 2-2

I still wanted to show all the reply links in the comment, however if a user tried to reply to an already indented comment, I wanted it to just add it to the bottom of the thread. For instance, clicking the reply link on "Reply 1-1" in the example above would add a "Reply 1-3" comment directly below "Reply-1.2". Make sense?

At first glance the easiest way to do this would be to modify Drupal 6 core comment code (which I usually don't recommend). Here is the code I added to the comment_save function in the comment.module file (The only code I added is after the @HACK comment, the rest is code from the comment_save function so you have context as to where it was added).

if ($edit['pid'] == 0) {
  // This is a comment with no parent comment (depth 0): we start
  // by retrieving the maximum thread level.
  $max = db_result(db_query('SELECT MAX(thread) FROM {comments} WHERE nid = %d', $edit['nid']));
 
  // Strip the "/" from the end of the thread.
  $max = rtrim($max, '/');
 
  // Finally, build the thread field for this new comment.
  $thread = int2vancode(vancode2int($max) + 1) .'/';
}
else {
  // This is comment with a parent comment: we increase
  // the part of the thread value at the proper depth.
 
  // Get the parent comment:
  $parent = _comment_load($edit['pid']);
 
  //@HACK to only allow one level of comments
  //This loop is a little bit ugly but I wrote it this way to keep the
  //rest of the comment code unchanged. After the loop is finished, the
  //$parent variable will contain the top level parent comment
  $new_parent = $parent;
  while(isset($new_parent->pid)) {
    $parent = $new_parent;
    $new_parent = _comment_load($new_parent->pid);
  }
  //print('<pre>'.print_r($parent,1).'</pre>');die();
 
  // Strip the "/" from the end of the parent thread.
  $parent->thread = (string) rtrim((string) $parent->thread, '/');
 
  // Get the max value in _this_ thread.
  $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
 
  if ($max == '') {
    // First child of this parent.
    $thread = $parent->thread .'.'. int2vancode(0) .'/';
  }
  else {
    // Strip the "/" at the end of the thread.
    $max = rtrim($max, '/');
 
    // We need to get the value at the correct depth.
    $parts = explode('.', $max);
    $parent_depth = count(explode('.', $parent->thread));
    $last = $parts[$parent_depth];
 
    // Finally, build the thread field for this new comment.
    $thread = $parent->thread .'.'. int2vancode(vancode2int($last) + 1) .'/';
  }
}

Basically the loop continues loading the parent comments until it gets to a comment without a parent. It then uses that as the parent comment of the comment you are trying to save. For instance, using the diagram above, if you are saving a comment reply to "Reply 1-1". The code is going to first load the initial parent "Reply 1-1", after that it will notice that "Reply 1-1" also has a parent. The code will proceed to load that parent as well (in this case "Comment 1"). Since "Comment 1" does not have a parent, it will use "Comment 1" as the parent of the newly created comment, rather than than using "Reply 1-1".

Because I never recommend hacking Drupal core, I looked for an alternative, and what do you know... hook_comment to the rescue... or so I thought. It turns out that using hook_comment, you cannot actually alter the comment before it is saved. I guess this is one of those rare cases where the only way to get it to work correctly is to modify core.

Note that if you are doing this, it removes some of the flexibility of the comment system. For instance, if you decide later you want deeper nesting for the comments, and you remove the code above, all the current comments will still stay at only one level of nesting (regardless of what reply links your users clicked on when generating their comments).

Hopefully this helps you keep your nested comments in line. If you find a way to do this without hacking core or if you have questions. Let us know in the comments.

Comments

This might be a bit of a late reply, but might be useful nonetheless (either for you to learn something new, or for me to learn if my way of doing things is no good -- and why)

Whenever I come across a situation where hacking core seems inevitable, it turns out to be still evitable. This is because it tends to be about either 1. form handling, or 2. mail handling (which tends to boil down to 1. form handling in 90% of the cases).

What I do in these cases, is use hook_form_alter, check the form ID to make sure I'm treating the right form, and find the submit handlers. The problematic handler gets overwritten by a copy-paste version of it which I add in the same custom module. In that copy-paste version, I can then make the changes I need without having to hack core.

In your specific example, I'd catch the comment form, overwrite comment_form_submit with a copy of it (called my_module_comment_form_submit, for example). In my_module_comment_form_submit, replace all calls to comment_save with a copy-paste version called my_module_comment_save, and do your hacks in there.

Of course, this still has its limitations. If comment_save is called in plenty of other places which are actually used (it IS called in some other places, but I'm not sure they're typically used). Another case where this method hits its limitations, is if it's nested 10 or so function calls deep within the submit handler. You can't go copy-paste 10 core functions, and adjust each one of them. Most of the time, though, a simple copy-paste and doing the hacking in the copy will work.

Alternatively, for the coments themselves, you might be able to do the while loop in a preprocess function at theme level. That still keeps the flexibility of the comments system. Drupal 7 also has hook_comment_view, which seems to solve this problem entirely.

Post new comment