Issue
I am using Netlify and Gatsby, actually working with this template to learn these systems. I can see in the pre-made files that there are hidden widgets in the admin/config.yml file for the CMS:
I am attempting create a new page with some widgets in the 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 }
The normal fields all appear in the CMS, and they all get saved to the appropriate .md file in the path that I specified. However, the hidden fields are not being saved. This is resulting in build failures because GraphQL is attempting to build a page that doesn't exist, since the hidden templateKey field is supposed to be directing it to the appropriate Gatsby component. This is only occurring for new pages that I am creating. If I remove the templateKey field from a page that came with the template, it will resave that hidden field when I update the page in the CMS.
I am using the netlify-cms-proxy-server, but even if I send the CMS updates to my remote repo, the hidden fields are not saved.
I only found a couple other references to things tangentially related, and those are from years ago, so I suspect that it's something that I am doing that's preventing these from saving for my new pages.
If I manually add the templateKey field into my sidebar page's .md file, Gatsby will compile and the page will render. I can then edit the page in the CMS, save new content to the .md file, and the templateKey field will remain. Saving a new version does not remove the templateKey field.
I have also created an Issue on the template's github repository to try and get some insight from those involved there.
Here is my gatsby-config, gatsby-node, and config.yml files as well, if those are helpful:
Netlify CMS Config
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 }
Gatsby Config
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
],
};
Gatsby Node
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,
})
}
}
https://www.netlifycms.org/docs/widgets/hidden/
The hidden widget is not supported for file collections at the moment
I'm using this package and I've created languages array and I'm trying to show them as buttons in my message but I receive this error
Unhandled rejection Error: ETELEGRAM: 400 Bad Request: can't parse inline keyboard button: InlineKeyboardButton must be an Object
Code
var languages = [
'ENGLISH',
'CHINESE',
'FRENCH',
'GERMAN',
'DUTCH NL / BE',
'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)',
'FINNISH / SUOMI',
'ITALIAN',
'HUNGARIAN',
'BALKAN',
'FILIPINO',
'SPANISH',
'RUSSIAN',
'ARABIC',
'TURKISH',
'SLOVAKIAN',
'ROUMANIAN',
'PORTUGUESE',
'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)',
'HINID / ENGLISH(ONLY INDIA)',
'INDONESIAN',
'MALAYSIAN',
'ZIMBABWE',
'NIGERIA',
'Other'
];
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
console.log(options);
const languagesButtons = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: [
options,
]
})
};
console.log(languagesButtons);
bot.sendMessage(chatId, "What language do you speak?", languagesButtons);
Results
console.log(options);
[
[ { text: 'ENGLISH', callback_data: 'ENGLISH' } ],
[ { text: 'CHINESE', callback_data: 'CHINESE' } ],
[ { text: 'FRENCH', callback_data: 'FRENCH' } ],
[ { text: 'GERMAN', callback_data: 'GERMAN' } ],
[ { text: 'DUTCH NL / BE', callback_data: 'DUTCH NL / BE' } ],
[
{
text: 'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)',
callback_data: 'SCANDINAVIAN(NORDISH / DANISH / SWEDISH)'
}
],
[ { text: 'FINNISH / SUOMI', callback_data: 'FINNISH / SUOMI' } ],
[ { text: 'ITALIAN', callback_data: 'ITALIAN' } ],
[ { text: 'HUNGARIAN', callback_data: 'HUNGARIAN' } ],
[ { text: 'BALKAN', callback_data: 'BALKAN' } ],
[ { text: 'FILIPINO', callback_data: 'FILIPINO' } ],
[ { text: 'SPANISH', callback_data: 'SPANISH' } ],
[ { text: 'RUSSIAN', callback_data: 'RUSSIAN' } ],
[ { text: 'ARABIC', callback_data: 'ARABIC' } ],
[ { text: 'TURKISH', callback_data: 'TURKISH' } ],
[ { text: 'SLOVAKIAN', callback_data: 'SLOVAKIAN' } ],
[ { text: 'ROUMANIAN', callback_data: 'ROUMANIAN' } ],
[ { text: 'PORTUGUESE', callback_data: 'PORTUGUESE' } ],
[
{
text: 'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)',
callback_data: 'HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)'
}
],
[
{
text: 'HINID / ENGLISH(ONLY INDIA)',
callback_data: 'HINID / ENGLISH(ONLY INDIA)'
}
],
[ { text: 'INDONESIAN', callback_data: 'INDONESIAN' } ],
[ { text: 'MALAYSIAN', callback_data: 'MALAYSIAN' } ],
[ { text: 'ZIMBABWE', callback_data: 'ZIMBABWE' } ],
[ { text: 'NIGERIA', callback_data: 'NIGERIA' } ],
[ { text: 'Other', callback_data: 'Other' } ]
]
console.log(languagesButtons);
{
parse_mode: 'html',
reply_markup: '{"inline_keyboard":[[[{"text":"ENGLISH","callback_data":"ENGLISH"}],[{"text":"CHINESE","callback_data":"CHINESE"}],[{"text":"FRENCH","callback_data":"FRENCH"}],[{"text":"GERMAN","callback_data":"GERMAN"}],[{"text":"DUTCH NL / BE","callback_data":"DUTCH NL / BE"}],[{"text":"SCANDINAVIAN(NORDISH / DANISH / SWEDISH)","callback_data":"SCANDINAVIAN(NORDISH / DANISH / SWEDISH)"}],[{"text":"FINNISH / SUOMI","callback_data":"FINNISH / SUOMI"}],[{"text":"ITALIAN","callback_data":"ITALIAN"}],[{"text":"HUNGARIAN","callback_data":"HUNGARIAN"}],[{"text":"BALKAN","callback_data":"BALKAN"}],[{"text":"FILIPINO","callback_data":"FILIPINO"}],[{"text":"SPANISH","callback_data":"SPANISH"}],[{"text":"RUSSIAN","callback_data":"RUSSIAN"}],[{"text":"ARABIC","callback_data":"ARABIC"}],[{"text":"TURKISH","callback_data":"TURKISH"}],[{"text":"SLOVAKIAN","callback_data":"SLOVAKIAN"}],[{"text":"ROUMANIAN","callback_data":"ROUMANIAN"}],[{"text":"PORTUGUESE","callback_data":"PORTUGUESE"}],[{"text":"HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)","callback_data":"HINDI / OURDOU / ENGLISH(PAKISTAN & INDIA)"}],[{"text":"HINID / ENGLISH(ONLY INDIA)","callback_data":"HINID / ENGLISH(ONLY INDIA)"}],[{"text":"INDONESIAN","callback_data":"INDONESIAN"}],[{"text":"MALAYSIAN","callback_data":"MALAYSIAN"}],[{"text":"ZIMBABWE","callback_data":"ZIMBABWE"}],[{"text":"NIGERIA","callback_data":"NIGERIA"}],[{"text":"Other","callback_data":"Other"}]]]}'
}
Can you tell me what's wrong?
Update
Based on CherryDT answer I can get my buttons but:
I have other inline keyboard which works as expected here is sample of it:
const contactKeyboardTwo = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: [
[{ text: 'Website', url: 'https://www.google.com' }],
[{ text: 'Chart', url: 'https://www.google.com' }],
[{ text: 'How to buy', url: 'https://www.google.com' }],
[{ text: 'Contract', url: 'https://www.google.com' }],
[{ text: 'Contract', url: 'https://www.google.com' }]
]
})
};
bot.sendMessage(chatId, "Welcome", contactKeyboardTwo);
result
Now new code return results like this
how can I have my languages list same as fist image?
The inline_keyboard array expects object of type InlineKeyboardButton like these (not the same library, but you can see the structure).
I can see you attempted to do that here:
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
But the problem is, this maps each language to an array with one element with the object inside, so at the end you get an array of arrays:
[
[
{ text: 'AAA', callback_data: 'AAA' }
],
[
{ text: 'BBB', callback_data: 'BBB' }
]
]
So, the error says that the button object is not an InlineKeyboardObject object because it's actually an array and hence invalid.
What you need is this:
[
{ text: 'AAA', callback_data: 'AAA' },
{ text: 'BBB', callback_data: 'BBB' }
]
...which you'll get by removing the square brackets [ ] in your map callback (replacing them with ( ) instead because otherwise it would be parsed as block and not as object literal):
var options = languages.map(
x => ({ text: x, callback_data: x })
);
Fixed
All I needed to do was to remove [] from my inline_keyboard
var options = languages.map(
x => [{ text: x, callback_data: x }]
);
const languagesButtons = {
parse_mode: "html",
reply_markup: JSON.stringify({
inline_keyboard: options, // removed [] around option
})
};
In my case InlineKeyboardObject was missing one of the optional parameters