Index: comment.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.admin.inc,v
retrieving revision 1.11
diff -u -r1.11 comment.admin.inc
--- comment.admin.inc	11 Nov 2008 21:44:01 -0000	1.11
+++ comment.admin.inc	25 Nov 2008 10:57:58 -0000
@@ -319,3 +319,45 @@
     _comment_delete_thread($comment);
   }
 }
+
+
+function comment_admin_subscribe() {
+  $form = array();
+  $form['comment_subscribe_enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable subscription to comments'),
+    '#default_value' => variable_get('comment_subscribe_enabled', 0),
+    '#description' => t('Enable subscription to comments.'),
+  );
+  $form['comment_notify_mailtext'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Default mail text for sending out notifications to commenters'),
+    '#description' => t(
+      'You can use the following variables to be replaced:
+      <ul>
+      <li>!commname = the username who posted the comment
+      <li>!commtext = the text of the posted comment
+      <li>!commsubj = the subject of the posted comment
+      <li>!comment_url = the full url of the post and comment - note: if you have paging enabled, this does not work correct - set your max comments per page so that all fit on one page or reverse order them
+      <li>!node_title = the title of the node that was commented on
+      <li>!node_teaser = the teaser of the node that was commented on
+      <li>!node_body = the body of the node that was commented on
+      <li>!mission = site_mission text
+      <li>!name = username receiving the alert
+      <li>!site = your site
+      <li>!uri = base_url of site
+      <li>!uri_brief = base_url of site w/o http
+      <li>!date = the current time
+      <li>!login_uri  uri to login the user
+      <li>!edit_uri = uri to edit user profile
+      <li>!link1 the QUICKLINK to disable future follow-up otifications for the user
+      </ul>'
+    ),
+    '#default_value' => variable_get('comment_subscribe_mailtext', t(COMMENT_SUBSCRIBE_MAILTEXT)),
+    '#return_value' => 1,
+    '#cols' => 80,
+    '#rows' => 15
+  );
+  
+  return system_settings_form($form);
+}
Index: comment.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.install,v
retrieving revision 1.27
diff -u -r1.27 comment.install
--- comment.install	15 Nov 2008 13:01:05 -0000	1.27
+++ comment.install	25 Nov 2008 16:34:59 -0000
@@ -1,6 +1,22 @@
 <?php
 // $Id: comment.install,v 1.27 2008/11/15 13:01:05 dries Exp $
 
+
+/**
+ * Implementation of hook_install().
+ */
+function comment_install() {
+  // Create tables.
+  drupal_install_schema('comment');
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function comment_uninstall() {
+  drupal_uninstall_schema('comment');
+}
+
 /**
  * Implementation of hook_enable().
  */
@@ -259,6 +275,41 @@
       'node_comment_timestamp' => array('last_comment_timestamp'),
     ),
   );
+  
+  $schema['comments_subscribe'] = array(
+    'description' => 'Stores subscribe data.',
+    'fields' => array(
+      'cid' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Unique comment ID.',
+      ),
+      'nid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {node}.nid to which this comment is a reply.',
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.',
+      ),
+      'hash' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The hash provided in notify email to create an unsubscribe link.',
+      ),
+    ),
+    'indexes' => array(
+      'nid' => array('nid'),
+      'cid' => array('cid'),
+      'uid' => array('uid'),
+    ),
+  );
 
   return $schema;
 }
Index: comment.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v
retrieving revision 1.664
diff -u -r1.664 comment.module
--- comment.module	16 Nov 2008 19:41:14 -0000	1.664
+++ comment.module	25 Nov 2008 16:07:12 -0000
@@ -90,6 +90,39 @@
  */
 define('COMMENT_PREVIEW_REQUIRED', 1);
 
+
+/**
+ * A notify email default text.
+ * It's taken from comment_notify module.
+ *
+ */
+define('COMMENT_SUBSCRIBE_MAILTEXT',
+'Hi !name,
+
+!commname has commented on: "!node_title"
+
+The post is about
+----
+!node_teaser
+----
+
+You can view the comment at the following url
+!comment_url
+
+You can stop receiving emails when someone replies to this post,
+by going to !link1
+
+If you have auto-following enabled in your account, you will receive emails like this for all replies to a blog post you commented on. You can disable this by logging in and going to your account settings or unchecking the flag at the time you post the comment.
+
+You can set up auto-following feature for all future posts
+by creating your own user with a few clicks here !uri/user/register
+
+Thanks for your feedback,
+
+Webmaster of !site
+!mission
+!uri');
+
 /**
  * Implementation of hook_help().
  */
@@ -182,6 +215,13 @@
     'access arguments' => array('administer comments'),
     'type' => MENU_LOCAL_TASK,
   );
+  $items['admin/content/comment/subscribe'] = array(
+    'title' => 'Subscribe',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('comment_admin_subscribe'),
+    'access arguments' => array('administer comments'),
+    'type' => MENU_LOCAL_TASK,
+  );
   $items['comment/delete'] = array(
     'title' => 'Delete comment',
     'page callback' => 'comment_delete',
@@ -209,6 +249,22 @@
     'access arguments' => array('administer comments'),
     'type' => MENU_CALLBACK,
   );
+  $items['comment/subscribe/%node'] = array(
+    'title' => 'Subscribe to comments',
+    'page callback' => 'comment_subscribe_immediate',
+    'page arguments' => array(1, 2),
+    'access callback' => 'comment_subscribe_immediate_access',
+    'access arguments' => array(2, TRUE),
+    'type' => MENU_CALLBACK,
+  );
+  $items['comment/unsubscribe/%node'] = array(
+    'title' => 'Unsubscribe to comments',
+    'page callback' => 'comment_subscribe_immediate',
+    'page arguments' => array(1, 2),
+    'access callback' => 'comment_subscribe_immediate_access',
+    'access arguments' => array(2, FALSE),
+    'type' => MENU_CALLBACK,
+  );
 
   return $items;
 }
@@ -257,6 +313,10 @@
       'title' => t('Post comments without approval'),
       'description' => t('Add comments to content (no approval required).'),
     ),
+    'subscribe to comments' => array(
+      'title' => t('Subscribe to comments'),
+      'description' => t('Subscribe to comments.'),
+    ),
   );
 }
 
@@ -476,6 +536,25 @@
         }
       }
     }
+    
+    if (comment_subscribe_enabled() && user_is_logged_in() && user_access('subscribe to comments')) {
+      if (comment_subscribe_is_subscriber(array('nid' => $node->nid))) {
+        $links['subscribe'] = array(
+          'title' => t('Unsubscribe to comments'),
+          'href' => 'comment/unsubscribe/' . $node->nid,
+          'query' => drupal_get_destination(),
+          'attributes' => array('title' => t('Unsubscribe to comments.')),
+        );
+      }
+      else {
+        $links['subscribe'] = array(
+          'title' => t('Subscribe to comments'),
+          'href' => 'comment/subscribe/' . $node->nid,
+          'query' => drupal_get_destination(),
+          'attributes' => array('title' => t('Subscribe to comments.')),
+        );
+      }
+    }
   }
 
   if ($type == 'comment') {
@@ -619,6 +698,9 @@
   db_delete('node_comment_statistics')
     ->condition('nid', $node->nid)
     ->execute();
+  db_delete('comments_subscribe')
+    ->condition('nid', $node->nid)
+    ->execute();
 }
 
 /**
@@ -814,6 +896,7 @@
         watchdog('content', 'Comment: added %subject.', array('%subject' => $edit['subject']), WATCHDOG_NOTICE, l(t('view'), 'node/' . $edit['nid'], array('fragment' => 'comment-' . $edit['cid'])));
       }
       _comment_update_node_statistics($edit['nid']);
+
       // Clear the cache so an anonymous user can see his comment being added.
       cache_clear_all();
 
@@ -825,6 +908,12 @@
       else {
         drupal_set_message(t('Your comment has been posted.'));
         comment_invoke_comment($edit, 'publish');
+        
+        // Handle subscribe
+        if (comment_subscribe_enabled()) {
+          comment_subscribe_manage($edit);
+          comment_subscribe_mail($edit);
+        }
       }
 
       return $edit['cid'];
@@ -842,6 +931,210 @@
 }
 
 /**
+ * Actually add subscribe data into the table.
+ */
+function comment_subscribe_subscribe($comment) {
+  db_insert('comments_subscribe')
+    ->fields(array(
+      'hash' => comment_subscribe_hash($comment['nid'], comment_user_mail($comment)),
+      'nid' => (int)$comment['nid'],
+      'uid' => (int)$comment['uid'],
+      'cid' => array_key_exists('cid', $comment)?(int)$comment['cid']:0,
+    ))
+    ->execute();
+  drupal_set_message(t('Comment: You are subscribed to comments.'));
+}
+
+/**
+ * Actually remove subscribe data from the table.
+ */
+function comment_subscribe_unsubscribe($comment, $hash = NULL) {
+  $query = db_delete('comments_subscribe')->condition('nid', $comment['nid']);
+  if ($hash) {
+    $query->condition('hash', $hash);
+  }
+  else {
+    $query->condition('uid', $comment['uid']);
+  }
+  $query->execute();
+  drupal_set_message(t('Comment: You are unsubscribed to comments.'));
+}
+
+/**
+ * Check if an user is subscriber of comments of the node.
+ */
+function comment_subscribe_is_subscriber($comment) {
+  global $user;
+  if ($user->uid) {
+    return (bool)db_query("SELECT 1 FROM {comments_subscribe} WHERE nid = :nid AND uid = :uid", array(':nid' => $comment['nid'], ':uid' => $user->uid))->fetchField();
+  }
+  
+  if ((isset($comment['mail']) && $comment['mail'] && $mail = $comment['mail']) || 
+    (isset($_COOKIE['comment_info_mail']) && $mail = $_COOKIE['comment_info_mail'])) {
+    return (bool)db_query("SELECT 1 FROM {comments_subscribe} cs LEFT JOIN {comments} c ON c.cid = cs.cid WHERE cs.nid = :nid AND c.mail = :mail", array(':nid' => $comment['nid'], ':mail' => $mail))->fetchField();
+  }
+  
+  return FALSE;
+}
+
+/**
+ * Make a subscribe logic depended on 'subscribe' checkbox.
+ */
+function comment_subscribe_manage($comment, $hash = NULL) {
+  if (!$hash) {
+    $is_subscriber = comment_subscribe_is_subscriber($comment);
+    if (array_key_exists('subscribe', $comment) && $comment['subscribe']) {
+      return $is_subscriber || comment_subscribe_subscribe($comment);
+    }
+    else {
+      return $is_subscriber && comment_subscribe_unsubscribe($comment);
+    }
+  }
+  return comment_subscribe_unsubscribe($comment, $hash);
+}
+
+/**
+ * A callback for immediate subscribe/unsubscribe to comments.
+ */
+function comment_subscribe_immediate($op, $node, $hash = NULL) {
+  global $user;
+  $comment = array();
+  $comment['nid'] = $node->nid;
+  $comment['uid'] = $user->uid;
+  $comment['subscribe'] = ($op == 'subscribe');
+  comment_subscribe_manage($comment, $hash);
+  drupal_goto();
+}
+
+/**
+ * Send notificatins.
+ * It's taken from comment_notify module.
+ */
+function comment_subscribe_mail($comment) {
+  $comment = (object) $comment;
+  global $language;
+  global $base_url;
+  global $user;
+
+  $initial_language = $language;
+
+  if (function_exists('locale')) {
+    $languages = locale_language_list();
+    $languages = $languages['name'];
+  }
+
+  $nid = $comment->nid;
+  $cid = $comment->cid;
+  $node = node_load($nid);
+  if (!isset($comment->mail)) {
+    $comment_account = user_load(array('name' => $comment->name));
+    $comment_mail = $comment_account->mail;
+  }
+  else {
+    $comment_mail = $comment->mail;
+  }
+  $sent_to = array();
+  
+  //Get the list of commenters to notify
+  $result = db_query("SELECT cs.uid, c.name, c.mail AS cmail, u.mail AS umail, u.init AS uinit, cs.hash as hash
+    FROM {comments_subscribe} cs LEFT JOIN users u ON cs.uid = u.uid
+    LEFT JOIN {comments} c ON c.cid = cs.cid
+    WHERE cs.nid = %d", $nid
+  );
+  
+  while ($alert = db_fetch_object($result)) {
+    $umail = empty($alert->umail) ? $alert->uinit : $alert->umail;
+    $mail = empty($alert->cmail) ? $umail : $alert->cmail;
+
+    if ($mail != $comment_mail && !in_array($mail, $sent_to) && $alert->uid != $comment->uid) {
+      $message = array();
+      if (!empty($alert->uid)) {
+        $recipient_user = user_load(array('uid' => $alert->uid));
+        $language = user_preferred_language($recipient_user);
+      }
+      else {
+        $language = language_default();
+      }
+
+      $message['subject'] = t('!site :: new comment for your post.', array('!site' => variable_get('site_name', 'drupal')));
+      $message['body'] = t(
+        variable_get('comment_subscribe_mailtext', DEFAULT_MAILTEXT),
+        array(
+          '!commname' => $comment->name,
+          '!commtext' => $comment->comment,
+          '!commsubj' => $comment->subject,
+          '!comment_url' => url('node/'. $nid, array('absolute' => TRUE, 'fragment' => 'comment-'. $cid)),
+          '!node_title' =>  $node->title,
+          '!node_teaser' => $node->teaser,
+          '!mission' => variable_get('site_mission', ''),
+          '!node_body' =>  $node->body,
+          '!name' => $alert->name,
+          '!site' => variable_get('site_name', 'drupal'),
+          '!uri' => $base_url,
+          '!uri_brief' => preg_replace('!^https?://!', '', $base_url),
+          '!date' => format_date(time()),
+          '!login_uri' => url('user', array('absolute' => TRUE)),
+          '!edit_uri' => url('user/'. $alert->uid .'/edit', array('absolute' => TRUE)),
+          '!link1' => url('comment/unsubscribe/' . $nid . '/'. $alert->hash , array('absolute' => TRUE))
+        )
+      );
+      drupal_mail('comment', 'comment_mail', $mail, $language, $message);
+      $sent_to[] = $mail;
+
+      // revert to previous (site default) locale
+      $language = $initial_language;
+    }
+  }
+}
+
+/**
+ * Implementation of hook_mail()
+ */
+function comment_mail($key, &$message, $params) {
+  $message['subject'] = $params['subject'];
+  $message['body'][] = $params['body'];
+}
+
+/**
+ * Access callback for immediate subscribe/unsubscribe to comments.
+ */
+function comment_subscribe_immediate_access($node, $subscribe = TRUE) {
+  $access = comment_subscribe_enabled() && user_access('subscribe to comments') && node_access('view', $node);
+  return $access && ($subscribe?user_is_logged_in():TRUE);
+}
+
+
+/**
+ * Access callback for subscribe/unsubscribe to comments.
+ */
+function comment_subscribe_access($node) {
+  return (variable_get('comment_subscribtion_enable', 0) && user_access('subscribe to comments') && node_access('view', $node));
+}
+
+/**
+ * Returns a hash provided in notify emails to unsubscribe (used for anonymous unsubscribe).
+ */
+function comment_subscribe_hash($nid, $mail) {
+  return drupal_get_token($nid . $mail);  
+}
+
+
+function comment_subscribe_enabled() {
+  return (bool)variable_get('comment_subscribe_enabled', 0);
+}
+
+/**
+ * Returns an email of the comment's user.
+ */
+function comment_user_mail($comment) {
+  if ($comment['uid']) {
+    $account = user_load($comment['uid']);
+    return empty($account->mail) ? $account->init : $account->mail;
+  }
+  return $comment['mail'];
+}
+
+/**
  * Build command links for a comment (e.g.\ edit, reply, delete) with respect to the current user's access permissions.
  *
  * @param $comment
@@ -1241,6 +1534,10 @@
       elseif (variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT) == COMMENT_ANONYMOUS_MUST_CONTACT) {
         form_set_error('mail', t('You have to leave an e-mail address.'));
       }
+      
+      if (isset($edit['subscribe']) && $edit['subscribe'] && !$edit['mail']) {
+        form_set_error('mail', t('You have to leave an e-mail address if you want to subscribe to comments.'));
+      }
 
       if ($edit['homepage']) {
         if (!valid_url($edit['homepage'], TRUE)) {
@@ -1451,7 +1748,20 @@
     '#input_format' => isset($edit['format']) ? $edit['format'] : FILTER_FORMAT_DEFAULT,
     '#required' => TRUE,
   );
-
+  
+  if (comment_subscribe_access($node)) {
+    $form['subscribe_fieldset'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Subscribe to comments'),
+    );
+    $form['subscribe_fieldset']['subscribe'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Recieve notifications for all comments on this post'),
+      '#default_value' => comment_subscribe_is_subscriber(array('nid' => $node->nid)),
+      '#description' => t('Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.'),
+    );
+  }
+  
   $form['cid'] = array(
     '#type' => 'value',
     '#value' => !empty($edit['cid']) ? $edit['cid'] : NULL,

