You are here

ajax_comments.module in AJAX Comments 7

Same filename and directory in other branches
  1. 8 ajax_comments.module
  2. 5 ajax_comments.module
  3. 6 ajax_comments.module

AJAX comments module file.

File

ajax_comments.module
View source
<?php

/**
 * @file
 * AJAX comments module file.
 */

/**
 * Implements hook_menu().
 */
function ajax_comments_menu() {
  $items['admin/config/content/ajax_comments'] = array(
    'title' => 'AJAX comments',
    'description' => 'AJAXifies comments on site.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ajax_comments_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'ajax_comments.admin.inc',
  );
  $items['ajax_comment/%comment/edit'] = array(
    'page callback' => 'ajax_comments_edit',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'comment_access',
    'access arguments' => array(
      'edit',
      1,
    ),
    'delivery callback' => 'ajax_deliver',
    'type' => MENU_CALLBACK,
  );

  // Special-casing for EditLimit Module.
  if (module_exists('edit_limit')) {
    $items['ajax_comment/%comment/edit']['access callback'] = 'edit_limit_comment_access';
  }
  $items['ajax_comment/%/delete'] = array(
    'page callback' => 'ajax_comments_delete',
    'page arguments' => array(
      1,
    ),
    'access arguments' => array(
      'administer comments',
    ),
    'delivery callback' => 'ajax_deliver',
    'type' => MENU_CALLBACK,
  );

  // Special-casing for comment_goodness Module
  if (module_exists('comment_goodness')) {
    $items['ajax_comment/%/delete']['access arguments'] = array(
      1,
    );
    $items['ajax_comment/%/delete']['access callback'] = 'comment_goodness_delete_comment_access';
  }

  // Special-casing for CommentAccess Module
  if (module_exists('commentaccess')) {
    $items['ajax_comment/%/delete']['access callback'] = 'commentaccess_access_check';
    $items['ajax_comment/%/delete']['access arguments'] = array(
      1,
      'delete',
    );
  }
  $items['ajax_comment/reply/%node'] = array(
    'page callback' => 'ajax_comments_reply',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'node_access',
    'access arguments' => array(
      'view',
      2,
    ),
    'delivery callback' => 'ajax_deliver',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_views_api().
 */
function ajax_comments_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'ajax_comments'),
  );
}

/**
 * Implements hook_views_data().
 */
function ajax_comments_views_data() {
  $data['node']['list_comments'] = array(
    'title' => t('List of comments'),
    'help' => t("Display the node's list of comments."),
    'field' => array(
      'handler' => 'ajax_comments_handler_field_list_comments',
    ),
  );
  $data['node']['ajax_comment'] = array(
    'title' => t('AJAX Add Comment'),
    'help' => t('Adds an inline AJAX comment form.'),
    'field' => array(
      'handler' => 'ajax_comments_handler_field_ajax_add_comment',
    ),
  );
  return $data;
}

/**
 * Implements hook_preprocess_node().
 *
 * AJAXify "Add new comment" link when there is no default form.
 */
function ajax_comments_preprocess_node(&$variables) {
  $node = $variables['node'];
  $view_mode = $variables['view_mode'];
  if (!ajax_comments_node_type_active($node->type)) {
    return;
  }
  if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW) {
    return;
  }
  if (empty($node->comment) || $node->comment == COMMENT_NODE_CLOSED) {
    return;
  }
  drupal_add_library('system', 'drupal.ajax');
  drupal_add_library('system', 'drupal.form');
  drupal_add_js(drupal_get_path('module', 'ajax_comments') . '/ajax_comments.js', 'file');
  $variables['content']['links']['comment']['#links']['comment-add']['attributes']['class'][] = 'use-ajax-comments';
  $variables['content']['links']['comment']['#links']['comment-add']['attributes']['class'][] = 'ajax-comments-reply-' . $node->nid . '-0';
  $variables['content']['links']['comment']['#links']['comment-add']['fragment'] = NULL;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function ajax_comments_form_comment_form_alter(&$form, &$form_state, $form_id) {

  // Check to see if this node type uses ajax comments.
  if (!ajax_comments_node_type_active($form['#node']->type)) {
    return;
  }

  // Disable output original node/comment in preview
  if (isset($form['comment_preview'])) {
    $form['notify_text'] = array(
      '#markup' => variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array(
        'type' => 'preview',
        'comment' => $form_state['comment'],
      )) : '',
      '#weight' => "-100",
    );
    if (isset($form['comment_output_below'])) {
      unset($form['comment_output_below']);
    }
  }
  if (empty($form_state['storage']['ajax_comments_form_id'])) {
    $cid = $pid = empty($form_state['comment']->cid) ? 0 : $form_state['comment']->cid;
    $pid = empty($form_state['comment']->pid) ? 0 : $form_state['comment']->pid;
    $id = 'ajax-comments-reply-form-' . $form_state['comment']->nid . '-' . $pid . '-' . $cid;
    $form_state['storage']['ajax_comments_form_id'] = $id;

    // Get comment form state class
    if (!empty($cid)) {

      // Comment Edit Form
      $form_state_class = 'ajax-comments-form-edit';
    }
    else {
      if (!empty($pid)) {

        // Comment Reply Form
        $form_state_class = 'ajax-comments-form-reply';
      }
      else {

        // Add New Comment Form
        $form_state_class = 'ajax-comments-form-add';
      }
    }
    $form_state['storage']['form_state_class'] = $form_state_class;
  }
  else {
    $id = $form_state['storage']['ajax_comments_form_id'];
    $form_state_class = $form_state['storage']['form_state_class'];
  }
  $form['#attributes']['id'] = drupal_html_id($id);
  $form['#attributes']['class'][] = $id;
  $form['#attributes']['class'][] = $form_state_class;
  $form['actions']['submit']['#ajax'] = array(
    'callback' => 'ajax_comments_submit_js',
    'wrapper' => $form['#attributes']['id'],
    'method' => 'replace',
    'effect' => 'fade',
  );

  // Set unique id (need for Views with enabled AJAX)
  if (empty($form['actions']['submit']['#id'])) {
    $form['actions']['submit']['#id'] = drupal_html_id('edit-' . $id);
  }
  $form['actions']['preview']['#ajax'] = array(
    'callback' => 'ajax_comments_preview_js',
    'wrapper' => $form['#attributes']['id'],
    'method' => 'replace',
    'effect' => 'fade',
  );

  // Set unique id (need for Views with enabled AJAX)
  if (empty($form['actions']['preview']['#id'])) {
    $form['actions']['preview']['#id'] = drupal_html_id('preview-' . $id);
  }

  // If this a reply to comment offer a 'cancel' button
  if (isset($form_state['comment']->pid)) {
    $form['actions']['cancel'] = array(
      '#type' => 'button',
      '#value' => t('Cancel'),
      '#access' => true,
      '#weight' => 21,
      '#limit_validation_errors' => array(),
    );
    $form['actions']['cancel']['#ajax'] = array(
      'wrapper' => $form['#attributes']['id'],
      'method' => 'replace',
      'effect' => 'fade',
    );
    if (empty($form_state['comment']->cid)) {
      $form['actions']['cancel']['#ajax']['callback'] = 'ajax_comments_cancel_js';
    }
    else {
      $form['actions']['cancel']['#ajax']['callback'] = 'ajax_comments_edit_cancel_js';
    }

    // Set unique id (need for Views with enabled AJAX)
    if (empty($form['actions']['cancel']['#id'])) {
      $form['actions']['cancel']['#id'] = drupal_html_id('cancel-' . $id);
    }
  }
  $form['#attached'] = array(
    'js' => array(
      drupal_get_path('module', 'ajax_comments') . '/ajax_comments.js',
    ),
  );
  $form['actions'] = ajax_pre_render_element($form['actions']);

  // HACK, stop ctools from modifying us in node_comment_form.inc
  $form_state['ctools comment alter'] = FALSE;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function ajax_comments_form_comment_confirm_delete_alter(&$form, &$form_state, $form_id) {
  if (ajax_comments_node_type_active(substr($form['#comment']->node_type, strlen('comment_node_')))) {
    $form['actions']['submit']['#ajax'] = array(
      'callback' => 'ajax_comments_delete_js',
      'wrapper' => $form['#id'],
      'method' => 'replace',
    );
    $form['actions']['cancel']['#attributes']['onclick'][] = 'jQuery(\'#' . $form['#id'] . '\').siblings().show().end().remove(); return false;';
  }
}

/**
 * Previews the comment.
 */
function ajax_comments_preview_js($form, &$form_state) {

  // Return the actual form if it contains errors.
  if (form_get_errors()) {

    // Remove comment preview
    if (isset($form['comment_preview'])) {
      unset($form['notify_text']);
      unset($form['comment_preview']);
    }
    return $form;
  }
  $pid = empty($form_state['comment']->pid) ? 0 : $form_state['comment']->pid;
  $cid = empty($form_state['comment']->cid) ? 0 : $form_state['comment']->cid;
  $commands[] = ajax_command_replace('.ajax-comments-reply-form-' . $form_state['comment']->nid . '-' . $pid . '-' . $cid, drupal_render($form));
  if (!variable_get('ajax_comments_disable_scroll', 0)) {
    $commands[] = array(
      'command' => 'ajaxCommentsScrollToElement',
      'selector' => '.ajax-comments-reply-form-' . $form_state['comment']->nid . '-' . $pid . '-' . $cid,
    );
  }
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Cancel edit  the comment.
 */
function ajax_comments_cancel_js($form, &$form_state) {
  $cid = empty($form_state['comment']->cid) ? 0 : $form_state['comment']->cid;
  $pid = empty($form_state['comment']->pid) ? 0 : $form_state['comment']->pid;
  $commands[] = ajax_command_remove('.ajax-comments-reply-form-' . $form['#node']->nid . '-' . $pid . '-' . $cid);
  if (variable_get('comment_form_location_' . $form['#node']->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_BELOW) {
    if (!variable_get('ajax_comments_disable_scroll', 0)) {
      $commands[] = array(
        'command' => 'ajaxCommentsScrollToElement',
        'selector' => '.comment-wrapper-' . $pid,
      );
    }
  }

  // Show reply to comment link
  $commands[] = ajax_command_invoke('.ajax-comments-reply-' . $form['#node']->nid . '-' . $pid, 'show');
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Re-grabs comment after editing is cancelled.
 */
function ajax_comments_edit_cancel_js($form, &$form_state) {
  $comment = comment_load($form_state['comment']->cid);
  $comment_build = comment_view($comment, $form['#node']);
  $comment_output = drupal_render($comment_build);
  $commands[] = ajax_command_replace('.ajax-comments-reply-form-' . $comment->nid . '-' . $comment->pid . '-' . $comment->cid, $comment_output);
  if (!variable_get('ajax_comments_disable_scroll', 0)) {
    $commands[] = array(
      'command' => 'ajaxCommentsScrollToElement',
      'selector' => '.comment-wrapper-' . $form_state['comment']->cid,
    );
  }
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Builds the comment.
 */
function ajax_comments_submit_js($form, &$form_state) {

  // Return the actual form if it contains errors.
  if (form_get_errors()) {

    // Remove comment preview
    if (isset($form['comment_preview'])) {
      unset($form['notify_text']);
      unset($form['comment_preview']);
    }
    return $form;
  }

  // This is to remove the "Your comment has been posted" status message that
  // will appear upon refresh. This seems dirty but it means we don't have to
  // rewrite the whole comment_form_submit(). Please chime in if you think this
  // is dumb.
  ajax_comments_remove_status();
  $cid = empty($form['cid']['#value']) ? 0 : $form['cid']['#value'];
  $pid = empty($form_state['comment']->pid) ? 0 : $form_state['comment']->pid;
  $key = _ajax_comments_cache_key($form_state['comment']->nid, $pid);
  if (cache_get($key)) {

    // This form cached. Remove it from cache.
    cache_clear_all($key, 'cache');
  }
  $comment = comment_load($form_state['comment']->cid);
  $node = $form['#node'];
  $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array(
    'comment' => $comment,
  )) : '';
  $comment_build = comment_view($comment, $node);

  // remove messages
  if (variable_get('ajax_comments_notify')) {
    $commands[] = ajax_command_remove('.messages.warning');
    $commands[] = ajax_command_remove('.messages.status');
  }

  // Don't display as a preview as this is being submitted.
  unset($comment_build['comment_body']['#object']->in_preview);
  unset($form_state['comment_preview']);
  $comment_output = drupal_render($comment_build);

  // Are we editing a comment.
  if (isset($form['cid']['#value'])) {

    //$commands[] = ajax_command_replace('.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid, $comment_output);
    $commands[] = array(
      'command' => 'ajaxCommentsReplace',
      'selector' => '.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid,
      'html' => $comment_output,
    );
  }
  elseif (!empty($form_state['values']['pid'])) {
    $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
    if (!empty($mode)) {

      // Threaded. Append comment to parent wrapper.

      //$commands[] = ajax_command_replace('.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid, $comment_output);
      $commands[] = array(
        'command' => 'ajaxCommentsReplace',
        'selector' => '.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid,
        'html' => $comment_output,
      );
    }
    else {

      // Flat. Check sort by comment_goodness.
      if (_ajax_comments_get_comment_sort_order($node) == 1) {

        // Older first. Append comment to last wrapper.
        $commands[] = ajax_command_invoke('.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid, 'remove');

        //$commands[] = ajax_command_after('.comment-wrapper-nid-' . $comment->nid . ' > .ajax-comment-wrapper:last', $comment_output);
        $commands[] = array(
          'command' => 'ajaxCommentsAfter',
          'selector' => '.comment-wrapper-nid-' . $comment->nid . ' > .ajax-comment-wrapper:last',
          'html' => $comment_output,
        );
      }
      else {

        // Newer first. Append comment to top.
        $commands[] = ajax_command_invoke('.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid, 'remove');

        //$commands[] = ajax_command_prepend('.comment-wrapper-nid-' . $comment->nid, $comment_output);
        $commands[] = array(
          'command' => 'ajaxCommentsPrepend',
          'selector' => '.comment-wrapper-nid-' . $comment->nid,
          'html' => $comment_output,
        );
      }
    }
    if (!variable_get('ajax_comments_disable_scroll', 0)) {
      $commands[] = array(
        'command' => 'ajaxCommentsScrollToElement',
        'selector' => '.comment-wrapper-' . $comment->cid,
      );
    }
  }
  else {

    // Check sort by comment_goodness.
    if (_ajax_comments_get_comment_sort_order($node) == 1) {

      // Older first. Append comment to last wrapper.

      //$commands[] = ajax_command_after('.comment-wrapper-nid-' . $comment->nid . ' > .ajax-comment-wrapper:last', $comment_output);
      $commands[] = array(
        'command' => 'ajaxCommentsAfter',
        'selector' => '.comment-wrapper-nid-' . $comment->nid . ' > .ajax-comment-wrapper:last',
        'html' => $comment_output,
      );
    }
    else {

      // Newer first. Append comment to top.

      //$commands[] = ajax_command_prepend('.comment-wrapper-nid-' . $comment->nid, $comment_output);
      $commands[] = array(
        'command' => 'ajaxCommentsBefore',
        'selector' => '.comment-wrapper-nid-' . $comment->nid . '> .ajax-comment-wrapper:first',
        'html' => $comment_output,
      );
    }

    // If we have a default form, update it with a new one.
    if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_BELOW) == COMMENT_FORM_BELOW && empty($form_state['build_info']['args'][1]['flag'])) {
      $new_form_state = array();
      $new_form_state['build_info']['args'][] = (object) array(
        'nid' => $node->nid,
      );

      // Don't pull from cache.
      $new_form_state['input'] = array();
      $new_form_build = drupal_build_form($form['#form_id'], $new_form_state);
      $commands[] = ajax_command_replace('.ajax-comments-reply-form-' . $form_state['comment']->nid . '-' . $pid . '-' . $cid, drupal_render($new_form_build));
      if (!variable_get('ajax_comments_disable_scroll', 0) && _ajax_comments_get_comment_sort_order($node) == 2) {
        $commands[] = array(
          'command' => 'ajaxCommentsScrollToElement',
          'selector' => '.comment-wrapper-nid-' . $comment->nid,
        );
      }
    }
    else {
      $commands[] = ajax_command_invoke('.ajax-comments-reply-form-' . $comment->nid . '-' . $pid . '-' . $cid, 'remove');
    }
  }
  if (!empty($form_state['build_info']['args'][1]['flag'])) {

    // Submiting comment via Views Add Comment Form
    switch ((int) $form_state['build_info']['args'][1]['flag']) {
      case 1:
        $commands[] = ajax_command_invoke('.views-comment-result-nid-' . $comment->nid, 'show');
        break;
      case 2:
        $commands[] = ajax_command_invoke('.ajax-comments-reply-' . $comment->nid . '-' . $pid, 'show');
        break;
    }
  }
  else {

    // Show reply to comment link
    $commands[] = ajax_command_invoke('.ajax-comments-reply-' . $comment->nid . '-' . $pid, 'show');
  }

  // Show notify
  if (!empty($notify_text)) {

    //$commands[] = ajax_command_before('.comment-wrapper-' . $comment->cid, $notify_text);
    $commands[] = array(
      'command' => 'ajaxCommentsBefore',
      'selector' => '.comment-wrapper-' . $comment->cid,
      'html' => $notify_text,
    );
  }
  $output = array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
  return $output;
}

/**
 * Removes the comment.
 */
function ajax_comments_delete_js($form, &$form_state) {
  $comment = $form['#comment'];
  ajax_comments_remove_status();
  $notify_text = variable_get('ajax_comments_notify', '') ? theme('ajax_comments_notify_text', array(
    'type' => 'delete',
    'comment' => $comment,
  )) : '';
  if ($notify_text) {
    $commands[] = ajax_command_remove('.messages.status');
    $commands[] = ajax_command_remove('.messages.warning');
    $commands[] = ajax_command_replace('.comment-wrapper-' . $comment->cid, $notify_text);
    $commands[] = ajax_command_remove('a#comment-' . $comment->cid);
  }
  else {
    $commands[] = ajax_command_remove('.comment-wrapper-' . $comment->cid);
    $commands[] = ajax_command_remove('a#comment-' . $comment->cid);
  }

  // Remove all replies to deleted comment from page
  if (!empty($form_state['storage']['cids'])) {
    foreach ($form_state['storage']['cids'] as $cid) {
      $commands[] = ajax_command_remove('.comment-wrapper-' . $cid);
      $commands[] = ajax_command_remove('a#comment-' . $cid);
    }
  }
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Implements hook_comment_view_alter().
 */
function ajax_comments_comment_view_alter(&$build) {
  global $user;

  // Check
  if (ajax_comments_node_type_active($build['#node']->type)) {

    // A little HACK for do not mark as NEW own comments
    if (isset($build['#comment']->new) && $build['#comment']->uid == $user->uid && ($build['#comment']->new == MARK_NEW || $build['#comment']->new == MARK_UPDATED)) {
      $build['#comment']->new = MARK_READ;
    }

    // Reply.
    if (isset($build['links']['comment']['#links']['comment-reply'])) {
      $classes = array(
        'use-ajax-comments',
        'ajax-comments-reply',
        'ajax-comments-reply-' . $build['#comment']->nid . '-' . $build['#comment']->cid,
      );
      if (empty($build['links']['comment']['#links']['comment-reply']['attributes']['class'])) {
        $build['links']['comment']['#links']['comment-reply']['attributes']['class'] = $classes;
      }
      else {
        $build['links']['comment']['#links']['comment-reply']['attributes']['class'] = array_unique(array_merge($build['links']['comment']['#links']['comment-reply']['attributes']['class'], $classes));
      }
    }

    // Edit.
    if (isset($build['links']['comment']['#links']['comment-edit'])) {
      $classes = array(
        'use-ajax-comments',
        'ajax-comments-edit',
        'ajax-comments-edit-' . $build['#comment']->cid,
      );
      if (empty($build['links']['comment']['#links']['comment-edit']['attributes']['class'])) {
        $build['links']['comment']['#links']['comment-edit']['attributes']['class'] = $classes;
      }
      else {
        $build['links']['comment']['#links']['comment-edit']['attributes']['class'] = array_unique(array_merge($build['links']['comment']['#links']['comment-edit']['attributes']['class'], $classes));
      }
    }

    // Delete.
    if (isset($build['links']['comment']['#links']['comment-delete'])) {
      $classes = array(
        'use-ajax-comments',
        'ajax-comments-delete',
        'ajax-comments-delete-' . $build['#comment']->cid,
      );
      if (empty($build['links']['comment']['#links']['comment-delete']['attributes']['class'])) {
        $build['links']['comment']['#links']['comment-delete']['attributes']['class'] = $classes;
      }
      else {
        $build['links']['comment']['#links']['comment-delete']['attributes']['class'] = array_unique(array_merge($build['links']['comment']['#links']['comment-delete']['attributes']['class'], $classes));
      }
    }
  }
}

/**
 * Implements template_preprocess_comment()
 * Wrap comments and their replies in a #comment-wrapper-(cid) div
 */
function ajax_comments_preprocess_comment(&$variables) {
  $element = $variables['elements'];
  $comment = $element['#comment'];
  if (ajax_comments_node_type_active(substr($comment->node_type, strlen('comment_node_')))) {
    $variables['classes_array'][] = 'ajax-comment-wrapper';
    $variables['classes_array'][] = 'comment-wrapper-' . $comment->cid;
  }
}

/*
 * Implements template_preprocess_comment_wrapper()
 */
function ajax_comments_preprocess_comment_wrapper(&$variables) {
  $variables['classes_array'][] = 'comment-wrapper-nid-' . $variables['node']->nid;
  if (empty($variables['content']['comments']) && $variables['node']->comment == COMMENT_NODE_OPEN && ajax_comments_node_type_active($variables['node']->type)) {

    /**
     * A little HACK for adding dummy comment which give us right point to place first comment on the page.
     */
    $variables['content']['comments']['dummyComment'] = array(
      '#prefix' => '<div class="ajax-comment-wrapper ajax-comment-dummy-comment" style="display:none">',
      '#type' => 'item',
      '#markup' => '',
      '#suffix' => '</div>',
    );
  }
}

/**
 * Callback for clicking "reply".
 * Note: $pid is an optional parameter. This functionality is utilized by the
 * "Add new comment" link on pages where there is no default comment form
 * (comment_form_location is COMMENT_FORM_SEPARATE_PAGE)
 */
function ajax_comments_reply($node, $pid = 0, $flag = 0) {
  if (!user_access('post comments')) {
    return MENU_ACCESS_DENIED;
  }

  // If there is a pid this is a reply to a comment.
  if (!empty($pid)) {
    if (!user_access('access comments')) {
      return MENU_ACCESS_DENIED;
    }

    // Make sure the comment is valid and published.
    if (!($comments = comment_load_multiple(array(
      $pid,
    ), array(
      'status' => COMMENT_PUBLISHED,
    )))) {
      return MENU_NOT_FOUND;
    }
    $comment = $comments[$pid];

    // Make sure the comment belongs to this node.
    if ($comment->nid != $node->nid) {
      return MENU_NOT_FOUND;
    }
  }
  if (!user_is_anonymous() || user_is_anonymous() && variable_get('cache', 0) != 0) {

    // Authenticated user or anonymous and page cache is enabled
    $key = _ajax_comments_cache_key($node->nid, $pid);
    if ($cache = cache_get($key)) {

      // Get form from cache
      $form_build = $cache->data;
    }
    else {

      // Build form and Save to cache*/
      $form_build = drupal_get_form("comment_node_{$node->type}_form", (object) array(
        'nid' => $node->nid,
        'pid' => $pid,
      ), array(
        'flag' => $flag,
      ));
      cache_set($key, $form_build, 'cache', time() + 360);
    }
  }
  else {

    // Anonymous and page cache is disabled
    $form_build = drupal_get_form("comment_node_{$node->type}_form", (object) array(
      'nid' => $node->nid,
      'pid' => $pid,
    ), array(
      'flag' => $flag,
    ));
  }
  $form = trim(drupal_render($form_build));
  if (variable_get('ajax_comments_reply_autoclose') && !empty($pid)) {
    $commands[] = ajax_command_remove('.ajax-comments-form-reply');
    $commands[] = ajax_command_invoke('.ajax-comments-reply', 'show');
  }

  // Add the new form.
  if (!empty($pid)) {
    $mode = variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED);
    if (empty($mode)) {
      $commands[] = ajax_command_after('.comment-wrapper-' . $pid, $form);
    }
    else {

      // Add div with class "indented" if they are not exist
      $commands[] = array(
        'command' => 'ajaxCommentsAddDummyDivAfter',
        'selector' => '.comment-wrapper-' . $pid,
        'class' => 'indented',
      );

      // Check sort by comment_goodness.
      if (_ajax_comments_get_comment_sort_order($node) == 1) {

        // Newer first.
        $commands[] = ajax_command_append('.comment-wrapper-' . $pid . ' + .indented', $form);
      }
      else {

        // Older first.
        $commands[] = ajax_command_prepend('.comment-wrapper-' . $pid . ' + .indented', $form);
      }
    }
  }
  else {

    // Check Views Add form
    if (!empty($flag)) {
      $commands[] = ajax_command_after('.views-comment-wrapper-nid-' . $node->nid . ' > .ajax-comment-wrapper:last', $form);
    }
    else {

      // Check sort by comment_goodness.
      if (_ajax_comments_get_comment_sort_order($node) == 1) {

        // Older first. Append comment to last wrapper.
        $commands[] = ajax_command_after('.comment-wrapper-nid-' . $node->nid . ' > .ajax-comment-wrapper:last', $form);
      }
      else {

        // Newer first. Append comment to top.
        $commands[] = ajax_command_before('.comment-wrapper-nid-' . $node->nid . '> .ajax-comment-wrapper:first', $form);
      }
    }
  }

  // Hide reply to comment link
  $commands[] = ajax_command_invoke('.ajax-comments-reply-' . $node->nid . '-' . $pid, 'hide');
  if (!variable_get('ajax_comments_disable_scroll', 0)) {
    $commands[] = array(
      'command' => 'ajaxCommentsScrollToElement',
      'selector' => '.ajax-comments-reply-form-' . $node->nid . '-' . $pid . '-0',
    );
    $commands[] = ajax_command_invoke('.ajax-comments-reply-form-' . $node->nid . '-' . $pid . '-0 .form-textarea', 'focus');
  }
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Create the key for caching a comment form.
 */
function _ajax_comments_cache_key($nid, $pid) {
  $key = 'ajax_comments_reply_form-' . $nid . '-' . $pid;
  if (!user_is_anonymous()) {
    $key .= '-' . session_id();
  }
  else {
    $key .= '-' . ip_address();
  }
  return $key;
}

/**
 * Callback for clicking "edit".
 */
function ajax_comments_edit($comment) {
  $node = node_load($comment->nid);

  // Build form.
  $form_build = drupal_get_form("comment_node_{$node->type}_form", $comment);
  $form = drupal_render($form_build);

  // Remove anchor
  $commands[] = ajax_command_remove('a#comment-' . $comment->cid);

  // Replace comment with form.
  $commands[] = ajax_command_replace('.comment-wrapper-' . $comment->cid, $form);
  if (!variable_get('ajax_comments_disable_scroll', 0)) {
    $commands[] = array(
      'command' => 'ajaxCommentsScrollToElement',
      'selector' => '.ajax-comments-reply-form-' . $comment->nid . '-' . $comment->pid . '-' . $comment->cid,
    );
  }
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Callback for clicking "delete".
 */
function ajax_comments_delete($cid) {
  if (!($comment = comment_load($cid))) {
    return MENU_NOT_FOUND;
  }

  // Need to include comment module admin file for delete form.
  $form_state = array();
  $form_state['build_info']['args'] = array(
    $comment,
  );

  // Load this using form_load_include so it's cached properly and works in the
  // ajax callback.
  form_load_include($form_state, 'inc', 'comment', 'comment.admin');

  // Get child comments (replies on this comment)
  $form_state['storage']['cids'] = array();
  $query = db_select('comment', 'c');
  $query
    ->addField('c', 'cid');
  $query
    ->condition('c.nid', $comment->nid)
    ->condition('c.thread', substr($comment->thread, 0, -1) . '.%', 'LIKE')
    ->addTag('node_access');
  if (!user_access('administer comments')) {
    $query
      ->condition('c.status', COMMENT_PUBLISHED);
  }
  $query
    ->orderBy('c.cid', 'ASC');
  $cids = $query
    ->execute()
    ->fetchCol();

  // Save child comments ids if they are exist
  if (!empty($cids)) {
    $form_state['storage']['cids'] = $cids;
  }
  $form_build = drupal_build_form('comment_confirm_delete', $form_state);
  $form = drupal_render($form_build);

  // Hide contents.
  $commands[] = ajax_command_invoke('.comment-wrapper-' . $cid . ' >*', 'hide');

  // Put form inside main comment wrapper.
  $commands[] = ajax_command_prepend('.comment-wrapper-' . $cid, $form);
  return array(
    '#type' => 'ajax',
    '#commands' => $commands,
  );
}

/**
 * Removes "Your comment has been posted." or "Your comment has been queued.."
 *   from the status message.
 */
function ajax_comments_remove_status() {
  if (!empty($_SESSION['messages']['status'])) {
    $deleted = t('The comment and all its replies have been deleted.');
    $published = t('Your comment has been posted.');
    $not_published = t('Your comment has been queued for review by site administrators and will be published after approval.');
    foreach ($_SESSION['messages']['status'] as $key => $value) {
      if ($value == $published || $value == $not_published || ($value = $deleted)) {
        unset($_SESSION['messages']['status'][$key]);
      }
    }
    if (empty($_SESSION['messages']['status'])) {
      unset($_SESSION['messages']['status']);
      if (empty($_SESSION['messages'])) {
        unset($_SESSION['messages']);
      }
    }
  }
}

/**
 * Returns TRUE if this node uses ajax comments or if no nodes are selected.
 */
function ajax_comments_node_type_active($node_type) {
  $types = array_filter(variable_get('ajax_comments_node_types', array()));
  if (empty($types) || !empty($types[$node_type])) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Implements hook_theme().
 */
function ajax_comments_theme($existing, $type, $theme, $path) {
  return array(
    'ajax_comments_notify_text' => array(
      'variables' => array(
        'type' => NULL,
        'comment' => NULL,
      ),
    ),
  );
}

/**
 * Returns text to notify user their comment has been added.
 */
function theme_ajax_comments_notify_text($vars = array()) {
  $text = t('Your comment has been posted.');
  $status = 'status';

  // If the comment is unapproved, alter the message
  if (isset($vars['comment']) && !$vars['comment']->status) {
    $text = t('Your comment has been queued for review by site administrators and will be published after approval.');
    $status = 'warning';
  }
  if ($vars['type'] == 'delete') {
    $text = t('Your comment has been deleted');
  }
  elseif ($vars['type'] == 'preview') {
    $text = t('This is the preview for your comment. You must click SAVE or your comment will be lost.');
    $status = 'warning';
  }
  drupal_set_message($text, $status);
  return theme('status_messages');
}

/**
 * Returns comment sort order.
 *
 * comment_goodnes module and comment_sort_created compatibility:
 * 1 - Older first
 * 2 - Newer first
 */
function _ajax_comments_get_comment_sort_order($node) {
  $result =& drupal_static(__FUNCTION__);
  if (!isset($result)) {
    $result = 1;
    if (module_exists('comment_goodness')) {
      $result = variable_get('comment_default_sorting_' . $node->type, comment_goodness_OLDER_FIRST);
    }
    else {
      if (module_exists('comment_sort_created')) {
        $result = variable_get('comment_sort_created_order_' . $node->type) == COMMENT_SORT_CREATED_OLDER_FIRST ? 1 : 2;
      }
      else {
        if (module_exists('sort_comments')) {
          $result = variable_get('comment_default_sorting_' . $node->type, SORT_COMMENTS_OLDER_FIRST);
        }
      }
    }
  }
  return $result;
}

Functions

Namesort descending Description
ajax_comments_cancel_js Cancel edit the comment.
ajax_comments_comment_view_alter Implements hook_comment_view_alter().
ajax_comments_delete Callback for clicking "delete".
ajax_comments_delete_js Removes the comment.
ajax_comments_edit Callback for clicking "edit".
ajax_comments_edit_cancel_js Re-grabs comment after editing is cancelled.
ajax_comments_form_comment_confirm_delete_alter Implements hook_form_FORM_ID_alter().
ajax_comments_form_comment_form_alter Implements hook_form_FORM_ID_alter().
ajax_comments_menu Implements hook_menu().
ajax_comments_node_type_active Returns TRUE if this node uses ajax comments or if no nodes are selected.
ajax_comments_preprocess_comment Implements template_preprocess_comment() Wrap comments and their replies in a #comment-wrapper-(cid) div
ajax_comments_preprocess_comment_wrapper
ajax_comments_preprocess_node Implements hook_preprocess_node().
ajax_comments_preview_js Previews the comment.
ajax_comments_remove_status Removes "Your comment has been posted." or "Your comment has been queued.." from the status message.
ajax_comments_reply Callback for clicking "reply". Note: $pid is an optional parameter. This functionality is utilized by the "Add new comment" link on pages where there is no default comment form (comment_form_location is COMMENT_FORM_SEPARATE_PAGE)
ajax_comments_submit_js Builds the comment.
ajax_comments_theme Implements hook_theme().
ajax_comments_views_api Implements hook_views_api().
ajax_comments_views_data Implements hook_views_data().
theme_ajax_comments_notify_text Returns text to notify user their comment has been added.
_ajax_comments_cache_key Create the key for caching a comment form.
_ajax_comments_get_comment_sort_order Returns comment sort order.