ShvetsGroup

 

Модуль «Форма комментариев поверх комментов»

  • neochief's picture
0 comments

Модуль «Форма комментариев поверх комментов»

Статья эвакуирована с DrupalDance.com

Давеча создал небольшой модуль для решения распространенной проблемы расположения формы коментариев поверх ветки самих комментариев. Модуль полезен, если вы хотите сделать аналог «стены» ВКонтакте или такую форму как на last.fm.

Ссылка на модуль — Comment form above comments.

Если вы не разработчик, дальше читать не обязательно.

Особенности «вклинивания» в модуль комментариев

Обход comment_render()

Дописывать что-то к комментам шестого друпала вещь абсолютно неблагодарная, так как все упирается в хардкорный вызов comment_render() в node_show(), на который повлиять никак нельзя, он будет вызван в любом случае, покуда модуль комментариев включен, а у типа контента включено комментирование:

function node_show($node, $cid, $message = FALSE) {
  ...
  // здесь вызываются все хуки и происходит темизация ноды,
  $output = node_view($node, FALSE, TRUE);

  // а на этот вызов мы уже не можем повлиять...
  if (function_exists('comment_render') && $node->comment) {
    $output .= comment_render($node, $cid);
  }
  
  // ...и все уходит на экран как есть.
  return $output;
}

Но все же одна лазейка есть. Нам стоит лишь обнулить $node->comment (что будет эмулировать запрещение комментариев к материалу) и стандартный вывод комментов прекратится.

Как это сделать? Смотрим чуть выше — там есть вызов node_view(), который во-первых вызывает уйму хуков для ноды, а во-вторых темизирует окончательный ее вид. Поэтому, создаем свой модуль, вклиниваемся в этот процесс и устанавливаем $node->comment в ноль. А чтобы минимизировать риски вмешательства в объект ноды, мы делаем вес нашего модуля заоблачным, чтобы он отрабатывал после всех.

Стандартный коменты вырублены, остается вывести где-то в нашем шаблоне свою реализацию ветки комментов. Вот так примерно будет выглядеть весь код:

function mymodule_preprocess_node(&$vars) {
  $node = $vars['node'];
  // добавляем в шаблон ноды переменную с содержимым ветки комментов
  $vars['comments'] = custom_comments_render($node);
  // запрещаем стандартный вывод
  $node->comment = NULL;
}

Таким образом и реализован модуль i18ncomments, который подменяет стандартный coment_render() своим собственным.

Обход функций темизации

Однако с модулем «Форма поверх комментов» все оказалось сложнее, так как проблема была уже внутри comment_render():

...
// Нам надо заменить этот участок таким образом, чтобы форма вставлялась перед веткой, а не добавлялась к ней.
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_BELOW) && !$reply) {
  $output .= comment_form_box(array('nid' => $nid), t('Post new comment'));
}

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

Если бы мы могли как-нибудь запретить первый вызов и переопределить theme_comment_wrapper(), вставив нашу форму в самом начале $output, все бы срослось.

Первая проблема решается вмешательством в глобальную переменную $conf, которая служит буфером между базой данных и variable_get(). Вот как это будет выглядеть:

// comment_render() вызывается уже после препроцессинга ноды, поэтому менять следует именно здесь
function mymodule_preprocess_node(&$vars) {
  $node = $vars['node'];

  global $conf;
  $conf['comment_form_location_'. $node->type] = COMMENT_FORM_SEPARATE_PAGE;
}

Осталась вторая проблема. Но мы не можем переопределить функцию темизации из модуля, а по-другому ничего не выйдет. Что же делать?

На помощь приходит умение работы с регистром темизации Друпала:

/**
 * Реализация hook_theme_registry_alter().
 */
function mymodule_theme_registry_alter(&$theme_registry) {
  // Телодвижения с регистром стоит выполнять только если темизация производится в функции, а не через шаблон
  if (isset($theme_registry['comment_wrapper']['function']) && function_exists($theme_registry['comment_wrapper']['function'])) {
    // Сохраняем название конечной функции на будущее.
    variable_set('comment_wrapper_function', $theme_registry['comment_wrapper']['function']);
    // Заменяем своей.
    $theme_registry['comment_wrapper']['function'] = 'mymodule_comment_wrapper';
  }
}

// Теперь при вызове theme('comment_wrapper') будет вызван следующий код.
function mymodule_comment_wrapper($content, $node) {
  // Получаем форму комментов.
  $output = comment_form_box(array('nid' => $node->nid), t('Post new comment'));
  // Получаем название конечной функции темизации, полученной из регистра (мы же не хотим испохаюить темизацию).
  $comment_wrapper_function = variable_get('comment_wrapper_function', 'theme_comment_wrapper');
  // Если эта функция существует, значит вызываем ее, но спереди вставляем нашу форму.
  if (function_exists($comment_wrapper_function)) {
    $output .= $comment_wrapper_function($content, $node);
    return $output;
  }
}

Got anything to add?