Альтернатива комментариям Drupal: виджет ВКонтакте

В Drupal 8 возможности комментариев значительно расширились по сравнению с Drupal 7, но все же стандартные комментарии подойдут не для каждого проекта.

Если основное содержимое сайта предназначено для анонимных пользователей, то необходимость регистрации и авторизации для того, чтобы оставить комментарий, может существенно снизить желание пользователей их оставлять.

Даже социальная аутентификация тут не всегда помогает, так как в процессе пользователь все равно перенаправляется на другие страницы и может уже не вернуться.

Так же стандартная отправка комментария требует перезагрузки страницы, что не очень хорошо с точки зрения пользовательского опыта, хотя это решается такими модулями как AJAX Comments или React Comments (только Drupal 8).

Существует ряд сторонних сервисов для организации комментариев, в том числе с поддержкой социальной аутентификации, которые можно интегрировать с Drupal.

Самым популярным решением, пожалуй, является Disqus, который, кстати, используется на этом сайте. Но и у Disqus есть свои недостатки, как например, довольно большой объем передаваемых данных. Disqus хорошо известен среди опытных пользователей сети, но среди рядовых пользователей не так распространен. К тому же Disqus, как и практически все бесплатные сервисы, монетизируется за счет данных о пользователях, это тоже нравится не всем. Платные же сервисы не подходят для небольших или некоммерческих проектов.

Еще одной альтернативой могут служить виджеты комментариев от социальных сетей, например Facebook или ВКонтакте. Но и тут все не так радужно, и тоже есть свои ограничения.

Одного универсального решения для любых сценариев использования мне найти не удалось, возможно потому, что его не существует.

Но может это и не плохо, ведь разные инструменты могут быть хороши каждый для своей задачи.

Виджет комментариев ВКонтакте

В этой статье я бы хотел рассказать об опыте использования виджета комментариев ВКонтакте, который я установил на сайте сообщества настольной игры Шакал, поддержкой которого я занимаюсь.

Аудитория сайта — это, в основном, рядовые пользователи, молодежь, дети и их родители. При этом сайт тесно связан с группой ВКонтакте, поэтому использование именно этого виджета здесь было вполне логично.

Получение кода виджета комментариев ВКонтакте

Сначала нужно создать свое приложение на этой странице. После этого, можно получить код виджета на этой странице.

Процедура довольно простая, и не должна вызвать особых проблем, поэтому мы не будем на ней останавливаться.

Установка виджета комментариев ВКонтакте в Drupal

На сайте сообщества игры Шакал виджет комментариев ВКонтакте я установил довольно давно, когда сайт еще работал на Drupal 7. Кстати, благодаря переходу сайта на Drupal 8 мы рассмотрим примеры для обоих платформ. Принципиально процесс установки не отличается, но некоторые детали выполняются по-разному.

Код может немного отличаться в зависимости от настроек виджета, и состоит из двух частей.

Первая часть — внешний скрипт загрузки API и код инициализации приложения, которые надо поместить внутрь тега head:

<!-- Put this script tag to the <head> of your page -->
<script type="text/javascript" src="https://vk.com/js/api/openapi.js?160"></script>

<script type="text/javascript">
  VK.init({apiId: YOURAPPID, onlyWidgets: true});
</script>

Вторая часть — контейнер и код инициализации непосредственно виджета комментариев, который надо поместить в то место, где должен отображаться сам виджет:

<!-- Put this div tag to the place, where the Comments block will be -->
<div id="vk_comments"></div>
<script type="text/javascript">
VK.Widgets.Comments("vk_comments", {limit: 10, attach: "*"});
</script>

Реализация для Drupal 7

В Drupal 7 первую часть можно добавить в файл template.php в папке вашей темы оформления:

/**
 * Implements hook_preprocess_page().
 */
function YOURTHEME_preprocess_page(&$vars) {
  drupal_add_js('https://vk.com/js/api/openapi.js?160',
    array('type' => 'external', 'scope' => 'header', 'weight' => 99)
  );
  drupal_add_js("VK.init({apiId: YOURAPPID, onlyWidgets: true});", 
    array('type' => 'inline', 'scope' => 'header', 'weight' => 100)
  );
}

Реализация для Drupal 8

В Drupal 8 внешний скрипт можно объявить в качестве библиотеки в файле YOURTHEME.libraries.yml в папке вашей темы оформления:

vk-api:
  header: true
  js:
    https://vk.com/js/api/openapi.js?160: { type: external }

И подключить библиотеку в файле YOURTHEME.info.yml:

libraries:
  - YOURTHEME/vk-api

Код инициализации приложения рекомендуется включить непосредственно в шаблон перед закрывающим тегом head в файле templates/layout/html.html.twig. Если такого файла в вашей теме нет, то его нужно взять из базовой темы и скопировать в вашу.

  ...
  <head>
    ...
    <script>
      VK.init({apiId: YOURAPPID, onlyWidgets: true});
    </script>
  </head>
  ...

Вторую часть кода, как в Drupal 7, так и в Drupal 8, можно добавить в пользовательский блок. Для простоты можно сделать это через интерфейс (формат текста должен быть Full HTML, без визуального редактора), хотя по-хорошему это должен быть блок программно созданный в модуле. Этот блок нужно расположить где-нибудь после содержимого. Модуль комментариев из ядра, при этом, можно отключить.

На этом этапе комментарии ВКонтакте уже должны заработать, однако не лишним будет упомянуть о нескольких нюансах.

Виджет привязывается к URL страницы, на которой он вызывается. Если адрес страницы меняется, будьте готовы к тому, что пропадут и комментарии. Какой-либо возможности перенести комментарии с одной страницы на другую я не нашел. Поэтому лучше заранее правильно настроить все переадресации (например, с системных путей на синонимы) и сразу включить протокол https.

Виджет не имеет каких-либо настроек внешнего вида, а также никак не уведомляет о новых комментариях. Вторую проблему можно решить с помощью небольшой доработки, которую я оформил в виде дополнительного модуля.

Уведомление о новых сообщениях для виджета комментариев ВКонтакте

Виджет передает события при создании и удалении комментария, которые мы можем отлавливать и что-нибудь с этим делать. Для этого во вторую часть кода, после инициализации виджета, нужно добавить функцию-обработчик события, в нашем случае — это событие добавления комментария: widgets.comments.new_comment. В функцию передаются несколько параметров, в том числе дата и текст последнего комментария (подробнее в документации), а от себя мы передадим еще URL и заголовок страницы. Все это наша функция отправляет в виде GET запроса на заданный нами URL, в примере это newcomment.

<!-- Put this div tag to the place, where the Comments block will be -->
<div id="vk_comments"></div>
<script type="text/javascript">
VK.Widgets.Comments("vk_comments", {limit: 10, attach: "*"});
VK.Observer.subscribe("widgets.comments.new_comment", function f(num, last_comment, date, sign) {
  const Http = new XMLHttpRequest();
  var params = 'num=' + encodeURIComponent(num) + '&last=' + encodeURIComponent(last_comment) + '&date=' + encodeURIComponent(date) + '&hash=' + encodeURIComponent(sign) + '&url=' + encodeURIComponent(window.location) + '&title=' + encodeURIComponent(document.title);
  Http.open("GET", "https://YOURSITE.COM/newcomment?" + params, true);
  Http.send(null);
});
</script>

Теперь можно обработать этот запрос на стороне Drupal и уведомить об этом кого-нибудь. Я решил, что проще всего будет отправлять e-mail сообщение и создал свой модуль, который назвал comment_mailer.

Реализация для Drupal 7

В Drupal 7 нужно как минимум два файла: comment_mailer.info и comment_mailer.module. Текст первого я опущу, а вот второго все же приведу:

/**
 * Implements hook_mail().
 */
function comment_mailer_mail ($key, &$message, $params) {
  switch ($key) {
    case 'comment_mail':
      $message['to'] = 'YOUR@MAIL.COM';
      $message['subject'] = t('YOURSITE.COM: New comment created');
      // TODO: handle comment params
      $message['body'][] = t('No more information about this comment.');
      break;
  }
}

/**
 * Implements hook_menu().
 */
function comment_mailer_menu() {
  $items['newcomment'] = array(
    'title' => 'New comment',
    'page callback' => 'comment_mailer_page',
    'page arguments' => array(0),
    'access callback' => TRUE,
  );

  return $items;
}

/**
 * Page callback for /newcomment.
 */
function comment_mailer_page($args) {
  global $language;
  $params = $args;
  // TODO: get and pass comment params
  drupal_mail('comment_mailer', 'comment_mail', 'YOUR@MAIL.COM', $language, $params, $from = NULL, $send = TRUE);
  return $args;
}

Тут у нас три функции:

  • comment_mailer_mail: регистрирует параметры отправки e-mail сообщения;
  • comment_mailer_menu: регистрирует обработчик URL newcomment и вызывает для него функцию comment_mailer_page;
  • comment_mailer_page: отправляет e-mail сообщение с параметрами, определенными в функции comment_mailer_mail.

Да, я поленился и не реализовал получение параметров комментария и передачу их в текст письма. Но на самом деле, уведомления о факте отправки комментария уже достаточно, чтобы пойти и проверить их на сайте. Для администраторов виджета доступна возможность просмотра всех комментариев на одной странице, и с этим можно работать.

Скачать архив с модулем для Drupal 7:

Comment mailer 7.x (ZIP, 2 Кб)

Реализация для Drupal 8

В Drupal 8 я доработал функционал, и тут уже параметры комментария передаются в сообщение. Код будет распределен по трем файлам, не считая comment_mailer.info.yml, содержимое которого я опущу.

Регистрируем параметры отправки сообщения в файле comment_mailer.module:

/**
 * Implements hook_mail().
 */
function comment_mailer_mail($key, &$message, $params) {
  switch ($key) {
    case 'new_comment':
      $message['from'] = \Drupal::config('system.site')->get('mail');
      $message['subject'] = t('YOURSITE.COM: New comment created');
      $message['body'][] = $params['message'];
      break;
  }
}

Регистрируем обработчик URL newcomment в файле comment_mailer.routing.yml:

comment_mailer.content:
  path: '/newcomment' 
  defaults: 
    _controller: '\Drupal\comment_mailer\Controller\NewCommentController::content' 
    _title: 'New comment'
  requirements: 
    _permission: 'access content' 

Для обработки нашего URL создадим свой контроллер в файле src/Controller/NewCommentController.php:

namespace Drupal\comment_mailer\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * NewCommentController controller.
 */
class NewCommentController extends ControllerBase {

  /**
   * Returns a render-able array for the page.
   */
  public function content() {

    // getting comment params
    $page_request = \Drupal::request();
    $values['num'] = $page_request->query->get('num');
    $values['last'] = $page_request->query->get('last');
    $values['date'] = $page_request->query->get('date');
    $values['hash'] = $page_request->query->get('hash');
    $values['url'] = $page_request->query->get('url');
    $values['title'] = $page_request->query->get('title');

    // prepare params for human
    $data = '';
    foreach ($values as $key => $value) {
      if ($value) {
        $data .= $key . ': ' . $value . "<br>";
      }
    }

    // handling mail sending
    $mailManager = \Drupal::service('plugin.manager.mail');

    $module = 'comment_mailer';
    $key = 'new_comment';
    $to = 'YOUR@MAIL.COM';
    $params['message'] = $data;
    $langcode = \Drupal::currentUser()->getPreferredLangcode();
    $send = true;

    $result = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);

    // add send result to data
    $data .= 'send_result' . ': ' . $result['result'] . "<br>";

    // add log record
    \Drupal::logger('comment_mailer')->notice($data);

    $build = [
      '#markup' => $data,
    ];
    return $build;
  }

}

В контроллере мы получаем параметры комментария из GET запроса, немного прихорашиваем их, и передаем в качестве текста сообщения. Затем эти же данные, но уже включая результат отправки сообщения, записываются в журнал, а также передаются в рендер-массив вывода страницы. По идее, пользователь не должен заходить и видеть эту страницу, но в процессе разработки модуля это было полезным.

Скачать архив с модулем для Drupal 8:

Comment mailer 8.x (ZIP, 4 Кб)

Не могу сказать, что модуль получился каким-то выдающимся, можно было бы реализовать интерфейс настройки, хотя бы того же адреса получателя. Или даже совместить его с кодом добавления виджета и добавить программную генерацию блока комментариев. Но лучшее, как известно, враг хорошего. И повторюсь, с этим можно работать.

Вместо итога

Казалось бы, задача решена, но на деле мне пришлось столкнуться с еще одной проблемой.

Если вы все настроили, но не получаете уведомлений, то первым делом проверьте настроена ли вообще отправка почты на хостинге. В модуле используются стандартный механизм Drupal, так что можно, например, запросить восстановление пароля и посмотреть придет ли письмо.

Если письмо пришло, значит почта от сайта ходит, и тогда нужно проверить папку спам вашего ящика. Gmail, например, несмотря на все мои уговоры и добавления отправителя в контакты, все равно продолжал помещать письма в спам, пока я не настроил отдельный фильтр, чтобы он этого не делал.

Видимо дело в том, что в письме присутствует ссылка, а может и в чем-то другом, но это уже совсем другая история...