Grabbing data from Netlify CMS folder - reactjs

I have successfully linked the netlify cms backend to my react frontend, however now my main problem is figuring out how to display said data from its folder _posts to my react frontend.
MySite
| _posts
| blog
/2020-06-23-my-post.md
| public
config.yml
backend:
name: git-gateway
branch: master
media_folder: "public/images" # Media files will be stored in the repo under static/images/uploads
public_folder: "/images"
collections:
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Blog" # Used in the UI
folder: "_posts/blog" # The path to the folder where the documents are stored
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: "Layout", name: "layout", widget: "hidden", default: "blog"}
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Featured Image", name: "thumbnail", widget: "image"}
- {label: "Rating (scale of 1-5)", name: "rating", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}

Assume you have set your static/admin/config.yml for blog post like below
backend:
name: github
repo: ozluy/destan-nakliyat-gatsby
branch: master
media_folder: static/assets
public_folder: /assets
site_url: https://destan-nakliyat.netlify.app
collections:
- name: post
label: Post
folder: post
create: true
fields:
- { name: path, label: Path }
- { name: date, label: Date, widget: datetime }
- { name: title, label: Title }
- { name: body, label: Body, widget: markdown }
with gatsby-source-filesystem plugin you can use set your plugin settings as below in gatsby-config.js like this:
plugins: [
...,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/post/`,
},
},
finally you can access saved post files through GraphQL as below:
{
allFile {
edges {
node {
extension
dir
modifiedTime
}
}
}
}

Related

How to add data section to Netlify CSM?

I have a Hugo-based site and a couple of partials on the main page that I would like to add as parts of CMS. The partials are made of HTML layout and YAML in the data section. How can I expose this information to CMS?
Your question is a little vague, but assuming that your partials are driven by that YAML data, you can look at the file collection type to manage it.
Here's an example of what your config.yml file might look like:
collections:
- label: "Settings"
name: "settings"
files:
- label: "Partials"
name: "partials"
file: "data/partials.yml"
fields:
- label: "List of partials"
name: "partials_list"
widget: "list"
fields:
- {label: "Name", name: "name", widget: "string"}
- {label: "Partial type", name: "type", widget: "select", options: ["typeA", "typeB", "typeC"] }

I wanted to remove the GitHub login while accessing Netlify CMS for my gatsby blog but still getting login with GitHub button

I want to remove the Login with Gatsby option, I don't know why I'm getting it. I want to use the Invite only Registration option which I've enabled.
here's the blog linkenter link description here
I haven't installed OAuth provider as you can see in the screenshot.
Also, have enabled the invite only registrations,
but, still whenever I go to the /admin, I get the screen to log in with GitHub
I don't know why that's happening, here's my config.yml file
backend:
name: github
repo: PavanSargar/test-blog
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/assets
public_folder: assets
slug:
encoding: "ascii"
clean_accents: true
sanitize_replacement: "_"
collections:
- name: posts
label: Blog
folder: src/blog
create: true
fields:
- { name: path, label: Path }
- { name: date, label: Date, widget: datetime}
- { name: author, lable: "Author", widget: string, default: "sporttech"}
- { name: title, label: Title, pattern: ['.{60,}', "Must have at least 60 characters"] }
- { name: quote, label: Quote, required: false}
- {label: "Featured Image", name: "thumbnail", widget: "image", required: true}
- { name: body, label: Body, widget: markdown }
Try switching to the git-gateway backend:
backend:
name: git-gateway
branch: main
media_folder: static/assets
public_folder: assets
[...]

NetlifyCMS Editorial Workflow: Failed to persist entry: TypeError: r.has is not a function

I'm using Gatsby.js + Netlify CMS for my personal blog.
Yesterday, I enabled Editorial Workflow on my blog by following this link, once it's done, whenever I clicked the Save button on "Writing in Blog Collection" page, it throws an error:
Failed to persist entry: TypeError: r.has is not a function
Here is the screenshot
Also, the Big Green Publish button disappeared as well.
To reproduce
Go to the Publish Mode on this page
Add the publish_mode: editorial_workflow into your /admin/config.yml
Deploy to the live website
Go to the admin page of your website
Click the New Blog button
type anything in the new article
click Save button
you will see the error message
Here is my Github Repository for my blog:
https://github.com/franva/askwinston
Applicable Versions:
Netlify CMS version: 2.9.6 # "netlify-cms-app": "^2.9.6",
netlify-cms-core 2.22.1
netlify-cms-app 2.11.31
Git provider: GitHub
OS: Windows 10
Browser version Firefox 76.0.1 (64-bit)
Node.JS version: v12.14.1
CMS configuration
backend:
name: git-gateway
branch: master
repo: franva/askwinston
commit_messages:
create: 'Create {{collection}} “{{slug}}”'
update: 'Update {{collection}} “{{slug}}”'
delete: 'Delete {{collection}} “{{slug}}”'
uploadMedia: '[skip ci] Upload “{{path}}”'
deleteMedia: '[skip ci] Delete “{{path}}”'
media_folder: static/img
public_folder: /img
publish_mode: editorial_workflow
local_backend: true
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: Subheading, name: subheading, widget: string}
- {label: Mainpitch, name: mainpitch, widget: object, fields: [{label: Description, name: description, widget: text}]}
- {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"}

Accessing data from array within Gatsby Graphql query

Im getting stumped here. I have a Gatsby site Im setting up with netlify cms. I have my config file set and I can create the pages and widgets from within netlify admin. When I run my graphql query, I can pull the page info, but I cant access the data from the widget.
admin/config.yml
name: git-gateway
branch: master # Branch to update (optional; defaults to master)
media_folder: "src/images" # Media files will be stored in the repo under static/images/uploads
collections:
- name: "pages"
label: "Page"
folder: "src/markdown-pages"
create: true
fields:
- {label: "Title", name: "title"}
- {label: "Content", name: "content"}
- {label: "Slug", name: "slug"}
- {label: "Path", name: "path"}
- label: "Page Sections"
name: "sections"
widget: "list"
types:
- label: "Call To Action"
name: "calltoaction"
widget: object
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Content", name: "content", widget: "markdown", required: false}
- {label: "Call to Action Link", name: "link", widget: "string", required: false, hint: "Call to Action Link"}
- {label: "Call to Action Text", name: "buttontext", widget: "string", required: false, hint: "Call to Action Text"}
- label: "Services"
name: "services"
widget: object
fields:
- {label: "Title", name: "title", widget: "string"}
- {label: "Content", name: "content", widget: "markdown", required: false}
gatsby-node.js
exports.createPages = ({actions, graphql}) => {
const {createPage} = actions;
const pageTemplate = path.resolve('src/templates/page.js');
return graphql(`{
allMarkdownRemark {
edges {
node {
id
frontmatter {
title
path
sections {
buttontext
content
link
title
}
}
}
}
}
}`)
.then(result => {
if (result.errors) {
result.errors.forEach(e => console.error(e.toString()))
return Promise.reject(result.errors)
}
result.data.allMarkdownRemark.edges.forEach(({node}) => {
createPage({
path: node.frontmatter.path,
component: pageTemplate,
context: {
id: node.id,
}
})
})
})
}
pagetemplate.js
import Helmet from 'react-helmet'
import Layout from "../components/layout"
//import CTA from '../components/sections/CTA-section'
export default function Template({ data }) {
const {markdownRemark: page} = data
return (
<Layout>
<h1>{page.frontmatter.title}</h1>
<p>{page.frontmatter.sections.title}</p>
// <CTA />
</Layout>
)
}
export const pageQuery = graphql`
query pageByPath($id: String!) {
markdownRemark(id: { eq: $id } ) {
id
frontmatter {
path
title
sections {
title
buttontext
content
link
}
}
}
}
`
GraphiQL screenshot
graphiql screenshot
Bonus question: I also need to figure out how to make the component for these widgets. As you can see from the commented out lines in pagetemplate.js I would like to make separate components for each widget. Since the data is dynamic, I dont think I will be able to use StaticQuery, and I cant make another page query on the same data since it will be imported.

Displaying contents of directory(markdown files) in Gatsby using graphql

I'm using Gatsby/Netlify CMS stack and have been trying to display markdown file contents on the main page. For example, I have a directory in src/pages/experience that displays all of the experience markdown files.
So using graphql, I have a query that actually works:
{
allMarkdownRemark(
limit: 3,
sort: { order: DESC, fields: [frontmatter___date] },
filter: { fileAbsolutePath: { regex: "/(experience)/" } }
) {
edges {
node {
id
frontmatter {
title
company_role
location
work_from
work_to
tags
}
excerpt
}
}
}
}
However, when running it on my component page it displays ×
TypeError: Cannot read property 'allMarkdownRemark' of undefined
However after entering this before return:
if (!data) { return null };
The error goes away but the entire section disappears. Here it is below:
const Experience = ({data}) => {
return (
<div id="experience" className="section accent">
<div className="w-container">
<div className="section-title-group">
<Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
</div>
<div className="columns w-row">
{data.allMarkdownRemark.edges.map(({node}) => (
<div className="column-2 w-col w-col-4 w-col-stack" key={node.id}>
<div className="text-block"><strong>{node.frontmatter.title}</strong></div>
<div className="text-block-4">{node.frontmatter.company_role}</div>
<div className="text-block-4">{node.frontmatter.location}</div>
<div className="text-block-3">{node.frontmatter.work_from} – {node.frontmatter.work_to}</div>
<p className="paragraph">{node.frontmatter.excerpt}</p>
<div className="skill-div">{node.frontmatter.tags}</div>
</div>
))}
</div>
</div>
</div>
)}
export default Experience
In gatsby-config-js, I've added a gatsby-source-filesystem resolve separate from /src/posts to /src/pages where the experience directory is src/pages/experience.
Update: 2/7/2019
Here is the gatsby-config-js file:
module.exports = {
siteMetadata: {
title: `Howard Tibbs Portfolio`,
description: `This is a barebones template for my portfolio site`,
author: `Howard Tibbs III`,
createdAt: 2019
},
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
{
resolve: 'gatsby-remark-images',
},
],
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/posts`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages`,
},
},
`gatsby-plugin-netlify-cms`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
`gatsby-transformer-sharp`
],
}
What I feel is that somewhere in gatsby-node-js, I did not create a instance to do something with that type query.
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
const PostTemplate = path.resolve('./src/templates/post-template.js')
const BlogTemplate = path.resolve('./src/templates/blog-template.js')
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === 'MarkdownRemark') {
const slug = createFilePath({ node, getNode, basePath: 'posts' })
createNodeField({
node,
name: 'slug',
value: slug,
})
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
{
allMarkdownRemark (limit: 1000) {
edges {
node {
fields {
slug
}
}
}
}
}
`)
const posts = result.data.allMarkdownRemark.edges
posts.forEach(({ node: post }) => {
createPage({
path: `posts${post.fields.slug}`,
component: PostTemplate,
context: {
slug: post.fields.slug,
},
})
})
const postsPerPage = 2
const totalPages = Math.ceil(posts.length / postsPerPage)
Array.from({ length: totalPages }).forEach((_, index) => {
const currentPage = index + 1
const isFirstPage = index === 0
const isLastPage = currentPage === totalPages
createPage({
path: isFirstPage ? '/blog' : `/blog/${currentPage}`,
component: BlogTemplate,
context: {
limit: postsPerPage,
skip: index * postsPerPage,
isFirstPage,
isLastPage,
currentPage,
totalPages,
},
})
})
}
Wanted to know if anyone was able to get something similar to work? Greatly appreciate your help.
Update: 2/6/2019
So made some changes to my code from pageQuery to StaticQuery and unfortunately it still doesn't work but I believe it is going the right direction:
export default() => (
<div id="experience" className="section accent">
<div className="w-container">
<div className="section-title-group">
<Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
</div>
<div className="columns w-row">
<StaticQuery
query={graphql`
query ExperienceQuery {
allMarkdownRemark(
limit: 2,
sort: { order: DESC, fields: [frontmatter___date]},
filter: {fileAbsolutePath: {regex: "/(experience)/"}}
) {
edges {
node {
id
frontmatter {
title
company_role
location
work_from
work_to
tags
}
excerpt
}
}
}
}
`}
render={data => (
<div className="column-2 w-col w-col-4 w-col-stack" key={data.allMarkdownRemark.id}>
<div className="text-block"><strong>{data.allMarkdownRemark.frontmatter.title}</strong></div>
<div className="text-block-4">{data.allMarkdownRemark.frontmatter.company_role}</div>
<div className="text-block-4">{data.allMarkdownRemark.frontmatter.location}</div>
<div className="text-block-3">{data.allMarkdownRemark.frontmatter.work_from} – {data.allMarkdownRemark.frontmatter.work_to}</div>
<p className="paragraph">{data.allMarkdownRemark.frontmatter.excerpt}</p>
<div className="skill-div">{data.allMarkdownRemark.frontmatter.tags}</div>
</div>
)}
/>
</div>
</div>
</div>
);
I get this error TypeError: Cannot read property 'title' of undefined
So what I'm trying to accomplish is this instance across this section. Of course this is a placeholder but I'm looking to replace that placeholder with the contents of each markdown.
Experience snip
Update: 2/7/2019
So no changes today but wanted to post a few fields to get a better perspective of what I'm trying to do. This is the config.yml file from NetlifyCMS where it is displaying the collections. This is what I'm accomplishing (Note: the test repo is just to see the actual CMS, I will look to change):
backend:
name: test-repo
branch: master
media_folder: static/images
public_folder: /images
display_url: https://gatsby-netlify-cms-example.netlify.com/
# This line should *not* be indented
publish_mode: editorial_workflow
collections:
- name: "experience"
label: "Experience"
folder: "experience"
create: true
fields:
- { name: "title", label: "Company Title", widget: "string" }
- { name: "company_role", label: "Position Title", widget: "string" }
- { name: "location", label: "Location", widget: "string" }
- { name: "work_from", label: "From", widget: "date", format: "MMM YYYY" }
- { name: "work_to", label: "To", default: "Present", widget: "date", format: "MMM YYYY" }
- { name: "description", label: "Description", widget: "text" }
- { name: "tags", label: "Skills Tags", widget: "select", multiple: "true",
options: ["ReactJS", "NodeJS", "HTML", "CSS", "Sass", "PHP", "Typescript", "Joomla", "CMS Made Simple"] }
- name: "blog"
label: "Blog"
folder: "blog"
create: true
slug: "{{year}}-{{month}}-{{day}}_{{slug}}"
fields:
- { name: path, label: Path }
- { label: "Image", name: "image", widget: "image" }
- { name: title, label: Title }
- { label: "Publish Date", name: "date", widget: "datetime" }
- {label: "Category", name: "category", widget: "string"}
- { name: "body", label: "body", widget: markdown }
- { name: tags, label: Tags, widget: list }
- name: "projects"
label: "Projects"
folder: "projects"
create: true
fields:
- { name: date, label: Date, widget: date }
- {label: "Category", name: "category", widget: "string"}
- { name: title, label: Title }
- { label: "Image", name: "image", widget: "image" }
- { label: "Description", name: "description", widget: "text" }
- { name: body, label: "Details", widget: markdown }
- { name: tags, label: Tags, widget: list}
- name: "about"
label: "About"
folder: "src/pages/about"
create: false
slug: "{{slug}}"
fields:
- {
label: "Content Type",
name: "contentType",
widget: "hidden",
default: "about",
}
- { label: "Path", name: "path", widget: "hidden", default: "/about" }
- { label: "Title", name: "title", widget: "string" }
- { label: "Body", name: "body", widget: "markdown" }
And for an example of a markdown page, this would be the format to look for in the Experience section, because as you see in the picture it displays across the container:
---
title: Test Company
company_role: Test Role
location: Anytown, USA
work_from: January, 2020
work_to: January, 2020
tags: Test, Customer Service
---
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
Update: 2/8/2019
I do have some updates with the code provided below but before I get into it, here are a few images of what I'm looking to accomplish. These are placeholders that I'm looking to replace for real data. This is for each section:
Full Experience snip
Projects snip
Blog snip
I've ran the code that was provided by #staypuftman in the answer below and came up with this error:
Your site's "gatsby-node.js" created a page with a component that
doesn't exist.
I've added the code in addition to what was already there and it processed that error. This is what I originally thought would happen and the reason I wanted to use StaticQuery independently. This was actually the main issue I had with the documentation and the starter repos, no one really has created multiple variables in node.js.
I also tried the revision from #DerekNguyen which looked like this:
import React from "react"
import { Link, graphql, StaticQuery } from "gatsby"
export default(data) => (
<div id="experience" className="section accent">
<div className="w-container">
<div className="section-title-group">
<Link to="#experience"><h2 className="section-heading centered">Experience</h2></Link>
</div>
<div className="columns w-row">
<StaticQuery
query={graphql`
query ExperienceQuery {
allMarkdownRemark(
limit: 2,
sort: { order: DESC, fields: [frontmatter___date]},
filter: {fileAbsolutePath: {regex: "/(experience)/"}}
) {
edges {
node {
id
frontmatter {
title
company_role
location
work_from
work_to
tags
}
excerpt
}
}
}
}
`}
render={data.allMarkdownRemark.edges.map(({ node }) => (
<div className="column-2 w-col w-col-4 w-col-stack" key={node.id}>
<div className="text-block"><strong>{node.frontmatter.title}</strong></div>
<div className="text-block-4">{node.frontmatter.company_role}</div>
<div className="text-block-4">{node.frontmatter.location}</div>
<div className="text-block-3">{node.frontmatter.work_from} – {node.frontmatter.work_to}</div>
<p className="paragraph">{node.frontmatter.excerpt}</p>
<div className="skill-div">{node.frontmatter.tags}</div>
</div>
))}
/>
</div>
</div>
</div>
);
However that also came with an error as well:
TypeError: Cannot read property 'edges' of undefined
Still working on it, but I think it is getting closer to the solution. Keep in mind I also would have to create it for the other variables.
Update: 2/10/2019
For those who want to see how I constructed the site using gatsby-starter, here it is below:
My portfolio
gastby-node.js is used when you have a bunch of pages that need to be at /pages/{variable-here}/. Gatsby uses gatsby-node.js to run a GraphQL query against your data source (Netlify CMS in this case) and grabs all the content needed based on your particular GraphQL query.
It then dynamically builds out X number of pages using a component in your project. How many pages it builds depends on what it finds at the remote data source. How they look depends on the component you specify. Read more about this in the Gatsby tutorial.
Staticquery is used to get one-time data into components, not to generate pages from a data source. It is highly useful but not what I think you're trying to do. Read more about it on the Gatsby site.
Based on all of this and what you've provided above, I think your gatsby-node.js should look this:
// Give Node access to path
const path = require('path')
// Leverages node's createPages capabilities
exports.createPages = async ({ graphql, actions }) => {
// Destructures createPage from redux actions, you'll use this in a minute
const { createPage } = actions
// Make your query
const allExperiencePages = await graphql(`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
id
frontmatter {
title
company_role
location
work_from
work_to
tags
}
excerpt
}
}
}
}
`)
// gatsby structures allExperiencePages into an object you can loop through
// The documentation isn't great but the 'data' property contains what you're looking for
// Run a forEach with a callback parameter that contains each page's data
allExperiencePages.data.allMarkdownRemark.edges.forEach( page => {
// Make individual pages with createPage variable you made earlier
// The 'path' needs to match where you want the pages to be when your site builds
// 'conponent' is what Gatsby will use to build the page
// 'context' is the data that the component will receive when you `gatsby build`
createPage({
path: `/pages/${page.node.title}/`,
component: path.resolve('src/components/Experience'),
context: {
id: page.node.id,
title: page.node.frontmatter.title,
company_role: page.node.frontmatter.company_role,
location: page.node.frontmatter.location,
work_from: page.node.frontmatter.work_from,
work_to: page.node.frontmatter.work_to,
tags: page.node.frontmatter.tags,
excerpt: page.node.excerpt
}
})
})
}
This alone may not be enough to generate a page! It all depends on what's going on with the component you specify in the createPage component part of the gatsby-node.js file.

Resources