Последние комментарии в сайдбаре Hugo
Комментарии на Disqus, а сайт статический. Хочется показать последние комментарии в сайдбаре, но Disqus не предоставляет такого виджета. Но комментарии можно вытащить через API и отрендерить самому.
Получаем API ключ
Для работы виджета нужен публичный ключ Disqus API. Получаем его так:
- Идём на disqus.com/api/applications/register
- Заполняем форму — название приложения, описание, сайт. Для виджета последних комментариев подходит любой вариант, включая бесплатный
- После регистрации ключи будут доступны на disqus.com/api/applications — там два: Public API Key и Secret API Key. Нам нужен Public API Key
- Важно: без подписки 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:
[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):
{{- partial "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 }}— сколько комментариев запрашивать, по умолчанию 5callback=disqusLastComments— имя функции, которую Disqus вызовет с данными
Значение флага useWordBoundary (true/false, из конфига truncateByWord) стараюсь не вставлять прямо в массив [30, ...] — Hugo-плейсхолдер {{ .Site.Params.truncateByWord }} внутри JS-литерала невалиден до сборки и сбивает редактор/линтер. Поэтому заранее выношу его в переменную truncateByWord в начале скрипта, а в truncate() передаю уже имя переменной. На готовую страницу это не влияет — Hugo рендерит строку var truncateByWord = true;, в продакшене получается обычный валидный JS.
Смысл флага: true — режет обрезанную строку до последнего пробела, чтобы текст не обрывался посреди слова; false — режет как есть, даже если это середина слова.
Результат
В сайдбаре появился блок «Последние комментарии» — компактный список ссылок на статьи с последними комментариями. Заголовок локализован через i18n.