Встраивание списка тем Discourse на другой сайт

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

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

Как встроить список тем

Сначала необходимо включить настройку сайта embed topics list.

Затем в вашем HTML добавьте тег <script>, содержащий JavaScript, необходимый для встраивания тем Discourse. Вы можете добавить его туда, где обычно размещаете скрипты. Например:

<script src="http://URL/javascripts/embed-topics.js"></script>

Замените URL на адрес форума, включая подпапку, если она существует.

После этого в теге <body> вашего HTML-документа добавьте тег d-topics-list, чтобы указать список тем, который вы хотите встроить. Здесь также нужно заменить URL на базовый URL вашего сайта:

<d-topics-list discourse-url="URL" category="1234" per-page="5"></d-topics-list>

Любые предоставленные вами атрибуты (кроме обязательного discourse-url) будут преобразованы в параметры запроса для поиска тем. Таким образом, если вы хотите искать темы по тегу, вы можете сделать следующее:

<d-topics-list discourse-url="URL" tags="cool"></d-topics-list>

Если параметр запроса содержит подчеркивания, замените их дефисами. В приведенном выше примере вы, вероятно, заметили, что per_page стало per-page.

В контекстах SameSite (т. е. когда встраиваемый сайт и ваш форум имеют общий родительский домен) Discourse определит, авторизованы ли вы на форуме, и отобразит результаты соответствующим образом. Не удивляйтесь, если вы увидите защищённые категории и тому подобное при входе в систему — анонимные пользователи не смогут этого сделать!

Список параметров

template: complete или basic (по умолчанию). Пока basic представляет собой просто список заголовков тем, complete включает заголовок, имя пользователя, аватар пользователя, дату создания и миниатюру темы.

per-page: Число. Определяет количество возвращаемых тем.

category: Число. Ограничивает темы одной категорией. Передайте id целевой категории.

allow-create: Булево значение. Если включено, во встроенном виджете появится кнопка «Новая тема».

tags: Строка. Ограничивает темы теми, которые связаны с этим тегом.

top_period: Одно из значений all, yearly, quarterly, monthly, weekly, daily. Если включено, будут возвращены «Лучшие» темы за указанный период.

Примеры

Я создал пример сайта здесь:

https://embed.eviltrout.com

Вы можете просмотреть исходный код в браузере, чтобы увидеть код, но весь исходный код также доступен на GitHub:

Это совершенно новая функция, поэтому любые отзывы и предложения будут очень полезны.

Стилизация списка

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

Например, по умолчанию наш встроенный список с использованием шаблона complete выглядит так:

Если вы хотите, чтобы он выглядел, например, как сетка, вы можете добавить пользовательский SCSS в Раздел > Общий > Встроенный CSS:

.topics-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  
  .topic-list-item { 
    .main-link {
      border: 1px dotted gray;
      padding: 0;
    }
  
    .topic-column-wrapper {
      flex-direction: column-reverse;
      
      .topic-column.details-column {
        width: 100%;
      }
        
      .topic-column.featured-image-column .topic-featured-image img {
        max-width: initial;
        max-height: initial;
        width: 100%;
      }
    }
  }
}

Это сделает его похожим на следующее:

95 лайков

Привет, ребята! Мы хотим, чтобы ссылки открывались в новой вкладке (то есть target=“_blank” вместо target=“_parent”). Есть ли простой способ сделать это с учетом iframe?

2 лайка

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

// Для `fetch` может потребоваться полифилл
const targetEl = document.getElementById("forumTopics");

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</a></li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// Добавляем `.json` к любому списку тем
fetch("https://forum.example.com/latest.json")
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(1, 6)
            .map((t) => [
                t.title,
                `https://forum.example.com/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });
3 лайка

Спасибо! Мне нравится эта идея, но, исходя из этого поста What are the risks of enabling Cross-origin resource sharing (DISCOURSE_ENABLE_CORS), я думаю, что включение CORS — не лучшая идея.

2 лайка

Включать CORS следует только для доверенных сайтов. Включение CORS для всех источников сводит его назначение на нет.

6 лайков

Я включил CORS только для других сайтов, которыми владею. Цель заключалась в том, чтобы загружать список тем форума на наших других сайтах с помощью фронтенд-кода. Этот метод, вероятно, не подойдёт для того, чтобы случайные сайты могли встраивать посты.

Редактирование: если вы копируете/вставляете этот код, будьте осторожны с .slice(1, 6), так как это удаляет одну из тем. (Кажется, это была закреплённая тема, которую я хотел убрать.)

3 лайка

Круто, это можно попробовать. У меня сейчас нет сайта, кроме localhost (в период тестирования), поэтому я колебался добавлять его. Но если я найду кого-то, у кого есть подходящее место, мы сможем попробовать. Спасибо за помощь!

4 лайка

Привет, @j127

Мы сейчас столкнулись с той же проблемой и хотим открывать ссылки в новой вкладке.
Всего один быстрый вопрос.

Где именно вы размещаете код, который вы предоставили?

@eviltrout, есть ли у вас идеи, почему в моём случае стили CSS не отображаются?

2 лайка

Этот код — просто краткий пример общей идеи. Я не думаю, что fetch работает во всех браузерах. Если хочешь, я могу переписать его на ES5.

Твой сайт — это WordPress или какая-то версия headless WordPress?

4 лайка

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

4 лайка

Мой сайт — обычный WordPress, а не headless.

1 лайк

Можно ли использовать это для встраивания списка тем Discourse в другой экземпляр Discourse? Или есть более умный способ это сделать?

Мой сценарий использования — создание «моста» между двумя экземплярами в качестве временной меры до их объединения в будущем. Было бы здорово, если бы это можно было автоматизировать и сделать динамическим.

2 лайка

Если вам не важен IE, то этот фрагмент должен работать. (В настройках Discourse необходимо включить CORS для внешнего домена.)

Для проверки откройте любую страницу этого форума и вставьте приведенный ниже фрагмент в консоль браузера. Я изменил целевой элемент на document.body, поэтому он заменит всю страницу списком тем форума. Для реального сайта просто измените целевой элемент на document.getElementById("#someId"), а затем добавьте на страницу div с этим ID. Скрипт заполнит его списком тем.

// `fetch` может потребовать полифилл, если вам не важен IE
// const targetEl = document.getElementById("forumTopics");

// это заменяет body страницы, просто в качестве примера
const targetEl = document.body;

// вставьте сюда URL вашего форума, чтобы протестировать на собственном форуме
const baseForumURL = `https://meta.discourse.org`;

// количество тем, которые вы хотите отобразить
const numTopics = 6;

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</a></li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// добавьте `.json` к любому списку тем
fetch(`${baseForumURL}/latest.json`)
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(0, numTopics)
            .map((t) => [
                t.title,
                `${baseForumURL}/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });

Пример: если целевой div где-то на вашем сайте WP выглядит так

<div id="forumTopics">
    <!-- здесь появятся сообщения форума -->
</div>

то JavaScript может обратиться к этому ID следующим образом:

// найдите эту строку в исходном примере, который я опубликовал
const targetEl = document.getElementById("forumTopics");
4 лайка

Вы имеете в виду код из первого поста или фрагмент из поста 50? И нет, мне не важен IE. Нет нужды подстраиваться под этот динозавр :).
В моём случае CSS не загружается для моего фрагмента ни в одном браузере.

Значит, это JavaScript, и при реализации в WordPress мне нужно обернуть его в тег <script>, верно?

2 лайка

Прошло уже довольно много времени с тех пор, как я пользовался WordPress. Вы добавляете это в HTML темы? Если да, то оберните мой код в тег script и разместите <div> там, где вы хотите, чтобы появлялись посты.

Думаю, вы можете просто вставить код ниже в HTML шаблона WordPress в то место, где хотите видеть посты. Обязательно обновите строку с URL форума. Если это не сработает, дайте мне знать. (Код не тестировался.)

Нажмите здесь, чтобы получить код
<div id="forumTopics"></div>
<script>
// вставьте URL вашего форума здесь для проверки на вашем собственном форуме
const baseForumURL = `https://forum.example.com`;

// сколько тем вы хотите показать
const numTopics = 6;

const targetEl = document.getElementById("forumTopics");
// Если посты по какой-то причине не загрузятся, вы можете показать ссылку на форум
targetEl.innerHTML = `<a class="link-to-discourse" href="${baseForumURL}/">Посмотреть последние посты на форуме</a>`;

function renderTemplate(topicsArr) {
    const items = topicsArr.map(
        (topic) => `<li><a href="${topic[1]}" target="_blank">${topic[0]}</li>`
    );

    targetEl.innerHTML = `<ul>${items.join("\n")}</ul>`;
}

// добавьте `.json` к списку тем
fetch(`${baseForumURL}/latest.json`)
    .then((res) => res.json())
    .then((json) => {
        const topics = json.topic_list.topics
            .slice(0, numTopics)
            .map((t) => [
                t.title,
                `${baseForumURL}/t/${t.slug}/${t.id}`,
            ]);

        renderTemplate(topics);
    });
</script>
3 лайка

Спасибо, ценю полный фрагмент кода.

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

А пока, как сделать так, чтобы скрипт показывал только посты с определённым тегом и из определённой категории?

2 лайка

Не уверен, но, кажется, формат такой:
https://forum.example.com/tags/c/<catgory_name>/<tag_name>

Пример: категория «support», тег «css»:
https://meta.discourse.org/tags/c/support/css

Чтобы получить JSON, добавьте .json в конец:
https://meta.discourse.org/tags/c/support/css.json

Таким образом, в функции fetch вместо /latest.json используйте что-то вроде /tags/c/support/css.json.

(Не проверено.)

3 лайка

А что, если я хочу встроить 5 тем прямо в экземпляр Discourse?

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

Спасибо.

1 лайк

Какой параметр запроса используется для топ-тем? Я пробовал order="top", но он не возвращает топ-темы так же, как форум, в зависимости от конкретного периода. Как можно воспроизвести результат фильтрации форума? Где можно найти значения атрибутов, которые принимают варианты фильтрации в Discourse? Спасибо!

2 лайка

Можно ли изменить стиль шрифта встроенного содержимого? Я пробовал CSS-селекторы вроде d-topics-list iframe html .topics-list .topic-list-item, но безрезультатно. Заранее благодарю за подсказку!

2 лайка