├── .env.development.example ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── create ├── createPages.js ├── createPosts.js ├── layouts.js └── utils.js ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── gatsby-ssr.js ├── globals.js ├── package.json ├── src ├── components │ ├── AllLayouts.js │ ├── FluidImage.js │ ├── Header.js │ ├── Layout.js │ ├── Menu.js │ ├── MenuItem.js │ ├── Pagination.js │ ├── PostEntry.js │ ├── SEO.js │ ├── UniversalLink.js │ └── layout.css ├── images │ ├── fallback.svg │ ├── gatsby-astronaut.png │ └── gatsby-icon.png ├── layouts │ ├── Hero │ │ ├── Hero.data.js │ │ ├── Hero.js │ │ └── index.js │ └── TextBlock │ │ ├── TextBlock.data.js │ │ ├── TextBlock.js │ │ └── index.js ├── pages │ └── 404.js ├── templates │ ├── fragments.js │ ├── page │ │ ├── data.js │ │ ├── index.js │ │ └── template.js │ └── post │ │ ├── blog.js │ │ ├── data.js │ │ └── index.js └── utils │ └── index.js ├── wordpress └── acf-export.json └── yarn.lock /.env.development.example: -------------------------------------------------------------------------------- 1 | # This is in gitignore and should not be pushed to the repository. 2 | 3 | WORDPRESS_URL=your-local-wordpress.local 4 | 5 | # Make sure there is no slash (/) in the end of the URL! 6 | 7 | # Make sure to rename this to .env.development !!! 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | .idea 9 | 10 | .template-cache 11 | 12 | # dotenv environment variable files 13 | .env* 14 | !.env.development.example 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Typescript v1 declaration files 48 | typings/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # gatsby files 63 | .cache/ 64 | public 65 | 66 | # Mac files 67 | .DS_Store 68 | 69 | # Yarn 70 | yarn-error.log 71 | .pnp/ 72 | .pnp.js 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | package.json 3 | package-lock.json 4 | public 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 gatsbyjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guide to Gatsby WordPress Starter Advanced with Previews, i18n and more - Overview 2 | 3 | :red_circle: 4 | **This repository is based on an older stack. I would recommend checking out https://github.com/henrikwirth/gatsby-starter-wordpress-twenty-twenty instead for a more up to date version. There could still be some learnings in this project, therefore I will just archive it.** 5 | :red_circle: 6 | 7 | 8 | Link to tutorial: [Guide to Gatsby WordPress Starter Advanced with Previews, i18n and more - Overview](https://dev.to/nevernull/overview-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-583l) 9 | 10 | ## Tutorial Outline 11 | 12 | 1. [Overview](https://dev.to/nevernull/overview-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-583l) 13 | 2. [Basic WordPress & Gatsby Setup](https://dev.to/nevernull/basic-wordpress-gatsby-setup-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-44d8) 14 | 3. [Setup Menu Navigation](https://dev.to/nevernull/setup-menu-navigation-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-3bfl) 15 | 4. [Deployment](https://dev.to/nevernull/deployment-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-2g2o) 16 | 5. [Blog with Pagination](https://dev.to/nevernull/blog-with-pagination-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-50g5) 17 | 6. [How to handle Images and make use of gatsby-image](https://dev.to/nevernull/how-to-handle-images-and-make-use-of-gatsby-image-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-g9b) 18 | 7. [PageBuilder with ACF Flexible Content](https://dev.to/nevernull/pagebuilder-with-acf-flexible-content-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-2lf4) 19 | 8. Internationalization - i18n 20 | 9. Dynamic Previews 21 | 10. ... 22 | 23 | Depending on time and upcoming discussions, this outline might change. I might add some more parts, if I discover other topics, that could be of interest. 24 | 25 | **Find the latest version here**: [https://gatsby-starter-wordpress-advanced.netlify.com](https://gatsby-starter-wordpress-advanced.netlify.com/) 26 | 27 | ## Who is this tutorial for? 👥 28 | 29 | For people who want to get started with Gatsby in combination with WordPress and create a production ready client-site or personal sites with lots of nice features. You should have a basic understanding of WordPress and JavaScript. The tutorial will get more advanced and into edge-cases, the further you go. 30 | 31 | Feel free to use the comments if something doesn't make sense to you. I'll try my best to help out for any level of experience. 32 | 33 | ## Requirements used for the project 📋 34 | 35 | - CMS 36 | - Highly Customizable 37 | - Easy to use 38 | - Secure 39 | - Translatable (Multi-Language) 40 | - Low Frequency Updates 41 | - Responsive Design 42 | - Good performance (SEO and site speed) 43 | - Scale: ~100 Pages 44 | 45 | So in short: **A modern extensible multi-language site**. 46 | 47 | ## What will you learn? 🎓 48 | 49 | Obviously this **depends on your knowledge**. The first parts (1-3) of the tutorial, will be more basic things, while from the middle onwards it will get more advanced, more specific to use cases and more edge-case. 50 | 51 | I just worked on a client project, that had all the above requirements and there is not many tutorials out there, which in depth, explain how to implement a full-fledged production ready site with i18n, previews and the ability to have some sort of page builder ability. 52 | 53 | **You will learn about:** 54 | 55 | - The basic setup and deployment. 56 | - How to make use of ACF flexible content field 57 | - How to dynamically render your components depending on which components are in use. 58 | - How WPGraphQl works and how you can extend it to your needs. 59 | - How to implement your multi-language setup with Polylang. 60 | - How you get Previews to work with dynamic data. Without the need of rebuilds. 61 | 62 | ## Repository 63 | 64 | Here is the code used in the tutorial. Checkout the branches for the different parts. 65 | 66 | {% github henrikwirth/gatsby-starter-wordpress-advanced no-readme %} 67 | 68 | ## Credits :heart: 69 | 70 | I want to thank some people, who helped me out a lot while working with those technologies. First of all thanks to all peeps on the [WPGraphQL Slack Chat](https://wpgql-slack.herokuapp.com/). Especially [Jason Bahl](https://github.com/jasonbahl), [Peter Pristas](https://github.com/pristas-peter), [Justin W Hall](https://github.com/justinwhall), [Esa-Matti Suuronen](https://github.com/esamattis), and all the others there I probably forgot about. If you ever need help, this is a great place to get well treated and sorted out. Thanks to [NeverNull](https://www.nevernull.io) for letting me work in this. 71 | 72 | Cover-Image vector art from rawpixel 73 | 74 | You might wanna checkout our huge list of resources for this subject: 75 | {% github henrikwirth/awesome-wordpress-gatsby no-readme %} 76 | 77 | 78 | 79 | ## Questions & Constructive Criticism :thought_balloon: 80 | 81 | I'd love to have discussions about my approaches. If you have any questions, or want to point out any mistakes. It is more than welcome. I'm new to writing tutorials and therefore am happy to improve in any way. 82 | 83 | ## Let's dive in :arrow_right: 84 | 85 | We will create our basic setup with WordPress & Gatsby. 86 | 87 | **Part 2** - [Basic WordPress & Gatsby Setup](https://dev.to/nevernull/basic-wordpress-gatsby-setup-guide-to-gatsby-wordpress-starter-advanced-with-previews-i18n-and-more-44d8) 88 | -------------------------------------------------------------------------------- /create/createPages.js: -------------------------------------------------------------------------------- 1 | const { FluidImageFragment } = require("../src/templates/fragments") 2 | const { PageTemplateFragment } = require("../src/templates/page/data") 3 | 4 | const _uniqBy = require("lodash.uniqby") 5 | const _isEmpty = require("lodash.isempty") 6 | 7 | const { getAllLayoutsData, createTemplate, createPageWithTemplate } = require("./utils") 8 | 9 | const filePathToComponents = "../src/layouts/" 10 | const templateCacheFolder = ".template-cache" 11 | const layoutMapping = require("./layouts") 12 | const pageTemplate = require.resolve("../src/templates/page/template.js") 13 | 14 | const GET_PAGES = (layouts) => ` 15 | ${FluidImageFragment} 16 | ${PageTemplateFragment(layouts)} 17 | 18 | query GET_PAGES($first:Int $after:String) { 19 | wpgraphql { 20 | pages( 21 | first: $first 22 | after: $after 23 | # This will make sure to only get the parent nodes and no children 24 | where: { 25 | parent: null 26 | } 27 | ) { 28 | pageInfo { 29 | hasNextPage 30 | endCursor 31 | } 32 | nodes { 33 | ...PageTemplateFragment 34 | } 35 | } 36 | } 37 | } 38 | ` 39 | 40 | const allPages = [] 41 | let pageNumber = 0 42 | const itemsPerPage = 10 43 | 44 | /** 45 | * This is the export which Gatbsy will use to process. 46 | * 47 | * @param { actions, graphql } 48 | * @returns {Promise} 49 | */ 50 | module.exports = async ({ actions, graphql, reporter }, options) => { 51 | 52 | /** 53 | * Get all layouts data as a concatenated string 54 | */ 55 | const layoutsData = getAllLayoutsData() 56 | 57 | /** 58 | * This is the method from Gatsby that we're going 59 | * to use to create pages in our static site. 60 | */ 61 | const { createPage } = actions 62 | /** 63 | * Fetch pages method. This accepts variables to alter 64 | * the query. The variable `first` controls how many items to 65 | * request per fetch and the `after` controls where to start in 66 | * the dataset. 67 | * 68 | * @param variables 69 | * @returns {Promise<*>} 70 | */ 71 | const fetchPages = async (variables) => 72 | /** 73 | * Fetch pages using the GET_PAGES query and the variables passed in. 74 | */ 75 | await graphql(GET_PAGES(layoutsData), variables).then(({ data }) => { 76 | /** 77 | * Extract the data from the GraphQL query results 78 | */ 79 | const { 80 | wpgraphql: { 81 | pages: { 82 | nodes, 83 | pageInfo: { hasNextPage, endCursor }, 84 | }, 85 | }, 86 | } = data 87 | 88 | /** 89 | * Map over the pages for later creation 90 | */ 91 | nodes 92 | && nodes.map((pages) => { 93 | allPages.push(pages) 94 | }) 95 | 96 | /** 97 | * If there's another page, fetch more 98 | * so we can have all the data we need. 99 | */ 100 | if (hasNextPage) { 101 | pageNumber++ 102 | reporter.info(`fetch page ${pageNumber} of pages...`) 103 | return fetchPages({ first: itemsPerPage, after: endCursor }) 104 | } 105 | 106 | /** 107 | * Once we're done, return all the pages 108 | * so we can create the necessary pages with 109 | * all the data on hand. 110 | */ 111 | return allPages 112 | }) 113 | 114 | /** 115 | * Kick off our `fetchPages` method which will get us all 116 | * the pages we need to create individual pages. 117 | */ 118 | await fetchPages({ first: itemsPerPage, after: null }).then((wpPages) => { 119 | 120 | wpPages && wpPages.map((page) => { 121 | let pagePath = `${page.uri}` 122 | 123 | /** 124 | * If the page is the front page, the page path should not be the uri, 125 | * but the root path '/'. 126 | */ 127 | if (page.isFrontPage) { 128 | pagePath = "/" 129 | } 130 | 131 | /** 132 | * Filter out empty objects. This can happen, if for some reason you 133 | * don't query for a specific layout (UnionType), that is potentially 134 | * there. 135 | */ 136 | const layouts = page.pageBuilder.layouts.filter(el => { 137 | return !_isEmpty(el) 138 | }) 139 | 140 | let mappedLayouts = [] 141 | 142 | if (layouts && layouts.length > 0) { 143 | /** 144 | * Removes all duplicates, as we only need to import each layout once 145 | */ 146 | const UniqueLayouts = _uniqBy(layouts, "fieldGroupName") 147 | 148 | /** 149 | * Maps data and prepares object for our template generation. 150 | */ 151 | mappedLayouts = UniqueLayouts.map((layout) => { 152 | return { 153 | layoutType: layout.fieldGroupName, 154 | componentName: layoutMapping[layout.fieldGroupName], 155 | filePath: filePathToComponents + layoutMapping[layout.fieldGroupName], 156 | } 157 | }) 158 | } 159 | 160 | createPageWithTemplate({ 161 | createTemplate: createTemplate, 162 | templateCacheFolder: templateCacheFolder, 163 | pageTemplate: pageTemplate, 164 | page: page, 165 | pagePath: pagePath, 166 | mappedLayouts: mappedLayouts, 167 | createPage: createPage, 168 | reporter: reporter, 169 | }) 170 | }) 171 | 172 | reporter.info(`# -----> PAGES TOTAL: ${wpPages.length}`) 173 | }) 174 | } 175 | -------------------------------------------------------------------------------- /create/createPosts.js: -------------------------------------------------------------------------------- 1 | 2 | const { 3 | PostTemplateFragment, 4 | BlogPreviewFragment, 5 | } = require("../src/templates/post/data.js") 6 | 7 | const {FluidImageFragment} = require("../src/templates/fragments") 8 | 9 | const { blogURI } = require("../globals") 10 | 11 | const postTemplate = require.resolve("../src/templates/post/index.js") 12 | const blogTemplate = require.resolve("../src/templates/post/blog.js") 13 | 14 | const GET_POSTS = ` 15 | # Here we make use of the imported fragments which are referenced above 16 | ${FluidImageFragment} 17 | ${PostTemplateFragment} 18 | ${BlogPreviewFragment} 19 | 20 | query GET_POSTS($first:Int $after:String) { 21 | wpgraphql { 22 | posts( 23 | first: $first 24 | after: $after 25 | # This will make sure to only get the parent nodes and no children 26 | where: { 27 | parent: null 28 | } 29 | ) { 30 | pageInfo { 31 | hasNextPage 32 | endCursor 33 | } 34 | nodes { 35 | uri 36 | 37 | # This is the fragment used for the Post Template 38 | ...PostTemplateFragment 39 | 40 | #This is the fragment used for the blog preview on archive pages 41 | ...BlogPreviewFragment 42 | } 43 | } 44 | } 45 | } 46 | ` 47 | 48 | 49 | const allPosts = [] 50 | const blogPages = []; 51 | let pageNumber = 0; 52 | const itemsPerPage = 10; 53 | 54 | 55 | /** 56 | * This is the export which Gatbsy will use to process. 57 | * 58 | * @param { actions, graphql } 59 | * @returns {Promise} 60 | */ 61 | module.exports = async ({ actions, graphql, reporter }, options) => { 62 | 63 | /** 64 | * This is the method from Gatsby that we're going 65 | * to use to create posts in our static site. 66 | */ 67 | const { createPage } = actions 68 | 69 | /** 70 | * Fetch posts method. This accepts variables to alter 71 | * the query. The variable `first` controls how many items to 72 | * request per fetch and the `after` controls where to start in 73 | * the dataset. 74 | * 75 | * @param variables 76 | * @returns {Promise<*>} 77 | */ 78 | const fetchPosts = async (variables) => 79 | /** 80 | * Fetch posts using the GET_POSTS query and the variables passed in. 81 | */ 82 | await graphql(GET_POSTS, variables).then(({ data }) => { 83 | /** 84 | * Extract the data from the GraphQL query results 85 | */ 86 | const { 87 | wpgraphql: { 88 | posts: { 89 | nodes, 90 | pageInfo: { hasNextPage, endCursor }, 91 | }, 92 | }, 93 | } = data 94 | 95 | /** 96 | * Define the path for the paginated blog page. 97 | * This is the url the page will live at 98 | * @type {string} 99 | */ 100 | const blogPagePath = !variables.after 101 | ? `${blogURI}/` 102 | : `${blogURI}/page/${pageNumber + 1}` 103 | 104 | /** 105 | * Add config for the blogPage to the blogPage array 106 | * for creating later 107 | * 108 | * @type {{ 109 | * path: string, 110 | * component: string, 111 | * context: {nodes: *, pageNumber: number, hasNextPage: *} 112 | * }} 113 | */ 114 | blogPages[pageNumber] = { 115 | path: blogPagePath, 116 | component: blogTemplate, 117 | context: { 118 | nodes, 119 | pageNumber: pageNumber + 1, 120 | hasNextPage, 121 | itemsPerPage, 122 | allPosts, 123 | }, 124 | } 125 | 126 | /** 127 | * Map over the posts for later creation 128 | */ 129 | nodes 130 | && nodes.map((posts) => { 131 | allPosts.push(posts) 132 | }) 133 | 134 | /** 135 | * If there's another post, fetch more 136 | * so we can have all the data we need. 137 | */ 138 | if (hasNextPage) { 139 | pageNumber++ 140 | reporter.info(`fetch post ${pageNumber} of posts...`) 141 | return fetchPosts({ first: itemsPerPage, after: endCursor }) 142 | } 143 | 144 | /** 145 | * Once we're done, return all the posts 146 | * so we can create the necessary posts with 147 | * all the data on hand. 148 | */ 149 | return allPosts 150 | }) 151 | 152 | /** 153 | * Kick off our `fetchPosts` method which will get us all 154 | * the posts we need to create individual posts. 155 | */ 156 | await fetchPosts({ first: itemsPerPage, after: null }).then((wpPosts) => { 157 | 158 | wpPosts && wpPosts.map((post) => { 159 | /** 160 | * Build post path based of theme blogURI setting. 161 | */ 162 | const path = `${blogURI}${post.uri}` 163 | 164 | createPage({ 165 | path: path, 166 | component: postTemplate, 167 | context: { 168 | post: post, 169 | }, 170 | }) 171 | 172 | reporter.info(`post created: ${post.uri}`) 173 | }) 174 | 175 | reporter.info(`# -----> POSTS TOTAL: ${wpPosts.length}`) 176 | 177 | /** 178 | * Map over the `blogPages` array to create the 179 | * paginated blog pages 180 | */ 181 | blogPages 182 | && blogPages.map((blogPage) => { 183 | if (blogPage.context.pageNumber === 1) { 184 | blogPage.context.publisher = true; 185 | blogPage.context.label = blogPage.path.replace('/', ''); 186 | } 187 | createPage(blogPage); 188 | reporter.info(`created blog archive page ${blogPage.context.pageNumber}`); 189 | }); 190 | }) 191 | } 192 | -------------------------------------------------------------------------------- /create/layouts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | page_Pagebuilder_Layouts_Hero: "Hero", 3 | page_Pagebuilder_Layouts_TextBlock: "TextBlock", 4 | } 5 | -------------------------------------------------------------------------------- /create/utils.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | 3 | module.exports.getAllLayoutsData = () => { 4 | const glob = require("glob") 5 | 6 | let allLayoutsString = "" 7 | 8 | const fileArray = glob.sync("./src/layouts/**/*.data.js") 9 | 10 | fileArray.forEach(function(file) { 11 | let queryString = require(path.resolve(file)) 12 | allLayoutsString = allLayoutsString + " \n " + queryString() 13 | }) 14 | 15 | return allLayoutsString 16 | } 17 | 18 | /** 19 | * Creates files based on a template string. 20 | * 21 | * @param {string} templateCacheFolderPath - Path where the temporary files should be saved. 22 | * @param {string} templatePath - Path to the file holding the template string. 23 | * @param {string} templateName - Name of the temporary created file. 24 | * @param {object[]} imports - An array of objects, that define the layoutType, componentName and filePath. 25 | * @returns {Promise<>} 26 | */ 27 | module.exports.createTemplate = ({ templateCacheFolderPath, templatePath, templateName, imports }) => { 28 | return new Promise((resolve) => { 29 | const fs = require("fs") 30 | 31 | const template = require(templatePath) 32 | const contents = template(imports) 33 | 34 | fs.mkdir(templateCacheFolderPath, { recursive: true }, (err) => { 35 | if (err) throw "Error creating template-cache folder: " + err 36 | 37 | 38 | const filePath = templateCacheFolderPath + "/" + ((templateName === "/" || templateName === "") ? "home" : templateName) + ".js" 39 | 40 | 41 | fs.writeFile(filePath, contents, "utf8", err => { 42 | if (err) throw "Error writing " + templateName + " template: " + err 43 | 44 | console.log("Successfully created " + templateName + " template.") 45 | resolve() 46 | }) 47 | }) 48 | }) 49 | } 50 | 51 | /** 52 | * Creates pages out of the temporary created templates. 53 | */ 54 | module.exports.createPageWithTemplate = ({ createTemplate, templateCacheFolder, pageTemplate, page, pagePath, mappedLayouts, createPage, reporter }) => { 55 | /** 56 | * First we create a new template file for each page. 57 | */ 58 | createTemplate( 59 | { 60 | templateCacheFolderPath: templateCacheFolder, 61 | templatePath: pageTemplate, 62 | templateName: "tmp-" + page.slug, 63 | imports: mappedLayouts, 64 | }).then(() => { 65 | 66 | /** 67 | * Then, we create a gatsby page with the just created template file. 68 | */ 69 | createPage({ 70 | path: pagePath, 71 | component: path.resolve(templateCacheFolder + "/" + "tmp-" + page.slug + ".js"), 72 | context: { 73 | page: page, 74 | }, 75 | }) 76 | 77 | reporter.info(`page created: ${pagePath}`) 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's Browser APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/browser-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | let activeEnv = 2 | process.env.GATSBY_ACTIVE_ENV || process.env.NODE_ENV || "development" 3 | 4 | console.log(`Using environment config: '${activeEnv}'`) 5 | 6 | require("dotenv").config({ 7 | path: `.env.${activeEnv}`, 8 | }) 9 | 10 | console.log(`This WordPress Endpoint is used: '${process.env.WORDPRESS_URL}'`) 11 | 12 | 13 | module.exports = { 14 | siteMetadata: { 15 | title: `Gatsby Starter WordPress Advanced`, 16 | description: `The great Gatsby Starter WordPress Advanced.`, 17 | author: `Henrik Wirth`, 18 | }, 19 | plugins: [ 20 | `gatsby-plugin-react-helmet`, 21 | `gatsby-transformer-sharp`, 22 | `gatsby-plugin-sharp`, 23 | `gatsby-plugin-offline`, 24 | { 25 | resolve: `gatsby-plugin-manifest`, 26 | options: { 27 | name: `Gatsby Starter WordPress Advanced`, 28 | short_name: `gatsby-starter-wordpress-advanced`, 29 | start_url: `/`, 30 | background_color: `#663399`, 31 | theme_color: `#663399`, 32 | display: `minimal-ui`, 33 | icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site. 34 | }, 35 | }, 36 | { 37 | resolve: `gatsby-source-filesystem`, 38 | options: { 39 | name: `images`, 40 | path: `${__dirname}/src/images`, 41 | }, 42 | }, 43 | { 44 | resolve: "gatsby-source-graphql", 45 | options: { 46 | typeName: "WPGraphQL", 47 | fieldName: "wpgraphql", 48 | url: `${process.env.WORDPRESS_URL}/graphql`, 49 | }, 50 | }, 51 | ], 52 | } 53 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const createPages = require("./create/createPages") 2 | const createPosts = require("./create/createPosts") 3 | const { createRemoteFileNode } = require(`gatsby-source-filesystem`) 4 | 5 | 6 | exports.createPagesStatefully = async ({ graphql, actions, reporter }, options) => { 7 | 8 | await createPages({ actions, graphql, reporter }, options) 9 | await createPosts({ actions, graphql, reporter }, options) 10 | } 11 | 12 | exports.createResolvers = ( 13 | { 14 | actions, 15 | cache, 16 | createNodeId, 17 | createResolvers, 18 | store, 19 | reporter, 20 | }, 21 | ) => { 22 | const { createNode } = actions 23 | createResolvers({ 24 | WPGraphQL_MediaItem: { 25 | imageFile: { 26 | type: `File`, 27 | resolve(source, args, context, info) { 28 | return createRemoteFileNode({ 29 | url: source.sourceUrl, 30 | store, 31 | cache, 32 | createNode, 33 | createNodeId, 34 | reporter, 35 | }) 36 | }, 37 | }, 38 | }, 39 | }) 40 | } 41 | -------------------------------------------------------------------------------- /gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Implement Gatsby's SSR (Server Side Rendering) APIs in this file. 3 | * 4 | * See: https://www.gatsbyjs.org/docs/ssr-apis/ 5 | */ 6 | 7 | // You can delete this file if you're not using it 8 | -------------------------------------------------------------------------------- /globals.js: -------------------------------------------------------------------------------- 1 | const Globals = { 2 | blogURI: 'blog' 3 | } 4 | 5 | module.exports = Globals 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gatsby-starter-wordpress-advanced", 3 | "private": true, 4 | "description": "The great Gatsby Starter WordPress Advanced.", 5 | "version": "1.0.0", 6 | "author": "Henrik Wirth", 7 | "dependencies": { 8 | "dotenv": "^8.2.0", 9 | "gatsby": "^2.17.11", 10 | "gatsby-cli": "^2.8.16", 11 | "gatsby-image": "^2.2.31", 12 | "gatsby-plugin-manifest": "^2.2.27", 13 | "gatsby-plugin-offline": "^3.0.19", 14 | "gatsby-plugin-react-helmet": "^3.1.14", 15 | "gatsby-plugin-sharp": "^2.2.37", 16 | "gatsby-source-filesystem": "^2.1.36", 17 | "gatsby-source-graphql": "^2.1.23", 18 | "gatsby-transformer-sharp": "^2.3.3", 19 | "glob": "^7.1.6", 20 | "lodash.isempty": "^4.4.0", 21 | "lodash.uniqby": "^4.7.0", 22 | "prop-types": "^15.7.2", 23 | "react": "^16.11.0", 24 | "react-dom": "^16.11.0", 25 | "react-helmet": "^5.2.1" 26 | }, 27 | "devDependencies": { 28 | "prettier": "^1.19.1" 29 | }, 30 | "keywords": [ 31 | "gatsby" 32 | ], 33 | "license": "MIT", 34 | "scripts": { 35 | "build": "gatsby build", 36 | "develop": "gatsby develop", 37 | "format": "prettier --write \"**/*.{js,jsx,json,md}\"", 38 | "start": "npm run develop", 39 | "serve": "gatsby serve", 40 | "clean": "gatsby clean && rm -rf .template-cache", 41 | "test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1", 42 | "type-check": "tsc --noEmit" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/components/AllLayouts.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Hero from "../layouts/Hero" 3 | import TextBlock from "../layouts/TextBlock" 4 | 5 | const AllLayouts = ({ layoutData }) => { 6 | 7 | const layoutType = layoutData.fieldGroupName 8 | 9 | /** 10 | * Default component 11 | */ 12 | const Default = () =>
In AllLayouts the mapping of this component is missing: {layoutType}
13 | 14 | /** 15 | * Mapping the fieldGroupName(s) to our components 16 | */ 17 | const layouts = { 18 | page_Pagebuilder_Layouts_Hero: Hero, 19 | page_Pagebuilder_Layouts_TextBlock: TextBlock, 20 | page_default: Default 21 | } 22 | 23 | /** 24 | * If layout type is not existing in our mapping, it shows our Default instead. 25 | */ 26 | const ComponentTag = layouts[layoutType] ? layouts[layoutType] : layouts['page_default'] 27 | 28 | return ( 29 | 30 | ) 31 | } 32 | 33 | export default AllLayouts 34 | -------------------------------------------------------------------------------- /src/components/FluidImage.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import GatsbyImage from "gatsby-image" 3 | import { graphql, useStaticQuery } from "gatsby" 4 | 5 | 6 | const FluidImage = ({ image, withFallback = false, ...props }) => { 7 | const data = useStaticQuery(graphql` 8 | query { 9 | fallbackImage: file(relativePath: { eq: "fallback.svg" }) { 10 | publicURL 11 | } 12 | } 13 | `) 14 | 15 | /** 16 | * Return fallback Image, if no Image is given. 17 | */ 18 | if (!image) { 19 | return withFallback ? {"Fallback"} : null 20 | } 21 | 22 | if (image && image.imageFile) { 23 | return 24 | } 25 | 26 | return {image.altText} 27 | } 28 | 29 | export default FluidImage 30 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import { Link } from "gatsby" 2 | import PropTypes from "prop-types" 3 | import React from "react" 4 | 5 | const Header = ({ siteTitle }) => ( 6 |
12 |
19 |

20 | 27 | {siteTitle} 28 | 29 |

30 |
31 |
32 | ) 33 | 34 | Header.propTypes = { 35 | siteTitle: PropTypes.string, 36 | } 37 | 38 | Header.defaultProps = { 39 | siteTitle: ``, 40 | } 41 | 42 | export default Header 43 | -------------------------------------------------------------------------------- /src/components/Layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Layout component that queries for data 3 | * with Gatsby's useStaticQuery component 4 | * 5 | * See: https://www.gatsbyjs.org/docs/use-static-query/ 6 | */ 7 | 8 | import React from "react" 9 | import PropTypes from "prop-types" 10 | import { useStaticQuery, graphql } from "gatsby" 11 | 12 | import Header from "./Header" 13 | import "./layout.css" 14 | import Menu from "./Menu" 15 | 16 | const Layout = ({ children }) => { 17 | const data = useStaticQuery(graphql` 18 | query SiteTitleQuery { 19 | site { 20 | siteMetadata { 21 | title 22 | } 23 | } 24 | } 25 | `) 26 | 27 | return ( 28 | <> 29 |
30 |
38 | 39 |
{children}
40 |
41 | © {new Date().getFullYear()}, Built with 42 | {` `} 43 | Gatsby 44 | {` by `} 45 | Henrik Wirth 46 |
47 |
48 | 49 | ) 50 | } 51 | 52 | Layout.propTypes = { 53 | children: PropTypes.node.isRequired, 54 | } 55 | 56 | export default Layout 57 | -------------------------------------------------------------------------------- /src/components/Menu.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { StaticQuery, graphql } from "gatsby" 3 | 4 | import MenuItem from "./MenuItem" 5 | 6 | /** 7 | * Define MenuItem fragment and get all primary menu items. 8 | */ 9 | const MENU_QUERY = graphql` 10 | 11 | fragment MenuItem on WPGraphQL_MenuItem { 12 | id 13 | label 14 | url 15 | title 16 | target 17 | } 18 | 19 | query GETMAINMENU { 20 | wpgraphql { 21 | menuItems(where: {location: PRIMARY}) { 22 | nodes { 23 | ...MenuItem 24 | } 25 | } 26 | generalSettings { 27 | url 28 | } 29 | } 30 | } 31 | ` 32 | 33 | const Menu = () => { 34 | return ( 35 | { 38 | if (data.wpgraphql.menuItems) { 39 | const menuItems = data.wpgraphql.menuItems.nodes 40 | const wordPressUrl = data.wpgraphql.generalSettings.url 41 | 42 | return ( 43 |
44 | { 45 | menuItems && 46 | menuItems.map((menuItem) => ( 47 | 48 | )) 49 | } 50 |
51 | ) 52 | } 53 | return null 54 | }} 55 | /> 56 | ) 57 | } 58 | 59 | export default Menu 60 | -------------------------------------------------------------------------------- /src/components/MenuItem.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { CreateLocalLink } from "../utils" 3 | import UniversalLink from "./UniversalLink" 4 | 5 | const MenuItem = ({ menuItem, wordPressUrl }) => { 6 | return ( 7 | 9 | {menuItem.label} 10 | 11 | ) 12 | } 13 | 14 | export default MenuItem 15 | -------------------------------------------------------------------------------- /src/components/Pagination.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "gatsby" 3 | import { blogURI } from "../../globals" 4 | 5 | const Pagination = ({ pageNumber, hasNextPage }) => { 6 | 7 | if (pageNumber === 1 && !hasNextPage) return null 8 | 9 | return ( 10 |
11 |

Posts navigation

12 |
13 | { 14 | pageNumber > 1 && ( 15 | 2 ? `${blogURI}/page/${pageNumber - 1}` : `${blogURI}/`} 23 | > 24 | Previous page 25 | 26 | ) 27 | } 28 | 29 | Page 30 | {pageNumber} 31 | 32 | 33 | { 34 | hasNextPage && ( 35 | 45 | Next page 46 | 47 | ) 48 | } 49 |
50 |
51 | ) 52 | } 53 | 54 | export default Pagination 55 | -------------------------------------------------------------------------------- /src/components/PostEntry.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link } from "gatsby" 3 | import { blogURI } from "../../globals" 4 | import FluidImage from "./FluidImage" 5 | 6 | const PostEntry = ({ post }) => { 7 | 8 | const { uri, title, featuredImage, excerpt } = post 9 | 10 | return ( 11 |
12 |
13 | 14 |

{title}

15 | 16 | 17 | 18 |
19 | 20 |
21 |
22 | ) 23 | } 24 | 25 | export default PostEntry 26 | -------------------------------------------------------------------------------- /src/components/SEO.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SEO component that queries for data with 3 | * Gatsby's useStaticQuery React hook 4 | * 5 | * See: https://www.gatsbyjs.org/docs/use-static-query/ 6 | */ 7 | 8 | import React from "react" 9 | import PropTypes from "prop-types" 10 | import Helmet from "react-helmet" 11 | import { useStaticQuery, graphql } from "gatsby" 12 | 13 | function SEO({ description, lang, meta, title }) { 14 | const { site } = useStaticQuery( 15 | graphql` 16 | query { 17 | site { 18 | siteMetadata { 19 | title 20 | description 21 | author 22 | } 23 | } 24 | } 25 | ` 26 | ) 27 | 28 | const metaDescription = description || site.siteMetadata.description 29 | 30 | return ( 31 | 72 | ) 73 | } 74 | 75 | SEO.defaultProps = { 76 | lang: `en`, 77 | meta: [], 78 | description: ``, 79 | } 80 | 81 | SEO.propTypes = { 82 | description: PropTypes.string, 83 | lang: PropTypes.string, 84 | meta: PropTypes.arrayOf(PropTypes.object), 85 | title: PropTypes.string.isRequired, 86 | } 87 | 88 | export default SEO 89 | -------------------------------------------------------------------------------- /src/components/UniversalLink.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { Link as GatsbyLink } from "gatsby" 3 | // Since DOM elements cannot receive activeClassName 4 | // and partiallyActive, destructure the prop here and 5 | // pass it only to GatsbyLink 6 | const UniversalLink = ({ children, to, activeClassName, partiallyActive, ...other }) => { 7 | // Tailor the following test to your environment. 8 | // This example assumes that any internal link (intended for Gatsby) 9 | // will start with exactly one slash, and that anything else is external. 10 | const internal = /^\/(?!\/)/.test(to) 11 | // Use Gatsby Link for internal links, and for others 12 | if (internal) { 13 | return ( 14 | 20 | {children} 21 | 22 | ) 23 | } 24 | return ( 25 | 26 | {children} 27 | 28 | ) 29 | } 30 | export default UniversalLink 31 | -------------------------------------------------------------------------------- /src/components/layout.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | } 6 | body { 7 | margin: 0; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | main, 19 | menu, 20 | nav, 21 | section, 22 | summary { 23 | display: block; 24 | } 25 | audio, 26 | canvas, 27 | progress, 28 | video { 29 | display: inline-block; 30 | } 31 | audio:not([controls]) { 32 | display: none; 33 | height: 0; 34 | } 35 | progress { 36 | vertical-align: baseline; 37 | } 38 | [hidden], 39 | template { 40 | display: none; 41 | } 42 | a { 43 | background-color: transparent; 44 | -webkit-text-decoration-skip: objects; 45 | } 46 | a:active, 47 | a:hover { 48 | outline-width: 0; 49 | } 50 | abbr[title] { 51 | border-bottom: none; 52 | text-decoration: underline; 53 | text-decoration: underline dotted; 54 | } 55 | b, 56 | strong { 57 | font-weight: inherit; 58 | font-weight: bolder; 59 | } 60 | dfn { 61 | font-style: italic; 62 | } 63 | h1 { 64 | font-size: 2em; 65 | margin: 0.67em 0; 66 | } 67 | mark { 68 | background-color: #ff0; 69 | color: #000; 70 | } 71 | small { 72 | font-size: 80%; 73 | } 74 | sub, 75 | sup { 76 | font-size: 75%; 77 | line-height: 0; 78 | position: relative; 79 | vertical-align: baseline; 80 | } 81 | sub { 82 | bottom: -0.25em; 83 | } 84 | sup { 85 | top: -0.5em; 86 | } 87 | img { 88 | border-style: none; 89 | } 90 | svg:not(:root) { 91 | overflow: hidden; 92 | } 93 | code, 94 | kbd, 95 | pre, 96 | samp { 97 | font-family: monospace, monospace; 98 | font-size: 1em; 99 | } 100 | figure { 101 | margin: 1em 40px; 102 | } 103 | hr { 104 | box-sizing: content-box; 105 | height: 0; 106 | overflow: visible; 107 | } 108 | button, 109 | input, 110 | optgroup, 111 | select, 112 | textarea { 113 | font: inherit; 114 | margin: 0; 115 | } 116 | optgroup { 117 | font-weight: 700; 118 | } 119 | button, 120 | input { 121 | overflow: visible; 122 | } 123 | button, 124 | select { 125 | text-transform: none; 126 | } 127 | [type="reset"], 128 | [type="submit"], 129 | button, 130 | html [type="button"] { 131 | -webkit-appearance: button; 132 | } 133 | [type="button"]::-moz-focus-inner, 134 | [type="reset"]::-moz-focus-inner, 135 | [type="submit"]::-moz-focus-inner, 136 | button::-moz-focus-inner { 137 | border-style: none; 138 | padding: 0; 139 | } 140 | [type="button"]:-moz-focusring, 141 | [type="reset"]:-moz-focusring, 142 | [type="submit"]:-moz-focusring, 143 | button:-moz-focusring { 144 | outline: 1px dotted ButtonText; 145 | } 146 | fieldset { 147 | border: 1px solid silver; 148 | margin: 0 2px; 149 | padding: 0.35em 0.625em 0.75em; 150 | } 151 | legend { 152 | box-sizing: border-box; 153 | color: inherit; 154 | display: table; 155 | max-width: 100%; 156 | padding: 0; 157 | white-space: normal; 158 | } 159 | textarea { 160 | overflow: auto; 161 | } 162 | [type="checkbox"], 163 | [type="radio"] { 164 | box-sizing: border-box; 165 | padding: 0; 166 | } 167 | [type="number"]::-webkit-inner-spin-button, 168 | [type="number"]::-webkit-outer-spin-button { 169 | height: auto; 170 | } 171 | [type="search"] { 172 | -webkit-appearance: textfield; 173 | outline-offset: -2px; 174 | } 175 | [type="search"]::-webkit-search-cancel-button, 176 | [type="search"]::-webkit-search-decoration { 177 | -webkit-appearance: none; 178 | } 179 | ::-webkit-input-placeholder { 180 | color: inherit; 181 | opacity: 0.54; 182 | } 183 | ::-webkit-file-upload-button { 184 | -webkit-appearance: button; 185 | font: inherit; 186 | } 187 | html { 188 | font: 112.5%/1.45em georgia, serif; 189 | box-sizing: border-box; 190 | overflow-y: scroll; 191 | } 192 | * { 193 | box-sizing: inherit; 194 | } 195 | *:before { 196 | box-sizing: inherit; 197 | } 198 | *:after { 199 | box-sizing: inherit; 200 | } 201 | body { 202 | color: hsla(0, 0%, 0%, 0.8); 203 | font-family: georgia, serif; 204 | font-weight: normal; 205 | word-wrap: break-word; 206 | font-kerning: normal; 207 | -moz-font-feature-settings: "kern", "liga", "clig", "calt"; 208 | -ms-font-feature-settings: "kern", "liga", "clig", "calt"; 209 | -webkit-font-feature-settings: "kern", "liga", "clig", "calt"; 210 | font-feature-settings: "kern", "liga", "clig", "calt"; 211 | } 212 | img { 213 | max-width: 100%; 214 | margin-left: 0; 215 | margin-right: 0; 216 | margin-top: 0; 217 | padding-bottom: 0; 218 | padding-left: 0; 219 | padding-right: 0; 220 | padding-top: 0; 221 | } 222 | h1 { 223 | margin-left: 0; 224 | margin-right: 0; 225 | margin-top: 0; 226 | padding-bottom: 0; 227 | padding-left: 0; 228 | padding-right: 0; 229 | padding-top: 0; 230 | margin-bottom: 1.45rem; 231 | color: inherit; 232 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 233 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 234 | font-weight: bold; 235 | text-rendering: optimizeLegibility; 236 | font-size: 2.25rem; 237 | line-height: 1.1; 238 | } 239 | h2 { 240 | margin-left: 0; 241 | margin-right: 0; 242 | margin-top: 0; 243 | padding-bottom: 0; 244 | padding-left: 0; 245 | padding-right: 0; 246 | padding-top: 0; 247 | margin-bottom: 1.45rem; 248 | color: inherit; 249 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 250 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 251 | font-weight: bold; 252 | text-rendering: optimizeLegibility; 253 | font-size: 1.62671rem; 254 | line-height: 1.1; 255 | } 256 | h3 { 257 | margin-left: 0; 258 | margin-right: 0; 259 | margin-top: 0; 260 | padding-bottom: 0; 261 | padding-left: 0; 262 | padding-right: 0; 263 | padding-top: 0; 264 | margin-bottom: 1.45rem; 265 | color: inherit; 266 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 267 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 268 | font-weight: bold; 269 | text-rendering: optimizeLegibility; 270 | font-size: 1.38316rem; 271 | line-height: 1.1; 272 | } 273 | h4 { 274 | margin-left: 0; 275 | margin-right: 0; 276 | margin-top: 0; 277 | padding-bottom: 0; 278 | padding-left: 0; 279 | padding-right: 0; 280 | padding-top: 0; 281 | margin-bottom: 1.45rem; 282 | color: inherit; 283 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 284 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 285 | font-weight: bold; 286 | text-rendering: optimizeLegibility; 287 | font-size: 1rem; 288 | line-height: 1.1; 289 | } 290 | h5 { 291 | margin-left: 0; 292 | margin-right: 0; 293 | margin-top: 0; 294 | padding-bottom: 0; 295 | padding-left: 0; 296 | padding-right: 0; 297 | padding-top: 0; 298 | margin-bottom: 1.45rem; 299 | color: inherit; 300 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 301 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 302 | font-weight: bold; 303 | text-rendering: optimizeLegibility; 304 | font-size: 0.85028rem; 305 | line-height: 1.1; 306 | } 307 | h6 { 308 | margin-left: 0; 309 | margin-right: 0; 310 | margin-top: 0; 311 | padding-bottom: 0; 312 | padding-left: 0; 313 | padding-right: 0; 314 | padding-top: 0; 315 | margin-bottom: 1.45rem; 316 | color: inherit; 317 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 318 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 319 | font-weight: bold; 320 | text-rendering: optimizeLegibility; 321 | font-size: 0.78405rem; 322 | line-height: 1.1; 323 | } 324 | hgroup { 325 | margin-left: 0; 326 | margin-right: 0; 327 | margin-top: 0; 328 | padding-bottom: 0; 329 | padding-left: 0; 330 | padding-right: 0; 331 | padding-top: 0; 332 | margin-bottom: 1.45rem; 333 | } 334 | ul { 335 | margin-left: 1.45rem; 336 | margin-right: 0; 337 | margin-top: 0; 338 | padding-bottom: 0; 339 | padding-left: 0; 340 | padding-right: 0; 341 | padding-top: 0; 342 | margin-bottom: 1.45rem; 343 | list-style-position: outside; 344 | list-style-image: none; 345 | } 346 | ol { 347 | margin-left: 1.45rem; 348 | margin-right: 0; 349 | margin-top: 0; 350 | padding-bottom: 0; 351 | padding-left: 0; 352 | padding-right: 0; 353 | padding-top: 0; 354 | margin-bottom: 1.45rem; 355 | list-style-position: outside; 356 | list-style-image: none; 357 | } 358 | dl { 359 | margin-left: 0; 360 | margin-right: 0; 361 | margin-top: 0; 362 | padding-bottom: 0; 363 | padding-left: 0; 364 | padding-right: 0; 365 | padding-top: 0; 366 | margin-bottom: 1.45rem; 367 | } 368 | dd { 369 | margin-left: 0; 370 | margin-right: 0; 371 | margin-top: 0; 372 | padding-bottom: 0; 373 | padding-left: 0; 374 | padding-right: 0; 375 | padding-top: 0; 376 | margin-bottom: 1.45rem; 377 | } 378 | p { 379 | margin-left: 0; 380 | margin-right: 0; 381 | margin-top: 0; 382 | padding-bottom: 0; 383 | padding-left: 0; 384 | padding-right: 0; 385 | padding-top: 0; 386 | margin-bottom: 1.45rem; 387 | } 388 | figure { 389 | margin-left: 0; 390 | margin-right: 0; 391 | margin-top: 0; 392 | padding-bottom: 0; 393 | padding-left: 0; 394 | padding-right: 0; 395 | padding-top: 0; 396 | margin-bottom: 1.45rem; 397 | } 398 | pre { 399 | margin-left: 0; 400 | margin-right: 0; 401 | margin-top: 0; 402 | margin-bottom: 1.45rem; 403 | font-size: 0.85rem; 404 | line-height: 1.42; 405 | background: hsla(0, 0%, 0%, 0.04); 406 | border-radius: 3px; 407 | overflow: auto; 408 | word-wrap: normal; 409 | padding: 1.45rem; 410 | } 411 | table { 412 | margin-left: 0; 413 | margin-right: 0; 414 | margin-top: 0; 415 | padding-bottom: 0; 416 | padding-left: 0; 417 | padding-right: 0; 418 | padding-top: 0; 419 | margin-bottom: 1.45rem; 420 | font-size: 1rem; 421 | line-height: 1.45rem; 422 | border-collapse: collapse; 423 | width: 100%; 424 | } 425 | fieldset { 426 | margin-left: 0; 427 | margin-right: 0; 428 | margin-top: 0; 429 | padding-bottom: 0; 430 | padding-left: 0; 431 | padding-right: 0; 432 | padding-top: 0; 433 | margin-bottom: 1.45rem; 434 | } 435 | blockquote { 436 | margin-left: 1.45rem; 437 | margin-right: 1.45rem; 438 | margin-top: 0; 439 | padding-bottom: 0; 440 | padding-left: 0; 441 | padding-right: 0; 442 | padding-top: 0; 443 | margin-bottom: 1.45rem; 444 | } 445 | form { 446 | margin-left: 0; 447 | margin-right: 0; 448 | margin-top: 0; 449 | padding-bottom: 0; 450 | padding-left: 0; 451 | padding-right: 0; 452 | padding-top: 0; 453 | margin-bottom: 1.45rem; 454 | } 455 | noscript { 456 | margin-left: 0; 457 | margin-right: 0; 458 | margin-top: 0; 459 | padding-bottom: 0; 460 | padding-left: 0; 461 | padding-right: 0; 462 | padding-top: 0; 463 | margin-bottom: 1.45rem; 464 | } 465 | iframe { 466 | margin-left: 0; 467 | margin-right: 0; 468 | margin-top: 0; 469 | padding-bottom: 0; 470 | padding-left: 0; 471 | padding-right: 0; 472 | padding-top: 0; 473 | margin-bottom: 1.45rem; 474 | } 475 | hr { 476 | margin-left: 0; 477 | margin-right: 0; 478 | margin-top: 0; 479 | padding-bottom: 0; 480 | padding-left: 0; 481 | padding-right: 0; 482 | padding-top: 0; 483 | margin-bottom: calc(1.45rem - 1px); 484 | background: hsla(0, 0%, 0%, 0.2); 485 | border: none; 486 | height: 1px; 487 | } 488 | address { 489 | margin-left: 0; 490 | margin-right: 0; 491 | margin-top: 0; 492 | padding-bottom: 0; 493 | padding-left: 0; 494 | padding-right: 0; 495 | padding-top: 0; 496 | margin-bottom: 1.45rem; 497 | } 498 | b { 499 | font-weight: bold; 500 | } 501 | strong { 502 | font-weight: bold; 503 | } 504 | dt { 505 | font-weight: bold; 506 | } 507 | th { 508 | font-weight: bold; 509 | } 510 | li { 511 | margin-bottom: calc(1.45rem / 2); 512 | } 513 | ol li { 514 | padding-left: 0; 515 | } 516 | ul li { 517 | padding-left: 0; 518 | } 519 | li > ol { 520 | margin-left: 1.45rem; 521 | margin-bottom: calc(1.45rem / 2); 522 | margin-top: calc(1.45rem / 2); 523 | } 524 | li > ul { 525 | margin-left: 1.45rem; 526 | margin-bottom: calc(1.45rem / 2); 527 | margin-top: calc(1.45rem / 2); 528 | } 529 | blockquote *:last-child { 530 | margin-bottom: 0; 531 | } 532 | li *:last-child { 533 | margin-bottom: 0; 534 | } 535 | p *:last-child { 536 | margin-bottom: 0; 537 | } 538 | li > p { 539 | margin-bottom: calc(1.45rem / 2); 540 | } 541 | code { 542 | font-size: 0.85rem; 543 | line-height: 1.45rem; 544 | } 545 | kbd { 546 | font-size: 0.85rem; 547 | line-height: 1.45rem; 548 | } 549 | samp { 550 | font-size: 0.85rem; 551 | line-height: 1.45rem; 552 | } 553 | abbr { 554 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 555 | cursor: help; 556 | } 557 | acronym { 558 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 559 | cursor: help; 560 | } 561 | abbr[title] { 562 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5); 563 | cursor: help; 564 | text-decoration: none; 565 | } 566 | thead { 567 | text-align: left; 568 | } 569 | td, 570 | th { 571 | text-align: left; 572 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12); 573 | font-feature-settings: "tnum"; 574 | -moz-font-feature-settings: "tnum"; 575 | -ms-font-feature-settings: "tnum"; 576 | -webkit-font-feature-settings: "tnum"; 577 | padding-left: 0.96667rem; 578 | padding-right: 0.96667rem; 579 | padding-top: 0.725rem; 580 | padding-bottom: calc(0.725rem - 1px); 581 | } 582 | th:first-child, 583 | td:first-child { 584 | padding-left: 0; 585 | } 586 | th:last-child, 587 | td:last-child { 588 | padding-right: 0; 589 | } 590 | tt, 591 | code { 592 | background-color: hsla(0, 0%, 0%, 0.04); 593 | border-radius: 3px; 594 | font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono", 595 | "Liberation Mono", Menlo, Courier, monospace; 596 | padding: 0; 597 | padding-top: 0.2em; 598 | padding-bottom: 0.2em; 599 | } 600 | pre code { 601 | background: none; 602 | line-height: 1.42; 603 | } 604 | code:before, 605 | code:after, 606 | tt:before, 607 | tt:after { 608 | letter-spacing: -0.2em; 609 | content: " "; 610 | } 611 | pre code:before, 612 | pre code:after, 613 | pre tt:before, 614 | pre tt:after { 615 | content: ""; 616 | } 617 | @media only screen and (max-width: 480px) { 618 | html { 619 | font-size: 100%; 620 | } 621 | } 622 | -------------------------------------------------------------------------------- /src/images/fallback.svg: -------------------------------------------------------------------------------- 1 | image 2 | -------------------------------------------------------------------------------- /src/images/gatsby-astronaut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrikwirth/gatsby-starter-wordpress-advanced/29ad0b350dc2b3dfabf4f1e870861270e2bd3608/src/images/gatsby-astronaut.png -------------------------------------------------------------------------------- /src/images/gatsby-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henrikwirth/gatsby-starter-wordpress-advanced/29ad0b350dc2b3dfabf4f1e870861270e2bd3608/src/images/gatsby-icon.png -------------------------------------------------------------------------------- /src/layouts/Hero/Hero.data.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = () => { 3 | return ` 4 | ... on WPGraphQL_Page_Pagebuilder_Layouts_Hero { 5 | fieldGroupName 6 | image { 7 | sourceUrl 8 | altText 9 | imageFile { 10 | childImageSharp { 11 | fluid(maxHeight: 400, quality: 90, cropFocus: CENTER) { 12 | ...GatsbyImageSharpFluid_tracedSVG 13 | } 14 | } 15 | } 16 | } 17 | text 18 | textColor 19 | } 20 | ` 21 | } 22 | -------------------------------------------------------------------------------- /src/layouts/Hero/Hero.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import FluidImage from "../../components/FluidImage" 3 | 4 | const Hero = ({ image, text, textColor }) => { 5 | 6 | return ( 7 |
8 | 9 |

{text}

20 | 21 |
22 | ) 23 | } 24 | 25 | export default Hero 26 | -------------------------------------------------------------------------------- /src/layouts/Hero/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Hero'; 2 | -------------------------------------------------------------------------------- /src/layouts/TextBlock/TextBlock.data.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = () => { 3 | return ` 4 | ... on WPGraphQL_Page_Pagebuilder_Layouts_TextBlock { 5 | fieldGroupName 6 | text 7 | textColor 8 | backgroundColor 9 | } 10 | ` 11 | } 12 | -------------------------------------------------------------------------------- /src/layouts/TextBlock/TextBlock.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | const TextBlock = ({ text, textColor, backgroundColor }) => { 4 | 5 | return ( 6 |
7 |
11 |
15 |
16 | 17 |
18 | ) 19 | } 20 | 21 | export default TextBlock 22 | -------------------------------------------------------------------------------- /src/layouts/TextBlock/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TextBlock'; 2 | -------------------------------------------------------------------------------- /src/pages/404.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Layout from "../components/Layout" 4 | import SEO from "../components/SEO" 5 | 6 | const NotFoundPage = () => ( 7 | 8 | 9 |

NOT FOUND

10 |

You just hit a route that doesn't exist... the sadness.

11 |
12 | ) 13 | 14 | export default NotFoundPage 15 | -------------------------------------------------------------------------------- /src/templates/fragments.js: -------------------------------------------------------------------------------- 1 | const FluidImageFragment = ` 2 | fragment GatsbyImageSharpFluid_tracedSVG on ImageSharpFluid { 3 | tracedSVG 4 | aspectRatio 5 | src 6 | srcSet 7 | sizes 8 | } 9 | ` 10 | 11 | module.exports.FluidImageFragment = FluidImageFragment 12 | -------------------------------------------------------------------------------- /src/templates/page/data.js: -------------------------------------------------------------------------------- 1 | 2 | const PageTemplateFragment = (layouts) => ` 3 | fragment PageTemplateFragment on WPGraphQL_Page { 4 | id 5 | title 6 | pageId 7 | content 8 | uri 9 | slug 10 | isFrontPage 11 | featuredImage { 12 | sourceUrl 13 | altText 14 | imageFile { 15 | childImageSharp { 16 | fluid(maxHeight: 400, maxWidth: 800, quality: 90, cropFocus: CENTER) { 17 | ...GatsbyImageSharpFluid_tracedSVG 18 | } 19 | } 20 | } 21 | } 22 | pageBuilder { 23 | layouts { 24 | ${layouts} 25 | } 26 | } 27 | } 28 | ` 29 | 30 | module.exports.PageTemplateFragment = PageTemplateFragment 31 | -------------------------------------------------------------------------------- /src/templates/page/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Layout from "../../components/Layout" 4 | import SEO from "../../components/SEO" 5 | import AllLayouts from "../../components/AllLayouts" 6 | 7 | 8 | const Page = ({ pageContext }) => { 9 | const { 10 | page: { title, pageBuilder }, 11 | } = pageContext 12 | 13 | const layouts = pageBuilder.layouts || [] 14 | 15 | return ( 16 | 17 | 18 |

{title}

19 | 20 | { 21 | layouts.map((layout, index) => { 22 | return 23 | }) 24 | } 25 | 26 |
27 | ) 28 | } 29 | 30 | export default Page 31 | -------------------------------------------------------------------------------- /src/templates/page/template.js: -------------------------------------------------------------------------------- 1 | module.exports = (imports) => { 2 | return` 3 | // This is a temporary generated file. Changes to this file will be overwritten eventually! 4 | import React from "react" 5 | 6 | import Layout from "../src/components/Layout" 7 | import SEO from "../src/components/SEO" 8 | 9 | // Sections 10 | ${imports.map(({ componentName, filePath }) => `import ${componentName} from '${filePath}';`).join('\n')} 11 | 12 | const Page = ({ pageContext }) => { 13 | const { 14 | page: { title, pageBuilder }, 15 | } = pageContext 16 | 17 | const layouts = pageBuilder.layouts || [] 18 | 19 | return ( 20 | 21 | 22 |

{title}

23 | 24 | { 25 | layouts.map((layout, index) => { 26 | ${imports.map(({ componentName, layoutType }) => { 27 | return ` 28 | if (layout.fieldGroupName === '${layoutType}') { 29 | return <${componentName} {...layout} key={index} />; 30 | } 31 | ` 32 | }).join('\n')} 33 | }) 34 | } 35 | 36 |
37 | ) 38 | } 39 | 40 | export default Page 41 | ` 42 | } 43 | -------------------------------------------------------------------------------- /src/templates/post/blog.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import Layout from "../../components/Layout" 3 | import PostEntry from "../../components/PostEntry" 4 | import Pagination from "../../components/Pagination" 5 | import SEO from "../../components/SEO" 6 | 7 | const Blog = ({ pageContext }) => { 8 | const { nodes, pageNumber, hasNextPage, itemsPerPage, allPosts } = pageContext 9 | 10 | return ( 11 | 12 | 17 | 18 | {nodes && nodes.map(post => )} 19 | 20 | 26 | 27 | ) 28 | } 29 | 30 | export default Blog 31 | -------------------------------------------------------------------------------- /src/templates/post/data.js: -------------------------------------------------------------------------------- 1 | const PostTemplateFragment = ` 2 | fragment PostTemplateFragment on WPGraphQL_Post { 3 | id 4 | postId 5 | title 6 | content 7 | link 8 | featuredImage { 9 | sourceUrl 10 | altText 11 | imageFile { 12 | childImageSharp { 13 | fluid(maxHeight: 400, maxWidth: 800, quality: 90, cropFocus: CENTER) { 14 | ...GatsbyImageSharpFluid_tracedSVG 15 | } 16 | } 17 | } 18 | } 19 | categories { 20 | nodes { 21 | name 22 | slug 23 | id 24 | } 25 | } 26 | tags { 27 | nodes { 28 | slug 29 | name 30 | id 31 | } 32 | } 33 | author { 34 | name 35 | slug 36 | } 37 | } 38 | ` 39 | 40 | const BlogPreviewFragment = ` 41 | fragment BlogPreviewFragment on WPGraphQL_Post { 42 | id 43 | postId 44 | title 45 | uri 46 | date 47 | slug 48 | excerpt 49 | content 50 | featuredImage { 51 | sourceUrl 52 | altText 53 | imageFile { 54 | childImageSharp { 55 | fluid(maxHeight: 400, maxWidth: 800, quality: 90, cropFocus: CENTER) { 56 | ...GatsbyImageSharpFluid_tracedSVG 57 | } 58 | } 59 | } 60 | } 61 | author { 62 | name 63 | slug 64 | } 65 | } 66 | ` 67 | 68 | module.exports.PostTemplateFragment = PostTemplateFragment 69 | module.exports.BlogPreviewFragment = BlogPreviewFragment 70 | -------------------------------------------------------------------------------- /src/templates/post/index.js: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | import Layout from "../../components/Layout" 4 | import SEO from "../../components/SEO" 5 | import FluidImage from "../../components/FluidImage" 6 | 7 | 8 | const Post = ({ pageContext }) => { 9 | const { 10 | post: { title, content, featuredImage }, 11 | } = pageContext 12 | 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 |

{title}

20 |
21 | 22 | ) 23 | } 24 | 25 | export default Post 26 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Parses a menu item object and returns Gatsby-field URI. 3 | * 4 | * @param {object} menuItem a single menu item 5 | * @param wordPressUrl 6 | * @param blogURI 7 | */ 8 | export const CreateLocalLink = (menuItem, wordPressUrl, blogURI='blog/') => { 9 | const { url, connectedObject } = menuItem; 10 | 11 | if (url === '#') { 12 | return null; 13 | } 14 | /** 15 | * Always want to pull of our API URL. 16 | */ 17 | let newUri = url.replace(wordPressUrl, ''); 18 | 19 | /** 20 | * If it's a blog link, respect the users blogURI setting. 21 | */ 22 | if (connectedObject && connectedObject.__typename === 'WPGraphQL_Post') { 23 | newUri = blogURI + newUri; 24 | } 25 | 26 | return newUri; 27 | }; 28 | -------------------------------------------------------------------------------- /wordpress/acf-export.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "key": "group_5decc2241a06d", 4 | "title": "Page Builder", 5 | "fields": [ 6 | { 7 | "key": "field_5decc2474dcfc", 8 | "label": "Layouts", 9 | "name": "layouts", 10 | "type": "flexible_content", 11 | "instructions": "", 12 | "required": 0, 13 | "conditional_logic": 0, 14 | "wrapper": { 15 | "width": "", 16 | "class": "", 17 | "id": "" 18 | }, 19 | "show_in_graphql": 1, 20 | "layouts": { 21 | "layout_5decc25b9abd5": { 22 | "key": "layout_5decc25b9abd5", 23 | "name": "hero", 24 | "label": "Hero", 25 | "display": "block", 26 | "sub_fields": [ 27 | { 28 | "key": "field_5decc2c051eeb", 29 | "label": "Image", 30 | "name": "image", 31 | "type": "image", 32 | "instructions": "", 33 | "required": 1, 34 | "conditional_logic": 0, 35 | "wrapper": { 36 | "width": "", 37 | "class": "", 38 | "id": "" 39 | }, 40 | "show_in_graphql": 1, 41 | "return_format": "array", 42 | "preview_size": "medium", 43 | "library": "all", 44 | "min_width": "", 45 | "min_height": "", 46 | "min_size": "", 47 | "max_width": "", 48 | "max_height": "", 49 | "max_size": "", 50 | "mime_types": "" 51 | }, 52 | { 53 | "key": "field_5decc4477634e", 54 | "label": "Text", 55 | "name": "text", 56 | "type": "text", 57 | "instructions": "This text will be placed on top of the image.", 58 | "required": 0, 59 | "conditional_logic": 0, 60 | "wrapper": { 61 | "width": "", 62 | "class": "", 63 | "id": "" 64 | }, 65 | "show_in_graphql": 1, 66 | "default_value": "", 67 | "placeholder": "", 68 | "prepend": "", 69 | "append": "", 70 | "maxlength": "" 71 | }, 72 | { 73 | "key": "field_5decc46e7634f", 74 | "label": "Text Color", 75 | "name": "text_color", 76 | "type": "color_picker", 77 | "instructions": "The color of the text. Default is white.", 78 | "required": 0, 79 | "conditional_logic": 0, 80 | "wrapper": { 81 | "width": "", 82 | "class": "", 83 | "id": "" 84 | }, 85 | "show_in_graphql": 1, 86 | "default_value": "#FFFFFF" 87 | } 88 | ], 89 | "min": "", 90 | "max": "" 91 | }, 92 | "layout_5decc4a5d2fc7": { 93 | "key": "layout_5decc4a5d2fc7", 94 | "name": "text_block", 95 | "label": "Text Block", 96 | "display": "block", 97 | "sub_fields": [ 98 | { 99 | "key": "field_5decc4c4d2fc8", 100 | "label": "Text", 101 | "name": "text", 102 | "type": "wysiwyg", 103 | "instructions": "", 104 | "required": 0, 105 | "conditional_logic": 0, 106 | "wrapper": { 107 | "width": "", 108 | "class": "", 109 | "id": "" 110 | }, 111 | "show_in_graphql": 1, 112 | "default_value": "", 113 | "tabs": "all", 114 | "toolbar": "full", 115 | "media_upload": 1, 116 | "delay": 0 117 | }, 118 | { 119 | "key": "field_5decc52fd2fca", 120 | "label": "Text Color", 121 | "name": "text_color", 122 | "type": "color_picker", 123 | "instructions": "The color of the text. Make sure it has enough contrast to the background color.", 124 | "required": 0, 125 | "conditional_logic": 0, 126 | "wrapper": { 127 | "width": "", 128 | "class": "", 129 | "id": "" 130 | }, 131 | "show_in_graphql": 1, 132 | "default_value": "#000000" 133 | }, 134 | { 135 | "key": "field_5decc509d2fc9", 136 | "label": "Background Color", 137 | "name": "background_color", 138 | "type": "color_picker", 139 | "instructions": "The background color. Make sure it has enough contrast to the text color.", 140 | "required": 0, 141 | "conditional_logic": 0, 142 | "wrapper": { 143 | "width": "", 144 | "class": "", 145 | "id": "" 146 | }, 147 | "show_in_graphql": 1, 148 | "default_value": "#FFFFFF" 149 | } 150 | ], 151 | "min": "", 152 | "max": "" 153 | } 154 | }, 155 | "button_label": "Add Row", 156 | "min": "", 157 | "max": "" 158 | } 159 | ], 160 | "location": [ 161 | [ 162 | { 163 | "param": "post_type", 164 | "operator": "==", 165 | "value": "page" 166 | } 167 | ] 168 | ], 169 | "menu_order": 0, 170 | "position": "normal", 171 | "style": "default", 172 | "label_placement": "top", 173 | "instruction_placement": "label", 174 | "hide_on_screen": [ 175 | "the_content", 176 | "discussion", 177 | "comments", 178 | "send-trackbacks" 179 | ], 180 | "active": true, 181 | "description": "", 182 | "show_in_graphql": 1, 183 | "graphql_field_name": "pageBuilder" 184 | } 185 | ] --------------------------------------------------------------------------------