3 min read

Allowing anonymous comment deletion rights

Allowing anonymous comment deletion rights

I have implemented a method of anonymous comment deletion on this site, based on URL callback of a link displayed at comment post time.

As I explained in this forum post, the development of a module that enables anonymous deletion arose from the desire to instantly remove spam comments that bypass any spam filtering on the website. A lot of emails notifying me of comments being posted arrive in the email box on my mobile phone. Since this isn't continuously logged into my site, having to log in every time I want to delete spam is a pain. By having a callback, secured with a hash, that can delete individual comments without logging in I am able to delete any comments with ease.

/**
 * Implements hook_menu()
 */
function mymodule_menu() {
  $items['comment/%/fastdelete/%'] = array(
    'title' => 'Fast Comment Deletion',
    'page callback' => 'mymodule_comment_fastdelete',
    'page arguments' => array(1, 3),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  
  return $items;
}

/**
 * Authentication function for menu callback determining if the
 * comment should be deleted
 */
function mymodule_comment_fastdelete($cid, $hash) {
  $comment = comment_load($cid);
  // First check to see if the comment actually exists
  if ($comment) {
    // Add in a timeout so the comment can be deleted only in the
    // first 24 hours after posting.
    $timeout = variable_get('user_password_reset_timeout', 86400);    
    $current = REQUEST_TIME;
    if ($current - $timeout > $comment->created) {      
      drupal_set_message(t('You have tried to use a comment delete link that has expired. To have the comment deleted please contact the site administrator.'), 'warning');
      drupal_goto('contact-me');
    }
    else {
      // Load part of the user object of the node author for a secret string to send to user_pass_rehash
      $author = mymodule_node_author_pass_from_cid($cid);
      if ($hash == user_pass_rehash($cid, $comment->created, $author->pass) && $current >= $comment->created) {
        watchdog('mymodule', 'Comment Autodelete link used', array(), WATCHDOG_NOTICE);
        comment_delete($cid);
        drupal_set_message('Comment successfully deleted!');        
        drupal_goto('node/' . $comment->nid);
      }
      else {
        drupal_set_message('You have tried to use an invalid comment deletion link.', 'warning');
        drupal_goto('node/' . $comment->nid);
      }
    }
  }
  else {
    drupal_set_message('You have tried to use an invalid comment deletion link.', 'warning');
    drupal_goto('');
  }
}

/**
 * Generates the deletion link for a specific comment.
 */
function mymodule_comment_fastdelete_link($cid) {
  $comment = comment_load($cid);
  $author = mymodule_node_author_pass_from_cid($cid);
  // Combine a number of variables to construct a private hash that will be validated in order to delete the comment.
  return url("comment/$cid/fastdelete/" . user_pass_rehash($cid, $comment->created, $author->pass), array('absolute' => TRUE));
}

/**
 * Returns the hashed password of the node author the comment is posted on.
 * Used for an unknown part of the hash that an anonymous user could not guess
 */
function mymodule_node_author_pass_from_cid($cid) {
  $result = db_query('SELECT u.pass FROM {comment} c JOIN {node} n on n.nid = c.nid JOIN {users} u ON n.uid = u.uid WHERE c.cid = :cid', array(':cid' => $cid));
  return $result->fetchObject();
}

/**
 * Implements hook_token_info_alter()
 */
function mymodule_token_info_alter(&$data) {
  $data['tokens']['comment']['comment_fastdelete_link'] = array(    
    'name' => t("Comment Delete Link"),
    'description' => t("A link to immediately delete a comment."),
  );
}

/**
 * Implements hook_tokens()
 *
 */
function mymodule_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'comment') {
    foreach ($tokens as $name => $original) {
    switch ($name) {
      case 'comment_fastdelete_link':
        $cid = $data['comment']->cid;
        $link = mymodule_comment_fastdelete_link($cid);
        if (isset($cid)) {
          $replacements[$original] = $link;
        }
        else {
          $replacements[$original] = '';
        }
        break;
      }
    }
  }
  return $replacements;
}

I've allowed anonymous users the permission of deleting their own comments on this node as a proof of concept for people wishing to test out the functionality. I'm considering creating a separate module for this functionality and releasing it for other Drupal users. This is all dependent on responses to this post, if they're good I'll make a proper module out of it!

A couple of additional things I'd add into a module would be the ability for the administrative user to allow/disallow the functionality on certain nodes/content types. I'd also add in some kind of alteration if the comment has child comments beneath it. Perhaps instead of deleting the comment a better way to deal with it would be to replace the comment body text with [deleted] or similar.