Ajax_comments и форма комментариев выше комментариев

Есть замечательный модуль Ajax_comments от neochief, который позволяет открывать форму комментария непосредственно под комментарием (посредством AJAX). И есть статья этого-же (кстати многоуважаемого лично мной) автора про перенос формы ввода комментария непосредственно под сам пост, перед списком остальных сообщений - Модуль «Форма комментариев поверх комментов» и даже есть модуль который все это реализует - Comment form above comments. Но есть один нюанс - не дружат эти модули между собой (по крайней мере у меня не получилось их подружить напрямую).
Просматривая исходные коды данных модулей в надежде их все таки подружить, я заметил что в принципе метод предложенный neochief, а именно подменой темы на некотором этапе рендеринга, на свою не единственный (опять же гибкость Drupal в этом плане меня поражает).
Поэтому хочу предложить свой метод, основанный на хуке hook_menu_alter, ведь в принципе ни кто нам не запрещает полностью переопределить вывод ноды подменив стандартный коллбэк «node/%node».

Дальше я заранее извиняюсь, что позаимствовал часть кода из модуля «Форма комментариев поверх комментов» в плане hook_form_alter.
Итак определяем новый модуль (назвал я его acomment)
создаем папку sites/all/modules/accoment

в нем файл acomment.info

; $Id$
name = Comment form above comments
description = Adds option to place comment form above comments.
dependencies[] = comment
core = 6.x

далее, в той же папке создадим файл acomment.module
Основная идея данного модуля, подменить стандартный вывод через node_page_view своим, и в нем проверить как настроен вывод комментария у данного типа ноды, если это стандартный вывод COMMENT_FORM_SEPARATE_PAGE или COMMENT_FORM_BELOW то вызываем стандартный node_show, если же наш новый тип вывода COMMENT_FORM_ABOVE, то вызовем свой обработчик acomment_node_show.
В принципе ничего интересного в самом коде нет, все функции дублируют аналогичные функции из стандартных модулей node и comment, единственное отличие функции
acomment_comment_render от comment_render из модуля comment - это замена строки
$output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
на
$output = comment_form_box(array('nid' => $nid), t('Post new comment')) . $output;
где мы собственно и переносим форму ввода комментария, сразу после ноды.

<?php
// $Id$
/**
 * @file
 * Adds option to place comment form above comments.
 */

define('COMMENT_FORM_ABOVE', 2);

/**
 * Implementation of hook_form_alter().
 */

function acomment_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
    $form['comment']['comment_form_location'] = array(
      '#type' => 'radios',
      '#title' => t('Location of comment submission form'),
      '#default_value' => variable_get('comment_form_location_'. $form['#node_type']->type, COMMENT_FORM_SEPARATE_PAGE),
      '#options' => array(
        COMMENT_FORM_SEPARATE_PAGE => t('Display on separate page'),
        COMMENT_FORM_BELOW => t('Display below post or comments'),
        COMMENT_FORM_ABOVE => t('Display below post, but before comments'),
      ),
    );
  }
}

function acomment_menu_alter(&$items) {
  $items['node/%node']['page callback'] = 'acomment_node_page_view';
}


function acomment_node_page_view($node, $cid = NULL) {
  drupal_set_title(check_plain($node->title));
  if (variable_get('comment_form_location_' . $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_ABOVE) {
    $output = acomment_node_show($node, $cid);
  }
  else {
    $output = node_show($node, $cid);
  }
  return $output;
}


function acomment_node_show($node, $cid = NULL) {
  $output = node_view($node, FALSE, TRUE);
  if ($node->comment) {
    $output .= acomment_comment_render($node, $cid);
  }
  node_tag_new($node->nid);
  return $output;
}


function acomment_comment_render($node, $cid = 0) {
  global $user;

  $output = '';

  if (user_access('access comments')) {
    // Pre-process variables.
    $nid = $node->nid;
    if (empty($nid)) {
      $nid = 0;
    }

    $mode = _comment_get_display_setting('mode', $node);
    $order = _comment_get_display_setting('sort', $node);
    $comments_per_page = _comment_get_display_setting('comments_per_page', $node);
    if ($cid && is_numeric($cid)) {
      // Single comment view.
      $query = 'SELECT c.cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.signature_format, u.picture, u.data, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.cid = %d';
      $query_args = array($cid);
      if (!user_access('administer comments')) {
        $query .= ' AND c.status = %d';
        $query_args[] = COMMENT_PUBLISHED;
      }

      $query = db_rewrite_sql($query, 'c', 'cid');
      $result = db_query($query, $query_args);

      if ($comment = db_fetch_object($result)) {
        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
        $links = module_invoke_all('link', 'comment', $comment, 1);
        drupal_alter('link', $links, $node, $comment);
        $output .= theme('comment_view', $comment, $node, $links);
      }
    }
    else {
      // Multiple comment view
      $query_count = 'SELECT COUNT(*) FROM {comments} c WHERE c.nid = %d';
      $query = 'SELECT c.cid as cid, c.pid, c.nid, c.subject, c.comment, c.format, c.timestamp, c.name, c.mail, c.homepage, u.uid, u.name AS registered_name, u.signature, u.signature_format, u.picture, u.data, c.thread, c.status FROM {comments} c INNER JOIN {users} u ON c.uid = u.uid WHERE c.nid = %d';

      $query_args = array($nid);
      if (!user_access('administer comments')) {
        $query .= ' AND c.status = %d';
        $query_count .= ' AND c.status = %d';
        $query_args[] = COMMENT_PUBLISHED;
      }

      if ($order == COMMENT_ORDER_NEWEST_FIRST) {
        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
          $query .= ' ORDER BY c.cid DESC';
        }
        else {
          $query .= ' ORDER BY c.thread DESC';
        }
      }
      elseif ($order == COMMENT_ORDER_OLDEST_FIRST) {
        if ($mode == COMMENT_MODE_FLAT_COLLAPSED || $mode == COMMENT_MODE_FLAT_EXPANDED) {
          $query .= ' ORDER BY c.cid';
        }
        else {
          // See comment above. Analysis reveals that this doesn't cost too
          // much. It scales much much better than having the whole comment
          // structure.
          $query .= ' ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))';
        }
      }
      $query = db_rewrite_sql($query, 'c', 'cid');
      $query_count = db_rewrite_sql($query_count, 'c', 'cid');

      // Start a form, for use with comment control.
      $result = pager_query($query, $comments_per_page, 0, $query_count, $query_args);

      $divs = 0;
      $num_rows = FALSE;
      $comments = '';
      drupal_add_css(drupal_get_path('module', 'comment') .'/comment.css');
      while ($comment = db_fetch_object($result)) {
        $comment = drupal_unpack($comment);
        $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
        $comment->depth = count(explode('.', $comment->thread)) - 1;

        if ($mode == COMMENT_MODE_THREADED_COLLAPSED || $mode == COMMENT_MODE_THREADED_EXPANDED) {
          if ($comment->depth > $divs) {
            $divs++;
            $comments .= '<div class="indented">';
          }
          else {
            while ($comment->depth < $divs) {
              $divs--;
              $comments .= '</div>';
            }
          }
        }

        if ($mode == COMMENT_MODE_FLAT_COLLAPSED) {
          $comments .= theme('comment_flat_collapsed', $comment, $node);
        }
        elseif ($mode == COMMENT_MODE_FLAT_EXPANDED) {
          $comments .= theme('comment_flat_expanded', $comment, $node);
        }
        elseif ($mode == COMMENT_MODE_THREADED_COLLAPSED) {
          $comments .= theme('comment_thread_collapsed', $comment, $node);
        }
        elseif ($mode == COMMENT_MODE_THREADED_EXPANDED) {
          $comments .= theme('comment_thread_expanded', $comment, $node);
        }

        $num_rows = TRUE;
      }
      while ($divs-- > 0) {
        $comments .= '</div>';
      }

      $comment_controls = variable_get('comment_controls_'. $node->type, COMMENT_CONTROLS_HIDDEN);
      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_ABOVE || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
      }

      $output .= $comments;
      $output .= theme('pager', NULL, $comments_per_page, 0);

      if ($num_rows && ($comment_controls == COMMENT_CONTROLS_BELOW || $comment_controls == COMMENT_CONTROLS_ABOVE_BELOW)) {
        $output .= drupal_get_form('comment_controls', $mode, $order, $comments_per_page);
      }
    }

    // If enabled, show new comment form if it's not already being displayed.
    $reply = arg(0) == 'comment' && arg(1) == 'reply';
    if (user_access('post comments') && node_comment_mode($nid) == COMMENT_NODE_READ_WRITE && (variable_get('comment_form_location_'. $node->type, COMMENT_FORM_SEPARATE_PAGE) == COMMENT_FORM_ABOVE) && !$reply) {
      $output = comment_form_box(array('nid' => $nid), t('Post new comment')) . $output;
    }

    if ($output) {
      $output = theme('comment_wrapper', $output, $node);
    }
  }

  return $output;
}

Ну и далее что бы оформить модуль подобающим образом (и прибраться за собой если модуль удаляют) создадим файл acomment.install
Ну здесь все просто, если модуль удаляют и у каких-либо типов нод вид вывода комментария равен COMMENT_FORM_ABOVE мы им назначаем вывод комментариев вида COMMENT_FORM_BELOW.
<?php
// $Id$

/**
 * @file
 * Install acomment module.
 */


/**
 * Implementation of hook_install(). We need to execute our module after
 * others to prevent some glitches caused by $node->comment = NULL set in
 * hook_preprocess_node().
 */

function acomment_install() {
  db_query("UPDATE {system} SET weight = 10 WHERE name = 'acomment'");
}
/**
 * Implementation of hook_uninstall().
*/

function acomment_uninstall() {
  $types = node_get_types('types');
  foreach ($types as $key => $value) {
    if (variable_get('comment_form_location_' . $key, COMMENT_FORM_SEPARATE_PAGE ) == COMMENT_FORM_ABOVE) {
      variable_set('comment_form_location_' . $key, COMMENT_FORM_BELOW);
    }
  }
}

Ну вроде все, только хочется добавить, что данный модуль отлично (по крайней мере у меня) «дружит» с ajax_comments (еще раз респект и уважуха :) neochief).
Ну и как это примерно выглядит, можно посмотреть например в ветке форума моего нового проекта Loros Studio

Ну и прикреплю архивчик с данным модулем, потесируйте если будет интерес выложу на drupal.org.

ПредпросмотрВложениеРазмер
acomment.zip4.33 кб
 

Комментарии

А на самом деле у neochief модули "дружат", только странным образом :) Если отметить галочкой поле Показывать на отдельной странице, то показывается ниже сообщения, но выше комментариев :))) У вас же, всё правильно. У neochief в случае, если отметить опцию "Показывать ниже сообщения, но выше комментариев", то он - да, показывает, но форма всегда раскрыта.