Windows, Linux, КПК и немного ещё.

Комментарии на Disqus, а сайт статический. Хочется показать последние комментарии в сайдбаре, но Disqus не предоставляет такого виджета. Но комментарии можно вытащить через API и отрендерить самому.


Получаем API ключ

Для работы виджета нужен публичный ключ Disqus API. Получаем его так:

  1. Идём на disqus.com/api/applications/register
  2. Заполняем форму — название приложения, описание, сайт. Для виджета последних комментариев подходит любой вариант, включая бесплатный
  3. После регистрации ключи будут доступны на disqus.com/api/applications — там два: Public API Key и Secret API Key. Нам нужен Public API Key
  4. Важно: без подписки Pro лимит API — 1000 запросов в день. Для виджета на нескольких страницах этого более чем достаточно

Подробнее: How to create an API application


API

Disqus API имеет эндпоинт forums/listPosts.jsonp. Выглядит так:

https://disqus.com/api/3.0/forums/listPosts.jsonp?api_key=КЛЮЧ&forum=ШОРТНЕЙМ&limit=7&related=thread&callback=имяФункции

api_key — публичный ключ, берётся из настроек Disqus API. Настройки вынес в config.toml:

toml
[params]
  # ключ API Disqus для виджета последних комментариев в сайдбаре
  disqusApi = 'КЛЮЧ'
  # сколько комментариев показывать
  LastComments = 7
  # обрезать текст комментария по границе слов
  truncateByWord = true

Почему JSONP, а не fetch? Потому что Disqus блокирует CORS-запросы — сервер не отдаёт заголовок Access-Control-Allow-Origin. JSONP обходит это: создаём <script> с callback=, Disqus оборачивает ответ в вызов функции, браузер исполняет.


Partial

Вся логика в одном файле — layouts/partials/last-comments.html.

Подключаю в сайдбаре (layouts/partials/sidebar.html):

sidebar.html
{{- partial "last-comments.html" . -}}
last-comments.html
<section class="sidebar-module">
    <h4>{{ i18n "last_comments" }}</h4>
    <ol class="list-unstyled" id="comment_widget_js">
    </ol>

    <script>
        (function () {
            var truncateByWord = {{ .Site.Params.truncateByWord }};

            function truncate(n, useWordBoundary) {
                if (this.length <= n) { return this; }
                var subString = this.substr(0, n - 1);
                return (useWordBoundary
                    ? subString.substr(0, subString.lastIndexOf(' '))
                    : subString) + "\u2026";
            }

            window.disqusLastComments = function (commentsresult) {
                var ol = document.getElementById('comment_widget_js');
                for (var i in commentsresult.response) {
                    var a = document.createElement('a');
                    a.href = commentsresult.response[i].url;
                    a.textContent = truncate.apply(
                        commentsresult.response[i].raw_message, [30, truncateByWord]
                    );
                    var li = document.createElement('li');
                    li.appendChild(a);
                    ol.appendChild(li);
                }
            };

            var script = document.createElement('script');
script.src = "https://disqus.com/api/3.0/forums/listPosts.jsonp?api_key={{ .Site.Params.disqusApi }}&forum={{ .Site.Config.Services.Disqus.Shortname }}&limit={{ default 5 .Site.Params.LastComments }}&related=thread&callback=disqusLastComments";
document.body.appendChild(script);
        })();
    </script>
</section>
  • disqusLastComments — callback, который вызовет Disqus API. Принимает объект commentsresult, внутри response — массив комментариев
  • truncate() — обрезает текст комментария до 30 слов, \u2026 — многоточие
  • a.href — ссылка на страницу, где опубликован комментарий
  • a.textContent — текст комментария (обрезанный)
  • limit={{ default 5 .Site.Params.LastComments }} — сколько комментариев запрашивать, по умолчанию 5
  • callback=disqusLastComments — имя функции, которую Disqus вызовет с данными

Значение флага useWordBoundary (true/false, из конфига truncateByWord) стараюсь не вставлять прямо в массив [30, ...] — Hugo-плейсхолдер {{ .Site.Params.truncateByWord }} внутри JS-литерала невалиден до сборки и сбивает редактор/линтер. Поэтому заранее выношу его в переменную truncateByWord в начале скрипта, а в truncate() передаю уже имя переменной. На готовую страницу это не влияет — Hugo рендерит строку var truncateByWord = true;, в продакшене получается обычный валидный JS.

Смысл флага: true — режет обрезанную строку до последнего пробела, чтобы текст не обрывался посреди слова; false — режет как есть, даже если это середина слова.


Результат

В сайдбаре появился блок «Последние комментарии» — компактный список ссылок на статьи с последними комментариями. Заголовок локализован через i18n.