diff --git a/includes/actions.inc b/includes/actions.inc
index 3ef5b848a60c24dcffbc3d12243a667ed76d6c59..ce196cfe556b2187a7ad8be7dfb72dee533602ea 100644
--- a/includes/actions.inc
+++ b/includes/actions.inc
@@ -6,6 +6,25 @@
  * This is the actions engine for executing stored 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".
+ */
+
 /**
  * Performs a given list of actions by executing their callback functions.
  *
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 88795e6861987ba43d0e83f16c95ffed7119dd36..93958f37a9f58006bbbab141b720db7077a1eb5a 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -2306,9 +2306,17 @@ function vancode2int($c = '00') {
 
 /**
  * Implement hook_action_info().
+ *
+ * @ingroup actions
  */
 function comment_action_info() {
   return array(
+    'comment_publish_action' => array(
+      'label' => t('Publish comment'),
+      'type' => 'comment',
+      'configurable' => FALSE,
+      'triggers' => array('comment_insert', 'comment_update'),
+    ),
     'comment_unpublish_action' => array(
       'label' => t('Unpublish comment'),
       'type' => 'comment',
@@ -2320,17 +2328,45 @@ function comment_action_info() {
       'type' => 'comment',
       'configurable' => TRUE,
       'triggers' => array('comment_insert', 'comment_update'),
-    )
+    ),
   );
 }
 
 /**
- * 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_query('SELECT subject FROM {comment} WHERE cid = :cid', array(':cid', $cid))->fetchField();
+  }
+  db_update('comment')
+    ->fields(array('status' => COMMENT_PUBLISHED))
+    ->condition('cid', $cid)
+    ->execute();
+  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)) {
@@ -2348,10 +2384,39 @@ 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_update('comment')
+        ->fields(array('status' => COMMENT_NOT_PUBLISHED))
+        ->condition('cid', $comment->cid)
+        ->execute();
+      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(
@@ -2366,36 +2431,13 @@ 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']));
 }
 
-/**
- * Implement 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 (strpos($comment->comment, $keyword) !== FALSE || strpos($comment->subject, $keyword) !== FALSE) {
-      db_update('comment')
-        ->fields(array('status' => COMMENT_NOT_PUBLISHED))
-        ->condition('cid', $comment->cid)
-        ->execute();
-      watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
-      break;
-    }
-  }
-}
-
 /**
  * Implement hook_ranking().
  */