diff --git a/includes/actions.inc b/includes/actions.inc
index 1808fb30d38b528097bcdb3dee2831a9e7da7c4d..d9b3f1ffd9d0977aebf1fd50dcb563e6c804eb3d 100644
--- a/includes/actions.inc
+++ b/includes/actions.inc
@@ -25,6 +25,25 @@
  * @} End of "defgroup actions".
  */
 
+/**
+ * @defgroup actions Actions
+ * @{
+ * Functions that perform an action on a certain system object.
+ *
+ * All modules should declare their action functions to be in this group and
+ * each action function should reference its configuration form, validate, and
+ * submit functions using \@see. Conversely, form, validate, and submit
+ * functions should reference the action function using \@see. For examples of
+ * this see comment_unpublish_by_keyword_action(), which has the following in
+ * its doxygen documentation:
+ *
+ * \@ingroup actions
+ * \@see comment_unpublish_by_keyword_action_form().
+ * \@see comment_unpublish_by_keyword_action_submit().
+ *
+ * @} End of "defgroup actions".
+ */
+
 /**
  * Perform a given list of actions by executing their callback functions.
  *
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index e6fe3836f132c0af17b8c35c01540e9ee3c611f3..9db716af63bda8edde40183f75935f0c3865ff79 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -2033,6 +2033,14 @@ function comment_hook_info() {
  */
 function comment_action_info() {
   return array(
+    'comment_publish_action' => array(
+      'description' => t('Publish comment'),
+      'type' => 'comment',
+      'configurable' => FALSE,
+      'hooks' => array(
+        'comment' => array('insert', 'update'),
+      ),
+    ),
     'comment_unpublish_action' => array(
       'description' => t('Unpublish comment'),
       'type' => 'comment',
@@ -2053,12 +2061,37 @@ function comment_action_info() {
 }
 
 /**
- * Drupal action to unpublish a comment.
+ * Action to publish a comment.
  *
+ * @param $comment
+ *   An optional comment object.
  * @param $context
  *   Keyed array. Must contain the id of the comment if $comment is not passed.
+ *
+ * @ingroup actions
+ */
+function comment_publish_action($comment, $context = array()) {
+  if (isset($comment->cid)) {
+    $cid = $comment->cid;
+    $subject = $comment->subject;
+  }
+  else {
+    $cid = $context['cid'];
+    $subject = db_result(db_query("SELECT subject FROM {comments} WHERE cid = %d", $cid));
+  }
+  db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_PUBLISHED, $cid);
+  watchdog('action', 'Published comment %subject.', array('%subject' => $subject));
+}
+
+/**
+ * Action to unpublish a comment.
+ *
  * @param $comment
  *   An optional comment object.
+ * @param $context
+ *   Keyed array. Must contain the id of the comment if $comment is not passed.
+ *
+ * @ingroup actions
  */
 function comment_unpublish_action($comment, $context = array()) {
   if (isset($comment->cid)) {
@@ -2073,10 +2106,36 @@ function comment_unpublish_action($comment, $context = array()) {
   watchdog('action', 'Unpublished comment %subject.', array('%subject' => $subject));
 }
 
+/**
+ * Action to unpublish a comment if it contains a certain string.
+ *
+ * @param $comment
+ *   A comment object.
+ * @param $context
+ *   An array providing more information about the context of the call to this action.
+ *   Unused here, since this action currently only supports the insert and update ops of
+ *   the comment hook, both of which provide a complete $comment object.
+ *
+ * @ingroup actions
+ * @see comment_unpublish_by_keyword_action_form()
+ * @see comment_unpublish_by_keyword_action_submit()
+ */
+function comment_unpublish_by_keyword_action($comment, $context) {
+  foreach ($context['keywords'] as $keyword) {
+    if (strpos($comment->comment, $keyword) !== FALSE || strpos($comment->subject, $keyword) !== FALSE) {
+      db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $comment->cid);
+      watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
+      break;
+    }
+  }
+}
+
 /**
  * Form builder; Prepare a form for blacklisted keywords.
  *
  * @ingroup forms
+ * @see comment_unpublish_by_keyword_action()
+ * @see comment_unpublish_by_keyword_action_submit()
  */
 function comment_unpublish_by_keyword_action_form($context) {
   $form['keywords'] = array(
@@ -2090,28 +2149,9 @@ function comment_unpublish_by_keyword_action_form($context) {
 
 /**
  * Process comment_unpublish_by_keyword_action_form form submissions.
+ *
+ * @see comment_unpublish_by_keyword_action()
  */
 function comment_unpublish_by_keyword_action_submit($form, $form_state) {
   return array('keywords' => drupal_explode_tags($form_state['values']['keywords']));
 }
-
-/**
- * Implementation of a configurable Drupal action.
- * Unpublish a comment if it contains a certain string.
- *
- * @param $context
- *   An array providing more information about the context of the call to this action.
- *   Unused here since this action currently only supports the insert and update ops of
- *   the comment hook, both of which provide a complete $comment object.
- * @param $comment
- *   A comment object.
- */
-function comment_unpublish_by_keyword_action($comment, $context) {
-  foreach ($context['keywords'] as $keyword) {
-    if (strstr($comment->comment, $keyword) || strstr($comment->subject, $keyword)) {
-      db_query('UPDATE {comments} SET status = %d WHERE cid = %d', COMMENT_NOT_PUBLISHED, $comment->cid);
-      watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
-      break;
-    }
-  }
-}