在另一个网站中嵌入 Discourse 主题列表

如果您获取 Discourse 的最新构建版本,您将能够通过一些简单的 JavaScript 和 HTML,在其他网站中嵌入主题列表

此功能的典型用例是博客或其他以内容为主的网站,您希望在屏幕侧边显示一个列出主题的部件。您可以按分类、标签或其他任何公开可用的筛选选项进行过滤。

如何嵌入主题列表

首先,您必须启用“嵌入主题列表”站点设置。

然后,在您的 HTML 中添加一个 <script> 标签,包含嵌入 Discourse 主题所需的 JavaScript。您可以将此标签放在您通常添加脚本的任何位置。例如:

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

URL 替换为论坛地址,如果存在子文件夹,请包含该子文件夹。

之后,在 HTML 文档的 <body> 中,添加一个 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 将知道您是否已登录论坛,并相应地显示结果。如果您在登录时看到安全分类等内容,请不要感到惊讶——匿名用户将无法看到!

参数列表

templatecompletebasic(默认)。虽然 basic 仅显示主题标题列表,但 complete 会包含标题、用户名、用户头像、创建日期和主题缩略图。

per-page:数字。控制返回的主题数量。

category:数字。将主题限制为单个分类。传递目标分类的 id

allow-create:布尔值。如果启用,嵌入内容将包含一个“新建主题”按钮。

tags:字符串。将主题限制为与此标签关联的主题。

top_periodallyearlyquarterlymonthlyweeklydaily 之一。如果启用,将返回该时期的“热门”主题。

示例

我在此处创建了一个示例网站:

https://embed.eviltrout.com

您应该能够在浏览器中查看源代码以查看代码,但整个源代码也在 GitHub 上:

这是一项全新功能,因此任何反馈或请求都将不胜感激。

样式化列表

您可以使用现有的主题功能为嵌入列表添加自定义样式。

例如,默认情况下,使用 complete 模板的嵌入列表看起来如下:

如果您希望它看起来像网格,可以在 主题 > 通用 > 嵌入 CSS 中添加自定义 SCSS:

.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` 可能需要 polyfill
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 个赞

谢谢!我喜欢这个想法,但根据这篇帖子 https://meta.discourse.org/t/what-are-the-risks-of-enabling-cross-origin-resource-sharing-discourse-enable-cors/41248,我认为启用 CORS 并不是最好的选择。

2 个赞

您只应为受信任的网站启用 CORS。为所有来源启用 CORS 会使其失去原本的意义。

6 个赞

我只为其他我控制的网站启用了 CORS。目标是使用前端代码在我们的其他网站上加载论坛主题列表。这种方法可能无法让随机网站嵌入帖子。

编辑: 如果你复制/粘贴那段代码,请注意 .slice(1, 6),因为它会移除其中一个主题。(我想那是我想移除的置顶主题。)

3 个赞

太好了,那我们可以试试。目前除了 localhost(测试期间),我还没有其他网站,所以之前有些犹豫。不过,如果我能找到有合适场地的人,我们就可以尝试一下。感谢你的帮助!

4 个赞

@j127

我们目前也遇到了同样的问题,想要在新标签页中打开链接。
只是快速问一下。

你提供的代码具体应该放在哪里?

@eviltrout 你知道为什么在我的情况下 CSS 样式没有显示出来吗?

2 个赞

那段代码只是一个概括性思路的快速示例。我认为 fetch 并非在所有浏览器中都能正常工作。如果你需要,我可以将其重写为 ES5 版本。

你的网站是 WordPress 还是某种无头 WordPress?

4 个赞

它似乎在所有_现代_浏览器中都能正常工作,而 Discourse 已不再支持 IE11。

4 个赞

我的站点只是普通的 WordPress,并非无头架构。

1 个赞

我可以用这个在另一个 Discourse 实例中嵌入一系列 Discourse 主题吗?或者有没有更聪明的方法?

我的使用场景是在两个实例之间建立一个“桥梁”,作为临时措施,直到未来可以将它们合并。如果能实现自动化和动态更新就更好了。

2 个赞

如果您不关心 IE 浏览器,那么这段代码片段应该可以工作。(需要在 Discourse 设置中为外部域名启用 CORS。)

要测试它,请打开此论坛上的任意页面,并将下面的代码片段粘贴到浏览器控制台中。我将目标元素更改为 document.body,因此它会将整个页面替换为论坛主题列表。对于实际网站,只需将目标元素更改为 document.getElementById("#someId"),然后在页面的某个位置放置一个具有该 ID 的 div。脚本会用主题列表填充它。

// 除非您不关心 IE,否则 `fetch` 可能需要 polyfill
// 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]}</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);
    });

示例: 如果您的 WordPress 网站上的目标 div 看起来像这样

<div id="forumTopics">
    <!-- 论坛帖子将显示在这里 -->
</div>

那么 JavaScript 可以这样定位该 ID:

// 在我之前发布的原始示例中查找这一行
const targetEl = document.getElementById("forumTopics");
4 个赞

你的意思是第一个帖子中的代码,还是第 50 个帖子中的代码片段?另外,是的,我不关心 IE。没必要迁就那个“恐龙”浏览器 :)。
在我的情况下,我的代码片段在任何浏览器中都没有加载 CSS。

所以这是 JavaScript,我在将其集成到 WordPress 时需要在周围加上 <script> 标签,对吗?

2 个赞

我已经有一段时间没有使用 WordPress 了。您是要将它添加到主题的 HTML 中吗?如果是的话,请将我的代码包裹在 script 标签中,并将 <div> 放在您希望帖子出现的位置。

我认为您可以直接将下面的代码插入到 WordPress 模板的 HTML 中,放在您希望帖子出现的位置。请务必更新包含论坛 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]}</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);
    });
</script>
3 个赞

谢谢,非常感谢提供完整的代码片段

初步测试后,只显示了一个链接。但我认为 CORS 被禁用了,所以会让我的主机服务商启用它。
同时,如何让脚本仅显示带有特定标签且来自特定分类的文章?

2 个赞

我不太确定,但我认为格式是
https://forum.example.com/tags/c/<category_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 个赞

如果我想在 Discourse 实例本身中嵌入 5 个主题呢?

我可以创建一个主题,将其设为维基,然后作为页面发布。那么,我该如何嵌入来自特定分类或标签的最新 5 个主题,或者使用该功能支持的模板?

谢谢。

1 个赞

top 主题的查询参数是什么?我尝试了 order="top",但它没有像论坛那样根据特定时间段返回 top 主题。我该如何复现论坛的筛选结果?在哪里可以查询 Discourse 筛选选项所接受的属性值?谢谢!

2 个赞

可以更改嵌入内容的字体样式吗?我尝试了类似 d-topics-list iframe html .topics-list .topic-list-item 的 CSS 选择器,但没有成功。提前感谢您的指点!

2 个赞