为 Hugo 博客集成 Netlify CMS

为 Hugo 博客集成 Netlify CMS

为什么需要 Netlify CMS

Netlify CMS 主要是解决了,随时随地打开浏览器写markdown博客的问题,同时它自动处理了图片上传等问题。

简单来说:

Netlify CMS = web 版的 markdown 文件管理器 + 自动图片上传并插入markdown代码 + 在web浏览器写markdown并自动推送到git仓库构建 ....

编辑器可以在富文本(所见即所得)和 Markdown 方式之间切换。 Markdown模式没有语法高亮显示,编辑起来略不方便。同时,切到 Markdown编辑模式后,编辑器的那些按钮都变灰不可用了。

Netlify CMS 只是附加功能,你完全可以在本地直接打开 NeoVim 或者 Emacs 写博客,然后用 Git push 发布文章。

代码编辑器可以自动折叠和伸展,支持选择语言,同时还支持选择是 emacs / sublime 还是 vim 编辑模式.

插入图片也非常方便,只是有一点,对于 Hugo Page Bundles 是不支持的,因此图片只能是集中上传到一个目录下(也就是配置文件中指定的 media_folder 路径)。

上传文件存相对目录 和 page bundle 支持,官方正式文档里并没有说明有这个功能。后来老灯发现这些新功能在beta版已经可用了 (beta 版的文档里有说明)。

beta 版的(最新版代码)可以去这里取: https://www.jsdelivr.com/package/npm/netlify-cms

由于是开源的,因此也可以在这查看changelog

部署方法

需要创建一个admin目录,里面放一个index.html 文件作为后台管理的入口。 由于这个文件实际上也是要deploy到静态博客的,因此,我们要放在Hugo的 static 目录下。

static/admin/index.html 内容如下:

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Blog Manager</title>
  <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
<body>
  <!-- Include the script that builds the page and powers Netlify CMS -->
  <!-- https://www.jsdelivr.com/package/npm/netlify-cms -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/netlify-cms.min.js"></script>

</body>
</html>

static/admin/config.yml 内容如下:

# https://www.netlifycms.org/docs/add-to-your-site/
backend:
  name: git-gateway
  branch: main # Branch to update (optional; defaults to master)
  squash_merges: true # beta feature

 # when using the default proxy server port
 # Run npx netlify-cms-proxy-server from the root dir of the repo
local_backend: true

# 编辑工作流程
publish_mode: editorial_workflow


# 全局静态文件上传目录
media_folder: "static/img/uploads" # Media files will be stored in the repo under images/uploads
public_folder: "/img/uploads" # The src attribute for uploaded media will begin with /images/uploads

media_folder_relative: true


collections:
  - name: "post" # Used in routes, e.g., /admin/collections/blog
    label: "Post" # Used in the UI
    folder: "content/post" # The path to the folder where the documents are stored
    path: '{{slug}}/index' # beta feature
    media_folder: ''
    public_folder: ''
    # adding a nested object will show the collection folder structure
    nested:
      depth: 100 # max depth to show in the collection tree
      summary: '{{title}}' # optional summary for a tree node, defaults to the inferred title field
    # adding a meta object with a path property allows editing the path of entries
    # moving an existing entry will move the entire sub tree of the entry to the new location
    meta: { path: { widget: string, label: 'Path', index_file: 'index', hint: 'markdown文件路径, 对于page bundle, 如 "一级目录/二级目录/page-bundle目录/index.md", 只需要填写 "一级目录/二级目录/page-bundle目录",要忘记填写page-bundle目录' } }
    create: true # Allow users to create new documents in this collection
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
    fields: # The fields for each document, usually in front matter
      - {label: "Title", name: "title", widget: "string", hint: "文章标题"}
      - {label: "Slug", name: "slug", widget: "string", required: false, hint: "Slug可以是目录,如 linux/archlinux, 此项会作为路径的功能可能会被 path meta 覆盖"}
      - {label: "Comments", name: "comments", widget: "boolean", default: true, hint: "true 允许评论, false 禁止评论"}
      - {label: "Draft", name: "draft", widget: "boolean", default: true, hint: "草稿不会 build"}
      - {label: "Publish Date", name: "date", widget: "datetime"}
      - {label: "Featured Image", name: "cover", widget: "image", required: false, hint: "封面图片"}
      - {label: "Tags", name: "tags", widget: "list", required: false, "文章标签,用半角逗号分隔"}
      - {label: "Body", name: "body", widget: "markdown", hint: "博客正文"}

注意,以上配置用于 Page bundle 模式写博客。如果设置了 slug , 则slug一定要与path` 的最后一部分("一级目录/二级目录/page-bundle目录" 中的 "page-bundle目录")相同,不然引入的图片路径就会不对。

这里的 comments 是老灯主题的自定义字段,表示文章是否允许评论。cover 字段也是主题里定义的,表示文章封面图。如果拿过去用,注意稍修改一下适配你自己的主题。

当前这个配置支持:

  1. 全局静态文件上传到 static/img/uploads
  2. 仅支持 page bundle 模式写文章(这也是老灯认为唯一适合的文件组织方式)
  3. 由于支持 page bundle,因此,文章图片上传也是上传到 page bundle 的目录。
  4. 支持 Netlify CMS 本地仓库模式

本地Git仓库模式

Working with a Local Git Repository https://www.netlifycms.org/docs/beta-features/#working-with-a-local-git-repository

简单启用,只需要在 config.yml 中添加 local_backend: true 即可。 然后 npx netlify-cms-proxy-server 启动 proxy 即可,它默认监听 8081 端口。 然后通过 hugo serve 启动,然后访问 http://localhost:1313/admin/ 即可。

如果要自定义端口,你需要在 Hugo 项目的根目录下新建一 .env 文件,内容如下:

PORT=8082

然后更新 config.yml 中的 local_backend 对象配置:

backend:
  name: git-gateway

local_backend:
  # when using a custom proxy server port
  url: http://localhost:8082/api/v1
  # when accessing the local site from a host other than 'localhost' or '127.0.0.1'
  # allowed_hosts: ['192.168.0.1']

如果需要允许局域网其它电脑访问,可以添加 allowed_hosts 配置。

toubleshoot

关于配置

Bool类型的一定要写truefalse, 不要写成了字符串。不然后台会报错:

Error loading the CMS configuration Config Errors: 'collections[0].fields[4].required' should be boolean Check your config.yml file.

参考文档

https://zhuanlan.zhihu.com/p/56319868

https://www.netlifycms.org/docs/add-to-your-site/

https://www.netlifycms.org/docs/widgets/

https://www.netlifycms.org/docs/beta-features/#folder-collections-media-and-public-folder

https://www.netlifycms.org/docs/configuration-options/

其它

关于 Page Bundles 的支持问题,已经有人提issue了,见:

https://github.com/netlify/netlify-cms/issues/1697

https://github.com/netlify/netlify-cms/issues/513

Store Assets Relative to Content (Bundles) #1472

https://github.com/netlify/netlify-cms/issues/1472#issuecomment-557927885

Hi everyone ! Does anyone know if this feature is implemented? This is the only thing preventing us from using Netlify CMS in our project... and the documentation is not clear on what the option media_folder_relative: true allows to do. Thanks for the help :)

https://github.com/netlify/netlify-cms/issues/2696#issuecomment-614544868

Closing as media_folder_relative was removed in favour of https://www.netlifycms.org/docs/beta-features/#folder-collections-media-and-public-folder

https://github.com/netlify/netlify-cms/issues/3227

https://github.com/netlify/netlify-cms/issues/1472#issuecomment-559376878

Latest code on master branch (not published yet to beta) lets you do:

slug: 'index'
path: '{{title}}/{{slug}}'

to save a file under title/index.md for example.

Next step will be to store images with the content.

相关已经合并 PR: https://github.com/netlify/netlify-cms/pull/2958