Netlify CMS 不保存隐藏字段
Netlify CMS not saving hidden fields
问题
我正在使用 Netlify 和 Gatsby,实际上是在使用 this template 来学习这些系统。我可以在预制文件中看到 CMS 的 admin/config.yml 文件中有隐藏的小部件:
我正在尝试在 CMS 中创建一个包含一些小部件的新页面:
- file: "src/pages/sidebar/index.md"
label: "Sidebar"
name: "sidebar"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "sidebar-page",
}
- { label: Title, name: title, widget: string }
- { label: Subtitle, name: subtitle, widget: string }
- { label: Body, name: body, widget: markdown }
- { label: Sidebar Title, name: sidebartitle, widget: string }
- { label: Sidebar Content, name: sidebarcontent, widget: markdown }
普通字段都出现在CMS中,并且它们都被保存到我指定路径中的相应.md文件中。但是,隐藏字段没有被保存。这会导致构建失败,因为 GraphQL 正在尝试构建一个不存在的页面,因为隐藏的 templateKey 字段应该将其定向到适当的 Gatsby 组件。这只发生在我正在创建的新页面上。如果我从模板附带的页面中删除 templateKey 字段,当我在 CMS 中更新页面时,它会重新保存该隐藏字段。
我正在使用 netlify-cms-proxy-server,但即使我将 CMS 更新发送到我的远程仓库,隐藏字段也不会保存。
我只发现了其他一些与切线相关的内容的引用,这些都是几年前的,所以我怀疑我正在做的事情阻止了这些内容保存到我的新页面中。
如果我手动将 templateKey 字段添加到侧边栏页面的 .md 文件中,Gatsby 将编译并呈现页面。然后我可以在 CMS 中编辑页面,将新内容保存到 .md 文件,templateKey 字段将保留。保存新版本不会删除 templateKey 字段。
我还在模板的 github repository 上创建了一个问题,试图从相关人员那里获得一些见解。
这是我的 gatsby-config、gatsby-node 和 config.yml 文件,如果它们有用的话:
Netlify CMS 配置
backend:
name: git-gateway
branch: main
commit_messages:
create: "Create {{collection}} “{{slug}}”"
update: "Update {{collection}} “{{slug}}”"
delete: "Delete {{collection}} “{{slug}}”"
uploadMedia: "[skip ci] Upload “{{path}}”"
deleteMedia: "[skip ci] Delete “{{path}}”"
local_backend: true
media_folder: static/img
public_folder: /img
collections:
- name: "blog"
label: "Blog"
folder: "src/pages/blog"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "blog-post",
}
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Description", name: "description", widget: "text" }
- { label: "Featured Post", name: "featuredpost", widget: "boolean" }
- { label: "Featured Image", name: "featuredimage", widget: image }
- { label: "Body", name: "body", widget: "markdown" }
- { label: "Tags", name: "tags", widget: "list" }
- name: "pages"
label: "Pages"
files:
- file: "src/pages/index.md"
label: "Landing Page"
name: "index"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "index-page",
}
- { label: Title, name: title, widget: string }
- { label: Image, name: image, widget: image }
- { label: Heading, name: heading, widget: string }
- { label: Subheading, name: subheading, widget: string }
- {
label: Mainpitch,
name: mainpitch,
widget: object,
fields:
[
{ label: Title, name: title, widget: string },
{ label: Description, name: description, widget: text },
],
}
- { label: Description, name: description, widget: string }
- {
label: Intro,
name: intro,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Blurbs,
name: blurbs,
widget: list,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Text, name: text, widget: text },
],
},
],
}
- {
label: Main,
name: main,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Image1,
name: image1,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image2,
name: image2,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image3,
name: image3,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
],
}
- file: "src/pages/about/index.md"
label: "About"
name: "about"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "about-page",
}
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
- file: "src/pages/products/index.md"
label: "Products Page"
name: "products"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "product-page",
}
- { label: Title, name: title, widget: string }
- { label: Image, name: image, widget: image }
- { label: Heading, name: heading, widget: string }
- { label: Description, name: description, widget: string }
- {
label: Intro,
name: intro,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Blurbs,
name: blurbs,
widget: list,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Text, name: text, widget: text },
],
},
],
}
- {
label: Main,
name: main,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Image1,
name: image1,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image2,
name: image2,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image3,
name: image3,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
],
}
- {
label: Testimonials,
name: testimonials,
widget: list,
fields:
[
{ label: Quote, name: quote, widget: string },
{ label: Author, name: author, widget: string },
],
}
- { label: Full_image, name: full_image, widget: image }
- {
label: Pricing,
name: pricing,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: string },
{
label: Plans,
name: plans,
widget: list,
fields:
[
{ label: Plan, name: plan, widget: string },
{ label: Price, name: price, widget: string },
{
label: Description,
name: description,
widget: string,
},
{ label: Items, name: items, widget: list },
],
},
],
}
- file: "src/pages/sidebar/index.md"
label: "Sidebar"
name: "sidebar"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "sidebar-page",
}
- { label: Title, name: title, widget: string }
- { label: Subtitle, name: subtitle, widget: string }
- { label: Body, name: body, widget: markdown }
- { label: Sidebar Title, name: sidebartitle, widget: string }
- { label: Sidebar Content, name: sidebarcontent, widget: markdown }
盖茨比配置
module.exports = {
siteMetadata: {
title: "Gatsby + Netlify CMS Starter",
description:
"This repo contains an example business website that is built with Gatsby, and Netlify CMS.It follows the JAMstack architecture by using Git as a single source of truth, and Netlify for continuous deployment, and CDN distribution.",
},
plugins: [
"gatsby-plugin-react-helmet",
{
resolve: "gatsby-plugin-sass",
options: {
sassOptions: {
indentedSyntax: false,
},
},
},
{
// keep as first gatsby-source-filesystem plugin for gatsby image support
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/static/img`,
name: "uploads",
},
},
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/src/pages`,
name: "pages",
},
},
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/src/img`,
name: "images",
},
},
`gatsby-plugin-image`,
"gatsby-plugin-sharp",
"gatsby-transformer-sharp",
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
{
resolve: "gatsby-remark-relative-images",
options: {
name: "uploads",
},
},
{
resolve: "gatsby-remark-images",
options: {
// It's important to specify the maxWidth (in pixels) of
// the content container as this plugin uses this as the
// base for generating different widths of each image.
maxWidth: 2048,
},
},
{
resolve: "gatsby-remark-copy-linked-files",
options: {
destinationDir: "static",
},
},
],
},
},
{
resolve: "gatsby-plugin-netlify-cms",
options: {
modulePath: `${__dirname}/src/cms/cms.js`,
},
},
{
resolve: "gatsby-plugin-purgecss", // purges all unused/unreferenced css rules
options: {
develop: true, // Activates purging in npm run develop
purgeOnly: ["/style.scss"], // applies purging only on the bulma css file
},
}, // must be after other CSS plugins
"gatsby-plugin-netlify", // make sure to keep it last in the array
],
};
盖茨比节点
const _ = require('lodash')
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
const { fmImagesToRelative } = require('gatsby-remark-relative-images')
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions
return graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
id
fields {
slug
}
frontmatter {
tags
templateKey
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()))
return Promise.reject(result.errors)
}
const posts = result.data.allMarkdownRemark.edges
posts.forEach((edge) => {
const id = edge.node.id
createPage({
path: edge.node.fields.slug,
tags: edge.node.frontmatter.tags,
component: path.resolve(
`src/templates/${String(edge.node.frontmatter.templateKey)}.js`
),
// additional data can be passed via context
context: {
id,
},
})
})
// Tag pages:
let tags = []
// Iterate through each post, putting all found tags into `tags`
posts.forEach((edge) => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
// Eliminate duplicate tags
tags = _.uniq(tags)
// Make tag pages
tags.forEach((tag) => {
const tagPath = `/tags/${_.kebabCase(tag)}/`
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.js`),
context: {
tag,
},
})
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
fmImagesToRelative(node) // convert image paths for gatsby images
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
文件 collections 不支持隐藏的小部件
问题 我正在使用 Netlify 和 Gatsby,实际上是在使用 this template 来学习这些系统。我可以在预制文件中看到 CMS 的 admin/config.yml 文件中有隐藏的小部件:
我正在尝试在 CMS 中创建一个包含一些小部件的新页面:
- file: "src/pages/sidebar/index.md"
label: "Sidebar"
name: "sidebar"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "sidebar-page",
}
- { label: Title, name: title, widget: string }
- { label: Subtitle, name: subtitle, widget: string }
- { label: Body, name: body, widget: markdown }
- { label: Sidebar Title, name: sidebartitle, widget: string }
- { label: Sidebar Content, name: sidebarcontent, widget: markdown }
普通字段都出现在CMS中,并且它们都被保存到我指定路径中的相应.md文件中。但是,隐藏字段没有被保存。这会导致构建失败,因为 GraphQL 正在尝试构建一个不存在的页面,因为隐藏的 templateKey 字段应该将其定向到适当的 Gatsby 组件。这只发生在我正在创建的新页面上。如果我从模板附带的页面中删除 templateKey 字段,当我在 CMS 中更新页面时,它会重新保存该隐藏字段。
我正在使用 netlify-cms-proxy-server,但即使我将 CMS 更新发送到我的远程仓库,隐藏字段也不会保存。
我只发现了其他一些与切线相关的内容的引用,这些都是几年前的,所以我怀疑我正在做的事情阻止了这些内容保存到我的新页面中。
如果我手动将 templateKey 字段添加到侧边栏页面的 .md 文件中,Gatsby 将编译并呈现页面。然后我可以在 CMS 中编辑页面,将新内容保存到 .md 文件,templateKey 字段将保留。保存新版本不会删除 templateKey 字段。
我还在模板的 github repository 上创建了一个问题,试图从相关人员那里获得一些见解。
这是我的 gatsby-config、gatsby-node 和 config.yml 文件,如果它们有用的话:
Netlify CMS 配置
backend:
name: git-gateway
branch: main
commit_messages:
create: "Create {{collection}} “{{slug}}”"
update: "Update {{collection}} “{{slug}}”"
delete: "Delete {{collection}} “{{slug}}”"
uploadMedia: "[skip ci] Upload “{{path}}”"
deleteMedia: "[skip ci] Delete “{{path}}”"
local_backend: true
media_folder: static/img
public_folder: /img
collections:
- name: "blog"
label: "Blog"
folder: "src/pages/blog"
create: true
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "blog-post",
}
- { label: "Title", name: "title", widget: "string" }
- { label: "Publish Date", name: "date", widget: "datetime" }
- { label: "Description", name: "description", widget: "text" }
- { label: "Featured Post", name: "featuredpost", widget: "boolean" }
- { label: "Featured Image", name: "featuredimage", widget: image }
- { label: "Body", name: "body", widget: "markdown" }
- { label: "Tags", name: "tags", widget: "list" }
- name: "pages"
label: "Pages"
files:
- file: "src/pages/index.md"
label: "Landing Page"
name: "index"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "index-page",
}
- { label: Title, name: title, widget: string }
- { label: Image, name: image, widget: image }
- { label: Heading, name: heading, widget: string }
- { label: Subheading, name: subheading, widget: string }
- {
label: Mainpitch,
name: mainpitch,
widget: object,
fields:
[
{ label: Title, name: title, widget: string },
{ label: Description, name: description, widget: text },
],
}
- { label: Description, name: description, widget: string }
- {
label: Intro,
name: intro,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Blurbs,
name: blurbs,
widget: list,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Text, name: text, widget: text },
],
},
],
}
- {
label: Main,
name: main,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Image1,
name: image1,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image2,
name: image2,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image3,
name: image3,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
],
}
- file: "src/pages/about/index.md"
label: "About"
name: "about"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "about-page",
}
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
- file: "src/pages/products/index.md"
label: "Products Page"
name: "products"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "product-page",
}
- { label: Title, name: title, widget: string }
- { label: Image, name: image, widget: image }
- { label: Heading, name: heading, widget: string }
- { label: Description, name: description, widget: string }
- {
label: Intro,
name: intro,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Blurbs,
name: blurbs,
widget: list,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Text, name: text, widget: text },
],
},
],
}
- {
label: Main,
name: main,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: text },
{
label: Image1,
name: image1,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image2,
name: image2,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
{
label: Image3,
name: image3,
widget: object,
fields:
[
{ label: Image, name: image, widget: image },
{ label: Alt, name: alt, widget: string },
],
},
],
}
- {
label: Testimonials,
name: testimonials,
widget: list,
fields:
[
{ label: Quote, name: quote, widget: string },
{ label: Author, name: author, widget: string },
],
}
- { label: Full_image, name: full_image, widget: image }
- {
label: Pricing,
name: pricing,
widget: object,
fields:
[
{ label: Heading, name: heading, widget: string },
{ label: Description, name: description, widget: string },
{
label: Plans,
name: plans,
widget: list,
fields:
[
{ label: Plan, name: plan, widget: string },
{ label: Price, name: price, widget: string },
{
label: Description,
name: description,
widget: string,
},
{ label: Items, name: items, widget: list },
],
},
],
}
- file: "src/pages/sidebar/index.md"
label: "Sidebar"
name: "sidebar"
fields:
- {
label: "Template Key",
name: "templateKey",
widget: "hidden",
default: "sidebar-page",
}
- { label: Title, name: title, widget: string }
- { label: Subtitle, name: subtitle, widget: string }
- { label: Body, name: body, widget: markdown }
- { label: Sidebar Title, name: sidebartitle, widget: string }
- { label: Sidebar Content, name: sidebarcontent, widget: markdown }
盖茨比配置
module.exports = {
siteMetadata: {
title: "Gatsby + Netlify CMS Starter",
description:
"This repo contains an example business website that is built with Gatsby, and Netlify CMS.It follows the JAMstack architecture by using Git as a single source of truth, and Netlify for continuous deployment, and CDN distribution.",
},
plugins: [
"gatsby-plugin-react-helmet",
{
resolve: "gatsby-plugin-sass",
options: {
sassOptions: {
indentedSyntax: false,
},
},
},
{
// keep as first gatsby-source-filesystem plugin for gatsby image support
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/static/img`,
name: "uploads",
},
},
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/src/pages`,
name: "pages",
},
},
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/src/img`,
name: "images",
},
},
`gatsby-plugin-image`,
"gatsby-plugin-sharp",
"gatsby-transformer-sharp",
{
resolve: "gatsby-transformer-remark",
options: {
plugins: [
{
resolve: "gatsby-remark-relative-images",
options: {
name: "uploads",
},
},
{
resolve: "gatsby-remark-images",
options: {
// It's important to specify the maxWidth (in pixels) of
// the content container as this plugin uses this as the
// base for generating different widths of each image.
maxWidth: 2048,
},
},
{
resolve: "gatsby-remark-copy-linked-files",
options: {
destinationDir: "static",
},
},
],
},
},
{
resolve: "gatsby-plugin-netlify-cms",
options: {
modulePath: `${__dirname}/src/cms/cms.js`,
},
},
{
resolve: "gatsby-plugin-purgecss", // purges all unused/unreferenced css rules
options: {
develop: true, // Activates purging in npm run develop
purgeOnly: ["/style.scss"], // applies purging only on the bulma css file
},
}, // must be after other CSS plugins
"gatsby-plugin-netlify", // make sure to keep it last in the array
],
};
盖茨比节点
const _ = require('lodash')
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
const { fmImagesToRelative } = require('gatsby-remark-relative-images')
exports.createPages = ({ actions, graphql }) => {
const { createPage } = actions
return graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
id
fields {
slug
}
frontmatter {
tags
templateKey
}
}
}
}
}
`).then((result) => {
if (result.errors) {
result.errors.forEach((e) => console.error(e.toString()))
return Promise.reject(result.errors)
}
const posts = result.data.allMarkdownRemark.edges
posts.forEach((edge) => {
const id = edge.node.id
createPage({
path: edge.node.fields.slug,
tags: edge.node.frontmatter.tags,
component: path.resolve(
`src/templates/${String(edge.node.frontmatter.templateKey)}.js`
),
// additional data can be passed via context
context: {
id,
},
})
})
// Tag pages:
let tags = []
// Iterate through each post, putting all found tags into `tags`
posts.forEach((edge) => {
if (_.get(edge, `node.frontmatter.tags`)) {
tags = tags.concat(edge.node.frontmatter.tags)
}
})
// Eliminate duplicate tags
tags = _.uniq(tags)
// Make tag pages
tags.forEach((tag) => {
const tagPath = `/tags/${_.kebabCase(tag)}/`
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.js`),
context: {
tag,
},
})
})
})
}
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
fmImagesToRelative(node) // convert image paths for gatsby images
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}