使用 Trello 作为超简单的 CMS

Avatar of Phil Hawksworth
Phil Hawksworth

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

有时我们的网站需要一些内容管理。 不总是。 不需要太多。 但需要一些。 CMS 市场 正在蓬勃发展,提供了负担得起、易于使用产品,因此我们不缺选择。 值得庆幸的是,这是一个与过去截然不同的世界,过去公司被迫为一个全能、全方位、全整合、全个性化、大企业认证™ 的 CMS 平台支付巨额费用(不是确切的费用:我四舍五入到最近的十亿)。

但有时,使用任何更新网站内容的人员都熟悉的一种非常简单的工具会很不错,而不是去掌握一个新的 CMS。

我非常喜欢 Trello 来管理想法和任务。 而且它有 一个 API。 为什么不把它用作网站的内容来源呢? 我的意思是,嘿,如果我们能用 Google Sheets 做到这一点,那还有什么能阻止我们尝试其他事情呢?

你好,Trello

这是一个简单的网站,可以探索。 它从 此 Trello 看板 获取其内容,这些内容以部分的形式显示。 每个部分都由我们 Trello 看板中卡片的标题和描述字段填充。

Two webpages side-by-side. The left is a Trello board with a bright pink background. The right is a screenshot of the build website using Trello data.

Trello 使用 Markdown,这在这里很有用。 任何在 Trello 卡片中编辑内容的人都可以应用基本文本格式,并使相同的 Markdown 流入网站,并通过构建过程转换为 HTML。

构建块

我非常喜欢这种运行构建的模型,它从各种提要和来源中提取内容,然后将它们与模板混合在一起,以生成网站的 HTML。 它将演示与内容管理分离(这就是现代流行的 CMS 产品中“分离”一词的来源)。 这意味着我们可以自由地以我们想要的方式设计网站,使用我们在这里在 CSS-Tricks 上学习的所有花哨技巧和技术。

Diagram showing the flow of data, going from Trello as JSON to Build where the data and the template are coupled, then finally, to the front end.

由于我们在构建时引入内容,因此如果我们的网站变得流行并带来大量流量,我们就不必担心数据源的使用配额或性能。 为什么不呢? 看看我们把它们做得多么漂亮!

我想玩!

好的。 您可以获取此网站代码的副本,并尽情修改。 此版本包含有关如何创建自己的 Trello 看板并将其用作构建内容来源的信息。

如果您想先了解它是如何工作的,然后再自己动手,请继续阅读。

发现 API

Trello 有一个 文档齐全的 API 和一组 开发者资源。 还有一个 方便的 Node 模块,用于简化身份验证和与 API 交互的任务。 但您也可以在探索 Trello 看板时通过修改 URL 来探索 API。

例如,上面 Trello 看板的 URL 是

https://trello.com/b/Zzc0USwZ/hellotrello

如果我们在此 URL 中添加 .json,Trello 会将内容以 JSON 的形式显示给我们。 看一下。我们可以使用此技术来检查 Trello 中的底层数据。 以下是特定卡片的 URL

https://trello.com/c/YVxlSEzy/4-sections-from-cards

如果我们使用这个小技巧并将 .json 添加到 URL 中,我们将会看到 描述该卡片的数据

我们会发现有趣的事情 - 看板、列表和卡片的唯一 ID。 我们可以看到卡片的内容和大量元数据。

我喜欢这样做! 看看这些可爱的数据! 我们应该如何使用它?

决定如何使用看板

对于这个例子,让我们假设我们有一个网站,只有一页可管理的内容。 我们看板中的列表或列非常适合控制该页面上的部分。 编辑人员可以为它们提供标题和内容,并将它们拖放到他们想要的顺序。

我们需要列表的 ID,以便我们可以通过 API 访问它。 幸运的是,我们已经看到了如何发现它 - 看看 所讨论列表中任何卡片的数据。 每个都有一个 idBoard 属性。 bingo!

生成网站

计划是从 Trello 获取数据并将其应用于一些模板以填充我们的网站。 大多数 静态网站生成器 (SSG) 都可以完成这项工作。 这就是它们擅长的地方。 我将使用 Eleventy,因为我认为它具有最简单的概念易于理解。 此外,它在获取数据和使用 Nunjucks(一种流行的模板语言)生成干净的 HTML 方面非常高效。

我们希望能够使用表达式 lin 我们的模板,为在名为 trello 的 JavaScript 对象中找到的每个项目输出一个 section 元素

<!-- index.njk -->
{% for card in trello %}
<section>
  <h2>{{ card.name }}</h2>
  <div>
    {% markdown %}
      {{- card.desc | safe }}
    {% endmarkdown %}
  </div>
</section>
{% endfor %}

获取构建数据

像这样的 Jamstack 网站的一种流行技术是使用 Gulp、Grunt 或 [在此处插入最新的构建脚本热度] 运行构建,它从各种 API 和提要中获取数据,将数据存储在适合 SSG 的格式中,然后运行 SSG 生成 HTML。 这很有效。

Eleventy 通过支持在 其数据文件中执行 JavaScript 来简化此过程。 换句话说,它不仅可以利用以 JSON 或 YAML 格式存储的数据,还可以使用 JavaScript 返回的任何内容,从而为在 Eleventy 构建运行时直接向 API 发出请求打开大门。 我们不需要单独的构建步骤来先获取数据。 Eleventy 会为我们做。

让我们使用它来获取模板中 trello 对象的数据。

我们可以使用 Trello Node 客户端 查询 API,但事实证明,我们想要的所有数据都在 看板的 JSON 中。 所有内容! 一次请求! 我们可以一次性获取它!

// trello.js
module.exports = () => {
  const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json';

  // Use node-fetch to get the JSON data about this board
  const fetch = require('node-fetch');
  return fetch(TRELLO_JSON_URL)
    .then(res => res.json())
    .then(json => console.log(json));
};

但是,我们不想显示看板中的所有数据。 它包括其他列表中的卡片、已关闭和删除的卡片等。 但是,我们可以使用 JavaScript 的 filter 方法 来过滤卡片,只包括我们感兴趣的卡片。

// trello.js
module.exports = () => {
   const TRELLO_JSON_URL='https://trello.com/b/Zzc0USwZ/hellotrello.json'
   const TRELLO_LIST_ID='5e98325d6d6bd120f2b7395f',
 
   // Use node-fetch to get the JSON data about this board
   const fetch = require('node-fetch');
   return fetch(TRELLO_JSON_URL)
   .then(res => res.json())
   .then(json => {
 
     // Just focus on the cards which are in the list we want
     // and do not have a closed status
     let contentCards = json.cards.filter(card => {
       return card.idList == TRELLO_LIST_ID && !card.closed;
     });
 
     return contentCards;
 });
};

就这样! 将其保存在名为 trello.js 的文件中,该文件位于 Eleventy 的数据目录中,我们将在名为 trello 的对象中准备好这些数据,以便在我们的模板中使用。

搞定! 🎉

但是我们可以做得更好。 让我们也处理附件图片,并添加一种方法,在内容上线之前将其暂存以供审核。

图片附件

可以将文件附加到 Trello 中的卡片。当您附加图像时,它会直接显示在卡片中,并包含在数据中描述的资源的源 URL。我们可以利用它!

如果一张卡片包含图像附件,我们将获取其源 URL,并将其作为图像标签添加到我们的模板在构建时插入页面的内容中。这意味着在 JSON 的描述属性(card.desc)中添加图像的 Markdown 代码。

然后,我们可以让 Eleventy 将其与其他内容一起转换为 HTML。这段代码会在我们的 JSON 中查找卡片,并将数据整理成我们需要的形式。

// trello.js

// If a card has an attachment, add it as an image 
// in the description markdown
contentCards.forEach(card => {
  if(card.attachments.length) {
    card.desc = card.desc + `\n![${card.name}](${card.attachments[0].url} '${card.name}')`;
  }
});

现在我们也可以在内容中移动图像了。非常方便!

暂存内容

让我们再添加一个功能,以展示如何使用 Trello 管理网站内容。

我们可能希望在将内容发布到世界之前进行预览,这有几种方法。我们的 Trello 看板可以包含一个用于暂存内容的列表,以及一个用于生产内容的列表。但这将难以直观地了解新内容是如何与已发布内容一起展示的。

更好的方法是使用 Trello 的标签来指示哪些卡片已发布,哪些卡片应该只包含在网站的暂存版本中。这将提供一个良好的工作流程。我们可以通过在正确位置添加新卡片来添加更多内容。使用“暂存”标签标记它,并将其从出现在生产分支的卡片中过滤掉。

Screenshot of the Trello board with a bright pink background. It has cards in a column called Published.
Trello 中显示哪些内容已暂存以及哪些内容已发布的标签提示

需要对我们的 JavaScript 对象进行更多过滤

// trello.js

// only include cards labelled with "live" or with
// the name of the branch we are in
contentCards = contentCards.filter(card => {
  return card.labels.filter(label => (
    label.name.toLowerCase() == 'live' ||
    label.name.toLowerCase() == BRANCH
   )).length;
 });

我们希望标记为“已发布”的内容出现在构建的每个版本中,无论是否暂存。此外,我们将尝试包含标签与名为“BRANCH”的变量匹配的卡片。

为什么?这是什么意思?

这里我们将发挥创意!我选择在Netlify(免责声明:我在那里工作)上托管此网站。这意味着我可以在Netlify 的 CI/CD 环境中运行构建。每当我将更改推送到其 Git 存储库时,它都会重新部署网站,还会提供一些对于此网站非常有用的功能。

其中之一是分支部署。如果您想要为网站创建新的环境,可以通过在 Git 存储库中创建新分支来创建它。构建将在该上下文中运行,并且您的网站将发布到包含分支名称的子域上。像这样

查看一下,您将看到来自我们列表的所有卡片,包括带有橙色“暂存”标签的卡片。我们将其包含在此构建中,因为其标签与构建上下文的支部分支名称匹配。BRANCH 是一个环境变量,其中包含构建运行的支部分支名称。

label.name.toLowerCase() == BRANCH

理论上,我们可以创建任意数量的分支和标签,并且拥有各种暂存和测试环境。准备好将内容从“暂存”提升到“已发布”了吗?交换标签,就可以开始!

但是它如何更新呢?

在 Netlify 等 CI/CD 中运行网站构建时,我们获得的第二个好处是可以根据需要触发构建运行。Netlify 允许我们创建构建钩子。当您向其发送 HTTP POST 请求时,这些是会启动新部署的 Webhook。

如果 Trello 也支持 Webhook,那么我们可以将这些服务连接在一起,并在 Trello 看板发生更改时自动刷新网站。猜猜怎么着?它们确实支持!太好了!

要创建 Netlify 构建钩子,您需要访问您网站的管理面板。(如果您想尝试一下,可以通过点击几次来将此演示网站引导到新的 Netlify 网站中。)

Screenshot of the netlify build hooks screen with options to add a build hook and generate a public deploy key.
创建 Netlify 构建钩子

现在,有了新的构建钩子 URL,我们需要注册一个新的 Trello Webhook,当内容发生更改时,它会调用该钩子。在 Trello 中创建 Webhook 的方法是通过 Trello API

此网站的存储库包含一个用于调用 Trello API 并为您创建 Webhook 的小工具。但是您需要拥有 Trello 开发者令牌和密钥。幸运的是,您可以通过访问Trello 开发者门户网站并按照“授权客户端”下的说明进行操作来免费创建这些令牌和密钥。

获得了它们吗?太棒了!如果您将它们保存在项目中的.env文件中,您可以运行此命令来设置 Trello Webhook

npm run hook --url https://api.netlify.com/build_hooks/XXXXX

这样,我们就创建了一个用于管理简单网站上的内容的不错的小流程。我们可以按照自己的意愿设计前端,并且内容更新会发生在 Trello 看板上,每当内容发生更改时,该看板都会自动更新网站。

我真的可以用它吗?

这是一个简单的示例。这是有意为之。我确实希望展示解耦的概念,以及使用外部服务的 API 来驱动网站内容的概念。

这不会取代更复杂项目的完整功能的解耦 CMS。但这些原理完全适用于更复杂的网站。

但是,此模型可能非常适合我们看到的独立商店、酒吧和餐厅等企业的网站类型。想象一下,一个 Trello 看板,其中一个列表用于管理餐厅的首页,一个列表用于管理餐厅的菜单项。餐厅员工可以非常轻松地管理这些内容,比每次菜单更改时都上传新的 PDF 格式的菜单要好得多。

准备探索示例并尝试使用您自己的看板和内容吗?试试这个