├── .babelrc ├── .contentful.json.sample ├── .github └── CODEOWNERS ├── .gitignore ├── .npmrc ├── .nvmrc ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── WHATS-NEXT.md ├── app.json ├── bin ├── hello.js └── setup.js ├── catalog-info.yaml ├── contentful └── export.json ├── gatsby-config.js ├── gatsby-node.js ├── netlify.toml ├── package-lock.json ├── package.json ├── screenshot.png ├── src ├── components │ ├── article-preview.js │ ├── article-preview.module.css │ ├── container.js │ ├── footer.js │ ├── footer.module.css │ ├── global.css │ ├── hero.js │ ├── hero.module.css │ ├── layout.js │ ├── navigation.js │ ├── navigation.module.css │ ├── seo.js │ ├── tags.js │ ├── tags.module.css │ └── variables.css ├── pages │ ├── 404.js │ ├── blog.js │ └── index.js └── templates │ ├── blog-post.js │ └── blog-post.module.css ├── static.json └── static ├── favicon.ico ├── fonts ├── Inter-italic.var.woff2 ├── Inter-roman.var.woff2 └── LICENSE.txt └── robots.txt /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "babel-preset-gatsby", 5 | { 6 | "targets": { 7 | "browsers": [">0.25%", "not dead"] 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.contentful.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "spaceId": "...", 3 | "accessToken": "..." 4 | } 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @contentful/team-devrel -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env* 59 | 60 | # Gatsby build folder 61 | public 62 | 63 | # Gatsby cache folder 64 | .cache 65 | 66 | # Contentful config file 67 | .contentful.json 68 | 69 | # Local Netlify folder 70 | .netlify 71 | 72 | # System files 73 | .DS_Store 74 | Thumbs.db -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "semi": false 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: "12" 3 | after_success: npm run build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Contentful Gatsby Starter Blog 2 | 3 | Create a [Gatsby](http://gatsbyjs.com/) blog powered by [Contentful](https://www.contentful.com). 4 | 5 | ![An article page of the starter blog](./screenshot.png "An article page of the starter blog") 6 | 7 | Static sites are scalable, secure and have very little required maintenance. They come with a drawback though. Not everybody feels good editing files, building a project and uploading it somewhere. This is where Contentful comes into play. 8 | 9 | With Contentful and Gatsby you can connect your favorite static site generator with an API that provides an easy to use interface for people writing content and automate the publishing using services like [Travis CI](https://travis-ci.org/) or [Netlify](https://www.netlify.com/). 10 | 11 | ## Features 12 | 13 | - Simple content model and structure. Easy to adjust to your needs. 14 | - Use the [synchronization feature](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization) of our [Delivery API](https://www.contentful.com/developers/docs/references/content-delivery-api/). 15 | - Responsive/adaptive images via [gatsby-plugin-image](https://www.gatsbyjs.org/packages/gatsby-plugin-image/) and our [Images API](https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization/initial-synchronization-of-entries-of-a-specific-content-type). 16 | 17 | ## Getting started 18 | 19 | See our [official Contentful getting started guide](https://www.contentful.com/developers/docs/tutorials/general/get-started/). 20 | 21 | ### Get the source code and install dependencies. 22 | 23 | ``` 24 | $ git clone https://github.com/contentful/starter-gatsby-blog.git 25 | $ npm install 26 | ``` 27 | 28 | Or use Gatsby Cloud 29 | 30 | Use Deploy Now to get started in [Gatsby Cloud](https://gatsbyjs.com/products/cloud): 31 | 32 | [Deploy to Gatsby Cloud](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/contentful/starter-gatsby-blog) 33 | 34 | If you use Deploy Now, Gatsby Cloud will run the `gatsby-provision` script on your behalf, if you choose, after you Quick Connected to your empty Contentful Space. That script will add the necessary content models and content to support this site. 35 | 36 | 37 | Or use the [Gatsby CLI](https://www.npmjs.com/package/gatsby-cli). 38 | 39 | ``` 40 | $ gatsby new contentful-starter-blog https://github.com/contentful/starter-gatsby-blog/ 41 | ``` 42 | 43 | ### Set up of the needed content model and create a configuration file 44 | 45 | This project comes with a Contentful setup command `npm run setup`. 46 | 47 | This command will ask you for a space ID, and access tokens for the Contentful Management and Delivery API and then import the needed content model into the space you define and write a config file (`./.contentful.json`). 48 | 49 | `npm run setup` automates that for you but if you want to do it yourself rename `.contentful.json.sample` to `.contentful.json` and add your configuration in this file. 50 | 51 | ## Crucial Commands 52 | 53 | ### `npm run dev` 54 | 55 | Run the project locally with live reload in development mode. 56 | 57 | ### `npm run build` 58 | 59 | Run a production build into `./public`. The result is ready to be put on any static hosting you prefer. 60 | 61 | ### `npm run serve` 62 | 63 | Spin up a production-ready server with your blog. Don't forget to build your page beforehand. 64 | 65 | ## Deployment 66 | 67 | See the [official Contentful getting started guide](https://www.contentful.com/developers/docs/tutorials/general/get-started/). 68 | 69 | ## Contribution 70 | 71 | Feel free to open pull requests to fix bugs. If you want to add features, please have a look at the [original version](https://github.com/contentful-userland/gatsby-contentful-starter). It is always open to contributions and pull requests. 72 | 73 | You can learn more about how Contentful userland is organized by visiting [our about repository](https://github.com/contentful-userland/about). 74 | -------------------------------------------------------------------------------- /WHATS-NEXT.md: -------------------------------------------------------------------------------- 1 | ## Deploy the blog 2 | 3 | A blog on your local machine is nice to have, but a blog on the internet is even better. Return to the [tutorial doc](https://www.contentful.com/developers/docs/tutorials/general/get-started/#deploy-the-starter-gatsby-blog), select a hosting service (Gatsby Cloud, Netlify, or Heroku) and follow the instructions to deploy. 4 | 5 | ## Explore the blog content structure 6 | 7 | Return to the [tutorial doc](https://www.contentful.com/developers/docs/tutorials/general/get-started/#explore-how-the-sample-website-is-built-with-contentful) to view the relationship between the blog content and the data model. 8 | 9 | ## Modify content and redeploy 10 | 11 | Follow this [tutorial](https://www.contentful.com/developers/docs/tutorials/general/automate-site-builds-with-webhooks/) to learn how to use webhooks to automate the process of redeploying your site after publishing new content. 12 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Contentful Gatsby Starter", 3 | "description": "Gatsby starter for a Contentful project.", 4 | "keywords": [ 5 | "contentful", 6 | "gatsby", 7 | "static", 8 | "ssg" 9 | ], 10 | "repository": "https://github.com/contentful-userland/gatsby-contentful-starter", 11 | "success_url": "/", 12 | "buildpacks": [ 13 | { 14 | "url": "heroku/nodejs" 15 | }, 16 | { 17 | "url": "https://github.com/heroku/heroku-buildpack-static" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /bin/hello.js: -------------------------------------------------------------------------------- 1 | const chalk = require("chalk"); 2 | const pkg = require("../package.json"); 3 | 4 | console.log(` 5 | 6 | ${chalk.green("Hey there! 👋")} 7 | 8 | Thanks for giving the ${pkg.name} a try. 🎉 9 | To get you going really quickly this project includes a setup step. 10 | 11 | ${chalk.yellow.bold("npm run setup")} automates the following steps for you: 12 | - creates a config file ${chalk.yellow("./.contentful.json")} 13 | - imports ${chalk.green("a predefined content model")} 14 | 15 | When this is done run: 16 | 17 | ${chalk.yellow( 18 | "npm run dev" 19 | )} to start a development environment at ${chalk.green("localhost:8000")} 20 | 21 | or 22 | 23 | ${chalk.yellow( 24 | "npm run build" 25 | )} to create a production ready static site in ${chalk.green("./public")} 26 | 27 | For further information check the readme of the project 28 | (https://github.com/contentful/starter-gatsby-blog) 29 | 30 | `); 31 | -------------------------------------------------------------------------------- /bin/setup.js: -------------------------------------------------------------------------------- 1 | const spaceImport = require("contentful-import"); 2 | const exportFile = require("../contentful/export.json"); 3 | const inquirer = require("inquirer"); 4 | const chalk = require("chalk"); 5 | const path = require("path"); 6 | const { writeFileSync } = require("fs"); 7 | 8 | const argv = require("yargs-parser")(process.argv.slice(2)); 9 | 10 | console.log(` 11 | To set up this project you need to provide your Space ID 12 | and the belonging API access tokens. Please use an empty space for this. 13 | 14 | You can find all the needed information in your Contentful space under: 15 | 16 | ${chalk.yellow( 17 | `app.contentful.com ${chalk.red("->")} Space Settings ${chalk.red( 18 | "->" 19 | )} API keys` 20 | )} 21 | 22 | The ${chalk.green("Content Management API Token")} 23 | will be used to import and write data to your space. 24 | 25 | The ${chalk.green("Content Delivery API Token")} 26 | will be used to ship published production-ready content in your Gatsby app. 27 | 28 | The ${chalk.green("Content Preview API Token")} 29 | will be used to show not published data in your development environment. 30 | 31 | Ready? Let's do it! 🎉 32 | `); 33 | 34 | const questions = [ 35 | { 36 | name: "spaceId", 37 | message: "Your Space ID", 38 | when: !argv.spaceId && !process.env.CONTENTFUL_SPACE_ID, 39 | validate: (input) => 40 | /^[a-z0-9]{12}$/.test(input) || 41 | "Space ID must be 12 lowercase characters", 42 | }, 43 | { 44 | name: "managementToken", 45 | when: !argv.managementToken, 46 | message: "Your Content Management API access token", 47 | }, 48 | { 49 | name: "accessToken", 50 | when: 51 | !argv.accessToken && 52 | !process.env.CONTENTFUL_ACCESS_TOKEN, 53 | message: "Your Content Delivery API access token", 54 | }, 55 | ]; 56 | 57 | inquirer 58 | .prompt(questions) 59 | .then(({ spaceId, managementToken, accessToken }) => { 60 | const { 61 | CONTENTFUL_SPACE_ID, 62 | CONTENTFUL_ACCESS_TOKEN, 63 | } = process.env; 64 | 65 | let config = { 66 | spaceId: CONTENTFUL_SPACE_ID || argv.spaceId || spaceId, 67 | accessToken: CONTENTFUL_ACCESS_TOKEN || 68 | argv.accessToken || 69 | accessToken, 70 | } 71 | 72 | managementToken = argv.managementToken || managementToken; 73 | 74 | console.log("Writing config file..."); 75 | const configFiles = [`.env.development`, `.env.production`].map((file) => 76 | path.join(__dirname, "..", file) 77 | ); 78 | 79 | configFiles.forEach((file) => { 80 | const fileArr = 81 | [ 82 | `# All environment variables will be sourced`, 83 | `# and made available to gatsby-config.js, gatsby-node.js, etc.`, 84 | `# Do NOT commit this file to source control`, 85 | `CONTENTFUL_SPACE_ID='${config.spaceId}'`, 86 | `CONTENTFUL_ACCESS_TOKEN='${config.accessToken}'`, 87 | ]; 88 | 89 | writeFileSync(file, fileArr.concat( 90 | file.includes('development') ? [ 91 | `# To add draft content preview, uncomment the below line and use the Content Preview API Access Token`, 92 | `# CONTENTFUL_HOST='preview.contentful.com'` 93 | ] : [] 94 | ).filter(Boolean).join("\n") + "\n", "utf8"); 95 | console.log(`Config file ${chalk.yellow(file)} written`); 96 | }); 97 | return { spaceId, managementToken }; 98 | }) 99 | .then(({ spaceId, managementToken }) => 100 | spaceImport({ spaceId, managementToken, content: exportFile }) 101 | ) 102 | .then((_, error) => { 103 | console.log( 104 | `All set! You can now run ${chalk.yellow( 105 | "npm run dev" 106 | )} to see it in action.` 107 | ); 108 | }) 109 | .catch((error) => console.error(error)); 110 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | # Backstage documentation 2 | # https://backstage.io/docs/features/software-catalog/descriptor-format/ 3 | 4 | # !!! WARNING !!! 5 | # This is a template file with a number of fields that need to be filled before merging this to the default branch 6 | apiVersion: backstage.io/v1alpha1 7 | # Component, API, Template, Group, User, Resource, System, Domain, Location 8 | kind: Component 9 | metadata: 10 | name: starter-gatsby-blog 11 | description: unknown 12 | annotations: 13 | github.com/project-slug: contentful/starter-gatsby-blog 14 | contentful.com/service-tier: "unknown" #1, 2, 3, 4 15 | 16 | tags: 17 | - update-me 18 | #need to add sast.yaml to .github/workflows and enable it in polaris dashboard 19 | #once that is done this can be changed to sast-enabled 20 | - sast-disabled 21 | #make this match the value from service-tier above 22 | - tier-unknown 23 | spec: 24 | #cli, component, contentful.com/template, documentation, function, library, service, template, website 25 | type: unknown 26 | #deprecated, experimental, production, unknown 27 | lifecycle: unknown 28 | system: unknown #optional 29 | # your team name as it appears in github when tagging them for reviews 30 | owner: group:team-devrel 31 | -------------------------------------------------------------------------------- /contentful/export.json: -------------------------------------------------------------------------------- 1 | { 2 | "contentTypes": [ 3 | { 4 | "sys": { 5 | "space": { 6 | "sys": { 7 | "type": "Link", 8 | "linkType": "Space", 9 | "id": "28p9vvm1oxuw" 10 | } 11 | }, 12 | "id": "person", 13 | "type": "ContentType", 14 | "createdAt": "2017-05-11T12:04:23.540Z", 15 | "updatedAt": "2017-05-18T14:04:51.650Z", 16 | "createdBy": { 17 | "sys": { 18 | "type": "Link", 19 | "linkType": "User", 20 | "id": "066RqBikAjzKy0SWUEtFvH" 21 | } 22 | }, 23 | "updatedBy": { 24 | "sys": { 25 | "type": "Link", 26 | "linkType": "User", 27 | "id": "2AAFsI4st4sZPlF1LFT13q" 28 | } 29 | }, 30 | "publishedCounter": 7, 31 | "version": 14, 32 | "publishedBy": { 33 | "sys": { 34 | "type": "Link", 35 | "linkType": "User", 36 | "id": "2AAFsI4st4sZPlF1LFT13q" 37 | } 38 | }, 39 | "publishedVersion": 13, 40 | "firstPublishedAt": "2017-05-11T12:04:23.869Z", 41 | "publishedAt": "2017-05-18T14:04:51.628Z" 42 | }, 43 | "displayField": "name", 44 | "name": "Person", 45 | "description": "", 46 | "fields": [ 47 | { 48 | "id": "name", 49 | "name": "Name", 50 | "type": "Symbol", 51 | "localized": false, 52 | "required": true, 53 | "validations": [], 54 | "disabled": false, 55 | "omitted": false 56 | }, 57 | { 58 | "id": "title", 59 | "name": "Title", 60 | "type": "Symbol", 61 | "localized": false, 62 | "required": true, 63 | "validations": [], 64 | "disabled": false, 65 | "omitted": false 66 | }, 67 | { 68 | "id": "company", 69 | "name": "Company", 70 | "type": "Symbol", 71 | "localized": false, 72 | "required": true, 73 | "validations": [], 74 | "disabled": false, 75 | "omitted": false 76 | }, 77 | { 78 | "id": "shortBio", 79 | "name": "Short Bio", 80 | "type": "RichText", 81 | "localized": false, 82 | "required": true, 83 | "validations": [], 84 | "disabled": false, 85 | "omitted": false 86 | }, 87 | { 88 | "id": "email", 89 | "name": "Email", 90 | "type": "Symbol", 91 | "localized": false, 92 | "required": false, 93 | "validations": [], 94 | "disabled": false, 95 | "omitted": false 96 | }, 97 | { 98 | "id": "phone", 99 | "name": "Phone", 100 | "type": "Symbol", 101 | "localized": false, 102 | "required": false, 103 | "validations": [], 104 | "disabled": false, 105 | "omitted": false 106 | }, 107 | { 108 | "id": "facebook", 109 | "name": "Facebook", 110 | "type": "Symbol", 111 | "localized": false, 112 | "required": false, 113 | "validations": [], 114 | "disabled": false, 115 | "omitted": false 116 | }, 117 | { 118 | "id": "twitter", 119 | "name": "Twitter", 120 | "type": "Symbol", 121 | "localized": false, 122 | "required": false, 123 | "validations": [], 124 | "disabled": false, 125 | "omitted": false 126 | }, 127 | { 128 | "id": "github", 129 | "name": "Github", 130 | "type": "Symbol", 131 | "localized": false, 132 | "required": false, 133 | "validations": [], 134 | "disabled": false, 135 | "omitted": false 136 | }, 137 | { 138 | "id": "image", 139 | "name": "Image", 140 | "type": "Link", 141 | "localized": false, 142 | "required": false, 143 | "validations": [], 144 | "disabled": false, 145 | "omitted": false, 146 | "linkType": "Asset" 147 | } 148 | ] 149 | }, 150 | { 151 | "sys": { 152 | "space": { 153 | "sys": { 154 | "type": "Link", 155 | "linkType": "Space", 156 | "id": "28p9vvm1oxuw" 157 | } 158 | }, 159 | "id": "blogPost", 160 | "type": "ContentType", 161 | "createdAt": "2017-05-11T12:17:42.862Z", 162 | "updatedAt": "2017-05-17T10:55:24.031Z", 163 | "createdBy": { 164 | "sys": { 165 | "type": "Link", 166 | "linkType": "User", 167 | "id": "066RqBikAjzKy0SWUEtFvH" 168 | } 169 | }, 170 | "updatedBy": { 171 | "sys": { 172 | "type": "Link", 173 | "linkType": "User", 174 | "id": "066RqBikAjzKy0SWUEtFvH" 175 | } 176 | }, 177 | "publishedCounter": 6, 178 | "version": 12, 179 | "publishedBy": { 180 | "sys": { 181 | "type": "Link", 182 | "linkType": "User", 183 | "id": "066RqBikAjzKy0SWUEtFvH" 184 | } 185 | }, 186 | "publishedVersion": 11, 187 | "firstPublishedAt": "2017-05-11T12:17:43.175Z", 188 | "publishedAt": "2017-05-17T10:55:24.004Z" 189 | }, 190 | "displayField": "title", 191 | "name": "Blog Post", 192 | "description": "", 193 | "fields": [ 194 | { 195 | "id": "title", 196 | "name": "Title", 197 | "type": "Symbol", 198 | "localized": false, 199 | "required": true, 200 | "validations": [], 201 | "disabled": false, 202 | "omitted": false 203 | }, 204 | { 205 | "id": "slug", 206 | "name": "Slug", 207 | "type": "Symbol", 208 | "localized": false, 209 | "required": true, 210 | "validations": [], 211 | "disabled": false, 212 | "omitted": false 213 | }, 214 | { 215 | "id": "heroImage", 216 | "name": "Hero Image", 217 | "type": "Link", 218 | "localized": false, 219 | "required": true, 220 | "validations": [], 221 | "disabled": false, 222 | "omitted": false, 223 | "linkType": "Asset" 224 | }, 225 | { 226 | "id": "description", 227 | "name": "Description", 228 | "type": "RichText", 229 | "localized": false, 230 | "required": true, 231 | "validations": [], 232 | "disabled": false, 233 | "omitted": false 234 | }, 235 | { 236 | "id": "body", 237 | "name": "Body", 238 | "type": "RichText", 239 | "localized": false, 240 | "required": true, 241 | "validations": [], 242 | "disabled": false, 243 | "omitted": false 244 | }, 245 | { 246 | "id": "author", 247 | "name": "Author", 248 | "type": "Link", 249 | "localized": false, 250 | "required": false, 251 | "validations": [ 252 | { 253 | "linkContentType": ["person"] 254 | } 255 | ], 256 | "disabled": false, 257 | "omitted": false, 258 | "linkType": "Entry" 259 | }, 260 | { 261 | "id": "publishDate", 262 | "name": "Publish Date", 263 | "type": "Date", 264 | "localized": false, 265 | "required": true, 266 | "validations": [], 267 | "disabled": false, 268 | "omitted": false 269 | }, 270 | { 271 | "id": "tags", 272 | "name": "Tags", 273 | "type": "Array", 274 | "localized": false, 275 | "required": false, 276 | "validations": [], 277 | "disabled": false, 278 | "omitted": false, 279 | "items": { 280 | "type": "Symbol", 281 | "validations": [ 282 | { 283 | "in": ["general", "javascript", "static-sites"] 284 | } 285 | ] 286 | } 287 | } 288 | ] 289 | } 290 | ], 291 | "editorInterfaces": [ 292 | { 293 | "controls": [ 294 | { 295 | "fieldId": "name", 296 | "widgetId": "singleLine" 297 | }, 298 | { 299 | "fieldId": "title", 300 | "widgetId": "singleLine" 301 | }, 302 | { 303 | "fieldId": "company", 304 | "widgetId": "singleLine" 305 | }, 306 | { 307 | "fieldId": "shortBio", 308 | "widgetId": "richTextEditor" 309 | }, 310 | { 311 | "fieldId": "email", 312 | "widgetId": "singleLine" 313 | }, 314 | { 315 | "fieldId": "phone", 316 | "widgetId": "singleLine" 317 | }, 318 | { 319 | "fieldId": "facebook", 320 | "widgetId": "singleLine" 321 | }, 322 | { 323 | "fieldId": "twitter", 324 | "widgetId": "singleLine" 325 | }, 326 | { 327 | "fieldId": "github", 328 | "widgetId": "singleLine" 329 | }, 330 | { 331 | "fieldId": "image", 332 | "widgetId": "assetLinkEditor" 333 | } 334 | ], 335 | "sys": { 336 | "id": "default", 337 | "type": "EditorInterface", 338 | "version": 14, 339 | "createdAt": "2017-05-11T12:04:24.145Z", 340 | "createdBy": { 341 | "sys": { 342 | "type": "Link", 343 | "linkType": "User", 344 | "id": "066RqBikAjzKy0SWUEtFvH" 345 | } 346 | }, 347 | "space": { 348 | "sys": { 349 | "type": "Link", 350 | "linkType": "Space", 351 | "id": "28p9vvm1oxuw" 352 | } 353 | }, 354 | "contentType": { 355 | "sys": { 356 | "type": "Link", 357 | "linkType": "ContentType", 358 | "id": "person" 359 | } 360 | }, 361 | "updatedAt": "2017-05-18T14:04:52.378Z", 362 | "updatedBy": { 363 | "sys": { 364 | "type": "Link", 365 | "linkType": "User", 366 | "id": "2AAFsI4st4sZPlF1LFT13q" 367 | } 368 | } 369 | } 370 | }, 371 | { 372 | "controls": [ 373 | { 374 | "fieldId": "title", 375 | "widgetId": "singleLine" 376 | }, 377 | { 378 | "fieldId": "slug", 379 | "widgetId": "slugEditor" 380 | }, 381 | { 382 | "fieldId": "heroImage", 383 | "widgetId": "assetLinkEditor" 384 | }, 385 | { 386 | "fieldId": "description", 387 | "widgetId": "richTextEditor" 388 | }, 389 | { 390 | "fieldId": "body", 391 | "widgetId": "richTextEditor" 392 | }, 393 | { 394 | "fieldId": "author", 395 | "widgetId": "entryLinkEditor" 396 | }, 397 | { 398 | "fieldId": "publishDate", 399 | "widgetId": "datePicker", 400 | "settings": { 401 | "format": "timeZ", 402 | "ampm": "24" 403 | } 404 | }, 405 | { 406 | "fieldId": "tags", 407 | "widgetId": "tagEditor" 408 | } 409 | ], 410 | "sys": { 411 | "id": "default", 412 | "type": "EditorInterface", 413 | "version": 12, 414 | "createdAt": "2017-05-11T12:17:43.510Z", 415 | "createdBy": { 416 | "sys": { 417 | "type": "Link", 418 | "linkType": "User", 419 | "id": "066RqBikAjzKy0SWUEtFvH" 420 | } 421 | }, 422 | "space": { 423 | "sys": { 424 | "type": "Link", 425 | "linkType": "Space", 426 | "id": "28p9vvm1oxuw" 427 | } 428 | }, 429 | "contentType": { 430 | "sys": { 431 | "type": "Link", 432 | "linkType": "ContentType", 433 | "id": "blogPost" 434 | } 435 | }, 436 | "updatedAt": "2017-05-17T10:55:25.370Z", 437 | "updatedBy": { 438 | "sys": { 439 | "type": "Link", 440 | "linkType": "User", 441 | "id": "066RqBikAjzKy0SWUEtFvH" 442 | } 443 | } 444 | } 445 | } 446 | ], 447 | "entries": [ 448 | { 449 | "sys": { 450 | "space": { 451 | "sys": { 452 | "type": "Link", 453 | "linkType": "Space", 454 | "id": "28p9vvm1oxuw" 455 | } 456 | }, 457 | "id": "15jwOBqpxqSAOy2eOO4S0m", 458 | "type": "Entry", 459 | "createdAt": "2017-05-11T12:04:29.532Z", 460 | "updatedAt": "2017-05-18T14:13:14.463Z", 461 | "createdBy": { 462 | "sys": { 463 | "type": "Link", 464 | "linkType": "User", 465 | "id": "066RqBikAjzKy0SWUEtFvH" 466 | } 467 | }, 468 | "updatedBy": { 469 | "sys": { 470 | "type": "Link", 471 | "linkType": "User", 472 | "id": "2AAFsI4st4sZPlF1LFT13q" 473 | } 474 | }, 475 | "publishedCounter": 15, 476 | "version": 190, 477 | "publishedBy": { 478 | "sys": { 479 | "type": "Link", 480 | "linkType": "User", 481 | "id": "2AAFsI4st4sZPlF1LFT13q" 482 | } 483 | }, 484 | "publishedVersion": 189, 485 | "firstPublishedAt": "2017-05-11T12:06:33.065Z", 486 | "publishedAt": "2017-05-18T14:13:14.431Z", 487 | "contentType": { 488 | "sys": { 489 | "type": "Link", 490 | "linkType": "ContentType", 491 | "id": "person" 492 | } 493 | } 494 | }, 495 | "fields": { 496 | "name": { 497 | "en-US": "John Doe" 498 | }, 499 | "title": { 500 | "en-US": "Web Developer" 501 | }, 502 | "company": { 503 | "en-US": "ACME" 504 | }, 505 | "shortBio": { 506 | "en-US": { 507 | "nodeType": "document", 508 | "data": { 509 | }, 510 | "content": [ 511 | { 512 | "nodeType": "paragraph", 513 | "data": { 514 | }, 515 | "content": [ 516 | { 517 | "nodeType": "text", 518 | "value": "Research and recommendations for modern stack websites.", 519 | "marks": [ 520 | ], 521 | "data": { 522 | } 523 | } 524 | ] 525 | } 526 | ] 527 | } 528 | }, 529 | "email": { 530 | "en-US": "john@doe.com" 531 | }, 532 | "phone": { 533 | "en-US": "0176 / 1234567" 534 | }, 535 | "facebook": { 536 | "en-US": "johndoe" 537 | }, 538 | "twitter": { 539 | "en-US": "johndoe" 540 | }, 541 | "github": { 542 | "en-US": "johndoe" 543 | }, 544 | "image": { 545 | "en-US": { 546 | "sys": { 547 | "type": "Link", 548 | "linkType": "Asset", 549 | "id": "7orLdboQQowIUs22KAW4U" 550 | } 551 | } 552 | } 553 | } 554 | }, 555 | { 556 | "sys": { 557 | "space": { 558 | "sys": { 559 | "type": "Link", 560 | "linkType": "Space", 561 | "id": "28p9vvm1oxuw" 562 | } 563 | }, 564 | "id": "31TNnjHlfaGUoMOwU0M2og", 565 | "type": "Entry", 566 | "createdAt": "2017-05-12T09:29:02.336Z", 567 | "updatedAt": "2017-05-30T12:55:11.986Z", 568 | "createdBy": { 569 | "sys": { 570 | "type": "Link", 571 | "linkType": "User", 572 | "id": "066RqBikAjzKy0SWUEtFvH" 573 | } 574 | }, 575 | "updatedBy": { 576 | "sys": { 577 | "type": "Link", 578 | "linkType": "User", 579 | "id": "2AAFsI4st4sZPlF1LFT13q" 580 | } 581 | }, 582 | "publishedCounter": 13, 583 | "version": 319, 584 | "publishedBy": { 585 | "sys": { 586 | "type": "Link", 587 | "linkType": "User", 588 | "id": "2AAFsI4st4sZPlF1LFT13q" 589 | } 590 | }, 591 | "publishedVersion": 318, 592 | "firstPublishedAt": "2017-05-12T09:30:09.613Z", 593 | "publishedAt": "2017-05-30T12:55:11.950Z", 594 | "contentType": { 595 | "sys": { 596 | "type": "Link", 597 | "linkType": "ContentType", 598 | "id": "blogPost" 599 | } 600 | } 601 | }, 602 | "fields": { 603 | "title": { 604 | "en-US": "Automate with webhooks" 605 | }, 606 | "slug": { 607 | "en-US": "automate-with-webhooks" 608 | }, 609 | "heroImage": { 610 | "en-US": { 611 | "sys": { 612 | "type": "Link", 613 | "linkType": "Asset", 614 | "id": "4shwYI3POEGkw0Eg6kcyaQ" 615 | } 616 | } 617 | }, 618 | "description": { 619 | "en-US": { 620 | "data": { 621 | }, 622 | "content": [ 623 | { 624 | "data": { 625 | }, 626 | "content": [ 627 | { 628 | "data": { 629 | }, 630 | "marks": [ 631 | ], 632 | "value": "Webhooks notify you, another person or system when resources have changed by calling a given HTTP endpoint.", 633 | "nodeType": "text" 634 | } 635 | ], 636 | "nodeType": "paragraph" 637 | } 638 | ], 639 | "nodeType": "document" 640 | } 641 | }, 642 | "body": { 643 | "en-US": { 644 | "nodeType": "document", 645 | "data": { 646 | }, 647 | "content": [ 648 | { 649 | "nodeType": "heading-2", 650 | "data": { 651 | }, 652 | "content": [ 653 | { 654 | "nodeType": "text", 655 | "value": "What are webhooks?", 656 | "marks": [ 657 | ], 658 | "data": { 659 | } 660 | } 661 | ] 662 | }, 663 | { 664 | "nodeType": "paragraph", 665 | "data": { 666 | }, 667 | "content": [ 668 | { 669 | "nodeType": "text", 670 | "value": "The webhooks are used to notify you when content has been changed. Specify a URL, configure your webhook, and we will send an HTTP POST request whenever something happens to your content.", 671 | "marks": [ 672 | ], 673 | "data": { 674 | } 675 | } 676 | ] 677 | }, 678 | { 679 | "nodeType": "heading-2", 680 | "data": { 681 | }, 682 | "content": [ 683 | { 684 | "nodeType": "text", 685 | "value": "How do I configure a webhook?", 686 | "marks": [ 687 | ], 688 | "data": { 689 | } 690 | } 691 | ] 692 | }, 693 | { 694 | "nodeType": "paragraph", 695 | "data": { 696 | }, 697 | "content": [ 698 | { 699 | "nodeType": "text", 700 | "value": "Go to Settings → Webhooks from the navigation bar at the top. From there, hit Add webhook, and you will be directed to your new webhook. Then choose a name, put in the information of your HTTP endpoint (URL and authentication), specify any custom headers and select the types of events that should trigger the webhook.", 701 | "marks": [ 702 | ], 703 | "data": { 704 | } 705 | } 706 | ] 707 | }, 708 | { 709 | "nodeType": "heading-2", 710 | "data": { 711 | }, 712 | "content": [ 713 | { 714 | "nodeType": "text", 715 | "value": "Why do I get an old version in the CDA?", 716 | "marks": [ 717 | ], 718 | "data": { 719 | } 720 | } 721 | ] 722 | }, 723 | { 724 | "nodeType": "paragraph", 725 | "data": { 726 | }, 727 | "content": [ 728 | { 729 | "nodeType": "text", 730 | "value": "As the delivery API is powered by a CDN network consisting of hundreds of servers distributed across continents, it takes some time (up to a few minutes) to reflect the changes to the published content. This must be taken into consideration when reacting to webhooks. In normal conditions, there could be a reasonable delay of 2 to 5 minutes.", 731 | "marks": [ 732 | ], 733 | "data": { 734 | } 735 | } 736 | ] 737 | }, 738 | { 739 | "nodeType": "paragraph", 740 | "data": { 741 | }, 742 | "content": [ 743 | { 744 | "nodeType": "text", 745 | "value": "Extracted from the ", 746 | "marks": [ 747 | ], 748 | "data": { 749 | } 750 | }, 751 | { 752 | "nodeType": "hyperlink", 753 | "data": { 754 | "uri": "https://www.contentful.com/faq/webhooks/" 755 | }, 756 | "content": [ 757 | { 758 | "nodeType": "text", 759 | "value": "Webhooks FAQ", 760 | "marks": [ 761 | ], 762 | "data": { 763 | } 764 | } 765 | ] 766 | }, 767 | { 768 | "nodeType": "text", 769 | "value": ".", 770 | "marks": [ 771 | ], 772 | "data": { 773 | } 774 | } 775 | ] 776 | } 777 | ] 778 | } 779 | }, 780 | "author": { 781 | "en-US": { 782 | "sys": { 783 | "type": "Link", 784 | "linkType": "Entry", 785 | "id": "15jwOBqpxqSAOy2eOO4S0m" 786 | } 787 | } 788 | }, 789 | "publishDate": { 790 | "en-US": "2017-05-12T00:00+02:00" 791 | }, 792 | "tags": { 793 | "en-US": ["javascript"] 794 | } 795 | } 796 | }, 797 | { 798 | "sys": { 799 | "space": { 800 | "sys": { 801 | "type": "Link", 802 | "linkType": "Space", 803 | "id": "28p9vvm1oxuw" 804 | } 805 | }, 806 | "id": "3K9b0esdy0q0yGqgW2g6Ke", 807 | "type": "Entry", 808 | "createdAt": "2017-05-15T11:51:15.331Z", 809 | "updatedAt": "2017-05-30T09:48:09.568Z", 810 | "createdBy": { 811 | "sys": { 812 | "type": "Link", 813 | "linkType": "User", 814 | "id": "066RqBikAjzKy0SWUEtFvH" 815 | } 816 | }, 817 | "updatedBy": { 818 | "sys": { 819 | "type": "Link", 820 | "linkType": "User", 821 | "id": "2AAFsI4st4sZPlF1LFT13q" 822 | } 823 | }, 824 | "publishedCounter": 8, 825 | "version": 722, 826 | "publishedBy": { 827 | "sys": { 828 | "type": "Link", 829 | "linkType": "User", 830 | "id": "2AAFsI4st4sZPlF1LFT13q" 831 | } 832 | }, 833 | "publishedVersion": 721, 834 | "firstPublishedAt": "2017-05-15T11:51:40.463Z", 835 | "publishedAt": "2017-05-30T09:48:09.520Z", 836 | "contentType": { 837 | "sys": { 838 | "type": "Link", 839 | "linkType": "ContentType", 840 | "id": "blogPost" 841 | } 842 | } 843 | }, 844 | "fields": { 845 | "title": { 846 | "en-US": "Hello world" 847 | }, 848 | "slug": { 849 | "en-US": "hello-world" 850 | }, 851 | "heroImage": { 852 | "en-US": { 853 | "sys": { 854 | "type": "Link", 855 | "linkType": "Asset", 856 | "id": "6Od9v3wzLOysiMum0Wkmme" 857 | } 858 | } 859 | }, 860 | "description": { 861 | "en-US": { 862 | "data": { 863 | }, 864 | "content": [ 865 | { 866 | "data": { 867 | }, 868 | "content": [ 869 | { 870 | "data": { 871 | }, 872 | "marks": [ 873 | ], 874 | "value": "Your very first content with Contentful, pulled in JSON format using the Content Delivery API.", 875 | "nodeType": "text" 876 | } 877 | ], 878 | "nodeType": "paragraph" 879 | } 880 | ], 881 | "nodeType": "document" 882 | } 883 | }, 884 | "body": { 885 | "en-US": { 886 | "data": { 887 | }, 888 | "content": [ 889 | { 890 | "data": { 891 | }, 892 | "content": [ 893 | { 894 | "data": { 895 | }, 896 | "marks": [ 897 | ], 898 | "value": "These is your very first content with Contentful, pulled in JSON format using the ", 899 | "nodeType": "text" 900 | }, 901 | { 902 | "data": { 903 | "uri": "https://www.contentful.com/developers/docs/references/content-delivery-api/" 904 | }, 905 | "content": [ 906 | { 907 | "data": { 908 | }, 909 | "marks": [ 910 | ], 911 | "value": "Content Delivery API", 912 | "nodeType": "text" 913 | } 914 | ], 915 | "nodeType": "hyperlink" 916 | }, 917 | { 918 | "data": { 919 | }, 920 | "marks": [ 921 | ], 922 | "value": ". Content and presentation are now decoupled, allowing you to focus your efforts in building the perfect app.", 923 | "nodeType": "text" 924 | } 925 | ], 926 | "nodeType": "paragraph" 927 | }, 928 | { 929 | "data": { 930 | }, 931 | "content": [ 932 | { 933 | "data": { 934 | }, 935 | "marks": [ 936 | ], 937 | "value": "Your first steps", 938 | "nodeType": "text" 939 | } 940 | ], 941 | "nodeType": "heading-2" 942 | }, 943 | { 944 | "data": { 945 | }, 946 | "content": [ 947 | { 948 | "data": { 949 | }, 950 | "marks": [ 951 | ], 952 | "value": "Building with Contentful is easy. First take a moment to get ", 953 | "nodeType": "text" 954 | }, 955 | { 956 | "data": { 957 | "uri": "https://www.contentful.com/r/knowledgebase/content-modelling-basics" 958 | }, 959 | "content": [ 960 | { 961 | "data": { 962 | }, 963 | "marks": [ 964 | ], 965 | "value": "the basics of content modelling", 966 | "nodeType": "text" 967 | } 968 | ], 969 | "nodeType": "hyperlink" 970 | }, 971 | { 972 | "data": { 973 | }, 974 | "marks": [ 975 | ], 976 | "value": ", which you can set up in the ", 977 | "nodeType": "text" 978 | }, 979 | { 980 | "data": { 981 | "uri": "https://app.contentful.com/" 982 | }, 983 | "content": [ 984 | { 985 | "data": { 986 | }, 987 | "marks": [ 988 | ], 989 | "value": "Contentful Web app", 990 | "nodeType": "text" 991 | } 992 | ], 993 | "nodeType": "hyperlink" 994 | }, 995 | { 996 | "data": { 997 | }, 998 | "marks": [ 999 | ], 1000 | "value": ". Once you get that, feel free to drop by the ", 1001 | "nodeType": "text" 1002 | }, 1003 | { 1004 | "data": { 1005 | "uri": "https://www.contentful.com/developers/docs/" 1006 | }, 1007 | "content": [ 1008 | { 1009 | "data": { 1010 | }, 1011 | "marks": [ 1012 | ], 1013 | "value": "Documentation", 1014 | "nodeType": "text" 1015 | } 1016 | ], 1017 | "nodeType": "hyperlink" 1018 | }, 1019 | { 1020 | "data": { 1021 | }, 1022 | "marks": [ 1023 | ], 1024 | "value": " to learn a bit more about how to build your app with Contentful, in particular the ", 1025 | "nodeType": "text" 1026 | }, 1027 | { 1028 | "data": { 1029 | "uri": "https://www.contentful.com/developers/docs/concepts/apis/" 1030 | }, 1031 | "content": [ 1032 | { 1033 | "data": { 1034 | }, 1035 | "marks": [ 1036 | ], 1037 | "value": "API basics", 1038 | "nodeType": "text" 1039 | } 1040 | ], 1041 | "nodeType": "hyperlink" 1042 | }, 1043 | { 1044 | "data": { 1045 | }, 1046 | "marks": [ 1047 | ], 1048 | "value": " and each one of our four APIs, as shown below.", 1049 | "nodeType": "text" 1050 | } 1051 | ], 1052 | "nodeType": "paragraph" 1053 | }, 1054 | { 1055 | "data": { 1056 | }, 1057 | "content": [ 1058 | { 1059 | "data": { 1060 | }, 1061 | "marks": [ 1062 | ], 1063 | "value": "Content Delivery API", 1064 | "nodeType": "text" 1065 | } 1066 | ], 1067 | "nodeType": "heading-3" 1068 | }, 1069 | { 1070 | "data": { 1071 | }, 1072 | "content": [ 1073 | { 1074 | "data": { 1075 | }, 1076 | "marks": [ 1077 | ], 1078 | "value": "The ", 1079 | "nodeType": "text" 1080 | }, 1081 | { 1082 | "data": { 1083 | "uri": "https://www.contentful.com/developers/docs/references/content-delivery-api/" 1084 | }, 1085 | "content": [ 1086 | { 1087 | "data": { 1088 | }, 1089 | "marks": [ 1090 | ], 1091 | "value": "Content Delivery API", 1092 | "nodeType": "text" 1093 | } 1094 | ], 1095 | "nodeType": "hyperlink" 1096 | }, 1097 | { 1098 | "data": { 1099 | }, 1100 | "marks": [ 1101 | ], 1102 | "value": " (CDA), available at ", 1103 | "nodeType": "text" 1104 | }, 1105 | { 1106 | "data": { 1107 | }, 1108 | "marks": [ 1109 | { 1110 | "type": "code" 1111 | } 1112 | ], 1113 | "value": "cdn.contentful.com", 1114 | "nodeType": "text" 1115 | }, 1116 | { 1117 | "data": { 1118 | }, 1119 | "marks": [ 1120 | ], 1121 | "value": ", is a read-only API for delivering content from Contentful to apps, websites and other media. Content is delivered as JSON data, and images, videos and other media as files.", 1122 | "nodeType": "text" 1123 | } 1124 | ], 1125 | "nodeType": "paragraph" 1126 | }, 1127 | { 1128 | "data": { 1129 | }, 1130 | "content": [ 1131 | { 1132 | "data": { 1133 | }, 1134 | "marks": [ 1135 | ], 1136 | "value": "The API is available via a globally distributed content delivery network. The server closest to the user serves all content, both JSON and binary. This minimizes latency, which especially benefits mobile apps. Hosting content in multiple global data centers also greatly improves the availability of content.", 1137 | "nodeType": "text" 1138 | } 1139 | ], 1140 | "nodeType": "paragraph" 1141 | }, 1142 | { 1143 | "data": { 1144 | }, 1145 | "content": [ 1146 | { 1147 | "data": { 1148 | }, 1149 | "marks": [ 1150 | ], 1151 | "value": "Content Management API", 1152 | "nodeType": "text" 1153 | } 1154 | ], 1155 | "nodeType": "heading-3" 1156 | }, 1157 | { 1158 | "data": { 1159 | }, 1160 | "content": [ 1161 | { 1162 | "data": { 1163 | }, 1164 | "marks": [ 1165 | ], 1166 | "value": "The ", 1167 | "nodeType": "text" 1168 | }, 1169 | { 1170 | "data": { 1171 | "uri": "https://www.contentful.com/developers/docs/references/content-management-api/" 1172 | }, 1173 | "content": [ 1174 | { 1175 | "data": { 1176 | }, 1177 | "marks": [ 1178 | ], 1179 | "value": "Content Management API", 1180 | "nodeType": "text" 1181 | } 1182 | ], 1183 | "nodeType": "hyperlink" 1184 | }, 1185 | { 1186 | "data": { 1187 | }, 1188 | "marks": [ 1189 | ], 1190 | "value": " (CMA), available at ", 1191 | "nodeType": "text" 1192 | }, 1193 | { 1194 | "data": { 1195 | }, 1196 | "marks": [ 1197 | { 1198 | "type": "code" 1199 | } 1200 | ], 1201 | "value": "api.contentful.com", 1202 | "nodeType": "text" 1203 | }, 1204 | { 1205 | "data": { 1206 | }, 1207 | "marks": [ 1208 | ], 1209 | "value": ", is a read-write API for managing content. Unlike the Content Delivery API, the management API requires you to authenticate as a Contentful user. You could use the CMA for several use cases, such as:", 1210 | "nodeType": "text" 1211 | } 1212 | ], 1213 | "nodeType": "paragraph" 1214 | }, 1215 | { 1216 | "data": { 1217 | }, 1218 | "content": [ 1219 | { 1220 | "data": { 1221 | }, 1222 | "content": [ 1223 | { 1224 | "data": { 1225 | }, 1226 | "content": [ 1227 | { 1228 | "data": { 1229 | }, 1230 | "marks": [ 1231 | ], 1232 | "value": "Automatic imports from different CMSes like WordPress or Drupal.", 1233 | "nodeType": "text" 1234 | } 1235 | ], 1236 | "nodeType": "paragraph" 1237 | } 1238 | ], 1239 | "nodeType": "list-item" 1240 | }, 1241 | { 1242 | "data": { 1243 | }, 1244 | "content": [ 1245 | { 1246 | "data": { 1247 | }, 1248 | "content": [ 1249 | { 1250 | "data": { 1251 | }, 1252 | "marks": [ 1253 | ], 1254 | "value": "Integration with other backend systems, such as an e-commerce shop.", 1255 | "nodeType": "text" 1256 | } 1257 | ], 1258 | "nodeType": "paragraph" 1259 | } 1260 | ], 1261 | "nodeType": "list-item" 1262 | }, 1263 | { 1264 | "data": { 1265 | }, 1266 | "content": [ 1267 | { 1268 | "data": { 1269 | }, 1270 | "content": [ 1271 | { 1272 | "data": { 1273 | }, 1274 | "marks": [ 1275 | ], 1276 | "value": "Building custom editing experiences. We built the ", 1277 | "nodeType": "text" 1278 | }, 1279 | { 1280 | "data": { 1281 | "uri": "https://app.contentful.com/" 1282 | }, 1283 | "content": [ 1284 | { 1285 | "data": { 1286 | }, 1287 | "marks": [ 1288 | ], 1289 | "value": "Contentful Web app", 1290 | "nodeType": "text" 1291 | } 1292 | ], 1293 | "nodeType": "hyperlink" 1294 | }, 1295 | { 1296 | "data": { 1297 | }, 1298 | "marks": [ 1299 | ], 1300 | "value": " on top of this API.", 1301 | "nodeType": "text" 1302 | } 1303 | ], 1304 | "nodeType": "paragraph" 1305 | } 1306 | ], 1307 | "nodeType": "list-item" 1308 | } 1309 | ], 1310 | "nodeType": "unordered-list" 1311 | }, 1312 | { 1313 | "data": { 1314 | }, 1315 | "content": [ 1316 | { 1317 | "data": { 1318 | }, 1319 | "marks": [ 1320 | ], 1321 | "value": "Preview API", 1322 | "nodeType": "text" 1323 | } 1324 | ], 1325 | "nodeType": "heading-3" 1326 | }, 1327 | { 1328 | "data": { 1329 | }, 1330 | "content": [ 1331 | { 1332 | "data": { 1333 | }, 1334 | "marks": [ 1335 | ], 1336 | "value": "The ", 1337 | "nodeType": "text" 1338 | }, 1339 | { 1340 | "data": { 1341 | "uri": "https://www.contentful.com/developers/docs/concepts/apis/#content-preview-api" 1342 | }, 1343 | "content": [ 1344 | { 1345 | "data": { 1346 | }, 1347 | "marks": [ 1348 | ], 1349 | "value": "Content Preview API", 1350 | "nodeType": "text" 1351 | } 1352 | ], 1353 | "nodeType": "hyperlink" 1354 | }, 1355 | { 1356 | "data": { 1357 | }, 1358 | "marks": [ 1359 | ], 1360 | "value": ", available at ", 1361 | "nodeType": "text" 1362 | }, 1363 | { 1364 | "data": { 1365 | }, 1366 | "marks": [ 1367 | { 1368 | "type": "code" 1369 | } 1370 | ], 1371 | "value": "preview.contentful.com", 1372 | "nodeType": "text" 1373 | }, 1374 | { 1375 | "data": { 1376 | }, 1377 | "marks": [ 1378 | ], 1379 | "value": ", is a variant of the CDA for previewing your content before delivering it to your customers. You use the Content Preview API in combination with a \"preview\" deployment of your website (or a \"preview\" build of your mobile app) that allows content managers and authors to view their work in-context, as if it were published, using a \"preview\" access token as though it were delivered by the CDA.", 1380 | "nodeType": "text" 1381 | } 1382 | ], 1383 | "nodeType": "paragraph" 1384 | }, 1385 | { 1386 | "data": { 1387 | }, 1388 | "content": [ 1389 | { 1390 | "data": { 1391 | }, 1392 | "marks": [ 1393 | ], 1394 | "value": "Images API", 1395 | "nodeType": "text" 1396 | } 1397 | ], 1398 | "nodeType": "heading-3" 1399 | }, 1400 | { 1401 | "data": { 1402 | }, 1403 | "content": [ 1404 | { 1405 | "data": { 1406 | }, 1407 | "marks": [ 1408 | ], 1409 | "value": "The ", 1410 | "nodeType": "text" 1411 | }, 1412 | { 1413 | "data": { 1414 | "uri": "https://www.contentful.com/developers/docs/concepts/apis/#images-api" 1415 | }, 1416 | "content": [ 1417 | { 1418 | "data": { 1419 | }, 1420 | "marks": [ 1421 | ], 1422 | "value": "Images API", 1423 | "nodeType": "text" 1424 | } 1425 | ], 1426 | "nodeType": "hyperlink" 1427 | }, 1428 | { 1429 | "data": { 1430 | }, 1431 | "marks": [ 1432 | ], 1433 | "value": ", available at ", 1434 | "nodeType": "text" 1435 | }, 1436 | { 1437 | "data": { 1438 | }, 1439 | "marks": [ 1440 | { 1441 | "type": "code" 1442 | } 1443 | ], 1444 | "value": "images.contentful.com", 1445 | "nodeType": "text" 1446 | }, 1447 | { 1448 | "data": { 1449 | }, 1450 | "marks": [ 1451 | ], 1452 | "value": ", allows you to resize and crop images, change their background color and convert them to different formats. Using our API for these transformations lets you upload high-quality assets, deliver exactly what your app needs, and still get all the benefits of our caching CDN.", 1453 | "nodeType": "text" 1454 | } 1455 | ], 1456 | "nodeType": "paragraph" 1457 | } 1458 | ], 1459 | "nodeType": "document" 1460 | } 1461 | }, 1462 | "author": { 1463 | "en-US": { 1464 | "sys": { 1465 | "type": "Link", 1466 | "linkType": "Entry", 1467 | "id": "15jwOBqpxqSAOy2eOO4S0m" 1468 | } 1469 | } 1470 | }, 1471 | "publishDate": { 1472 | "en-US": "2017-05-15T00:00+02:00" 1473 | }, 1474 | "tags": { 1475 | "en-US": ["general"] 1476 | } 1477 | } 1478 | }, 1479 | { 1480 | "sys": { 1481 | "space": { 1482 | "sys": { 1483 | "type": "Link", 1484 | "linkType": "Space", 1485 | "id": "28p9vvm1oxuw" 1486 | } 1487 | }, 1488 | "id": "2PtC9h1YqIA6kaUaIsWEQ0", 1489 | "type": "Entry", 1490 | "createdAt": "2017-05-15T12:01:00.579Z", 1491 | "updatedAt": "2017-05-30T12:31:34.521Z", 1492 | "createdBy": { 1493 | "sys": { 1494 | "type": "Link", 1495 | "linkType": "User", 1496 | "id": "066RqBikAjzKy0SWUEtFvH" 1497 | } 1498 | }, 1499 | "updatedBy": { 1500 | "sys": { 1501 | "type": "Link", 1502 | "linkType": "User", 1503 | "id": "2AAFsI4st4sZPlF1LFT13q" 1504 | } 1505 | }, 1506 | "publishedCounter": 7, 1507 | "version": 218, 1508 | "publishedBy": { 1509 | "sys": { 1510 | "type": "Link", 1511 | "linkType": "User", 1512 | "id": "2AAFsI4st4sZPlF1LFT13q" 1513 | } 1514 | }, 1515 | "publishedVersion": 217, 1516 | "firstPublishedAt": "2017-05-15T12:01:52.543Z", 1517 | "publishedAt": "2017-05-30T12:31:34.481Z", 1518 | "contentType": { 1519 | "sys": { 1520 | "type": "Link", 1521 | "linkType": "ContentType", 1522 | "id": "blogPost" 1523 | } 1524 | } 1525 | }, 1526 | "fields": { 1527 | "title": { 1528 | "en-US": "Static sites are great" 1529 | }, 1530 | "slug": { 1531 | "en-US": "static-sites-are-great" 1532 | }, 1533 | "heroImage": { 1534 | "en-US": { 1535 | "sys": { 1536 | "type": "Link", 1537 | "linkType": "Asset", 1538 | "id": "4NzwDSDlGECGIiokKomsyI" 1539 | } 1540 | } 1541 | }, 1542 | "description": { 1543 | "en-US": { 1544 | "data": { 1545 | }, 1546 | "content": [ 1547 | { 1548 | "data": { 1549 | }, 1550 | "content": [ 1551 | { 1552 | "data": { 1553 | }, 1554 | "marks": [ 1555 | ], 1556 | "value": "Worry less about security, caching, and talking to the server. Static sites are the new thing.", 1557 | "nodeType": "text" 1558 | } 1559 | ], 1560 | "nodeType": "paragraph" 1561 | } 1562 | ], 1563 | "nodeType": "document" 1564 | } 1565 | }, 1566 | "body": { 1567 | "en-US": { 1568 | "nodeType": "document", 1569 | "data": { 1570 | }, 1571 | "content": [ 1572 | { 1573 | "nodeType": "heading-2", 1574 | "data": { 1575 | }, 1576 | "content": [ 1577 | { 1578 | "nodeType": "text", 1579 | "value": "The case for the static site generator", 1580 | "marks": [ 1581 | ], 1582 | "data": { 1583 | } 1584 | } 1585 | ] 1586 | }, 1587 | { 1588 | "nodeType": "paragraph", 1589 | "data": { 1590 | }, 1591 | "content": [ 1592 | { 1593 | "nodeType": "text", 1594 | "value": "More and more developers are jumping on the \"go static train\", and rightfully so. Static pages are fast, lightweight, they scale well. They are more secure, and simple to maintain and they allow you to focus all your time and effort on the user interface. Often times, this dedication really shows.", 1595 | "marks": [ 1596 | ], 1597 | "data": { 1598 | } 1599 | } 1600 | ] 1601 | }, 1602 | { 1603 | "nodeType": "paragraph", 1604 | "data": { 1605 | }, 1606 | "content": [ 1607 | { 1608 | "nodeType": "text", 1609 | "value": "It just so happens that static site generators are mostly loved by developers, but not by the average Joe. They do not offer WYSIWYG, previewing on demo sites may take an update cycle, they are often based on markdown text files, and they require some knowledge of modern day repositories.", 1610 | "marks": [ 1611 | ], 1612 | "data": { 1613 | } 1614 | } 1615 | ] 1616 | }, 1617 | { 1618 | "nodeType": "paragraph", 1619 | "data": { 1620 | }, 1621 | "content": [ 1622 | { 1623 | "nodeType": "text", 1624 | "value": "Moreover, when teams are collaborating, it can get complicated quickly. Has this article already been proof-read or reviewed? Is this input valid? Are user permissions available, e.g. for administering adding and removing team members? Can this article be published at a future date? How can a large repository of content be categorized, organized, and searched? All these requirements have previously been more or less solved within the admin area of your CMS. But of course with all the baggage that made you leave the appserver-app-database-in-one-big-blob stack in the first place.", 1625 | "marks": [ 1626 | ], 1627 | "data": { 1628 | } 1629 | } 1630 | ] 1631 | }, 1632 | { 1633 | "nodeType": "heading-2", 1634 | "data": { 1635 | }, 1636 | "content": [ 1637 | { 1638 | "nodeType": "text", 1639 | "value": "Content APIs to the rescue", 1640 | "marks": [ 1641 | ], 1642 | "data": { 1643 | } 1644 | } 1645 | ] 1646 | }, 1647 | { 1648 | "nodeType": "paragraph", 1649 | "data": { 1650 | }, 1651 | "content": [ 1652 | { 1653 | "nodeType": "text", 1654 | "value": "An alternative is decoupling the content management aspect from the system. And then replacing the maintenance prone server with a cloud based web service offering. Effectively, instead of your CMS of old, you move to a ", 1655 | "marks": [ 1656 | ], 1657 | "data": { 1658 | } 1659 | }, 1660 | { 1661 | "nodeType": "hyperlink", 1662 | "data": { 1663 | "uri": "https://www.contentful.com/r/knowledgebase/content-as-a-service/" 1664 | }, 1665 | "content": [ 1666 | { 1667 | "nodeType": "text", 1668 | "value": "Content Management as a Service (CMaaS)", 1669 | "marks": [ 1670 | ], 1671 | "data": { 1672 | } 1673 | } 1674 | ] 1675 | }, 1676 | { 1677 | "nodeType": "text", 1678 | "value": " world, with a content API to deliver all your content. That way, you get the all the ", 1679 | "marks": [ 1680 | ], 1681 | "data": { 1682 | } 1683 | }, 1684 | { 1685 | "nodeType": "hyperlink", 1686 | "data": { 1687 | "uri": "http://www.digett.com/blog/01/16/2014/pairing-static-websites-cms" 1688 | }, 1689 | "content": [ 1690 | { 1691 | "nodeType": "text", 1692 | "value": "benefits of content management features", 1693 | "marks": [ 1694 | ], 1695 | "data": { 1696 | } 1697 | } 1698 | ] 1699 | }, 1700 | { 1701 | "nodeType": "text", 1702 | "value": " while still being able to embrace the static site generator mantra.", 1703 | "marks": [ 1704 | ], 1705 | "data": { 1706 | } 1707 | } 1708 | ] 1709 | }, 1710 | { 1711 | "nodeType": "paragraph", 1712 | "data": { 1713 | }, 1714 | "content": [ 1715 | { 1716 | "nodeType": "text", 1717 | "value": "It so happens that Contentful is offering just that kind of content API. A service that", 1718 | "marks": [ 1719 | ], 1720 | "data": { 1721 | } 1722 | } 1723 | ] 1724 | }, 1725 | { 1726 | "nodeType": "unordered-list", 1727 | "data": { 1728 | }, 1729 | "content": [ 1730 | { 1731 | "nodeType": "list-item", 1732 | "data": { 1733 | }, 1734 | "content": [ 1735 | { 1736 | "nodeType": "paragraph", 1737 | "data": { 1738 | }, 1739 | "content": [ 1740 | { 1741 | "nodeType": "text", 1742 | "value": "from the ground up has been designed to be fast, scalable, secure, and offer high uptime, so that you don’t have to worry about maintenance ever again.", 1743 | "marks": [ 1744 | ], 1745 | "data": { 1746 | } 1747 | } 1748 | ] 1749 | } 1750 | ] 1751 | }, 1752 | { 1753 | "nodeType": "list-item", 1754 | "data": { 1755 | }, 1756 | "content": [ 1757 | { 1758 | "nodeType": "paragraph", 1759 | "data": { 1760 | }, 1761 | "content": [ 1762 | { 1763 | "nodeType": "text", 1764 | "value": "offers a powerful editor and lots of flexibility in creating templates for your documents that your editors can reuse and combine, so that no developers resources are required in everyday writing and updating tasks.", 1765 | "marks": [ 1766 | ], 1767 | "data": { 1768 | } 1769 | } 1770 | ] 1771 | } 1772 | ] 1773 | }, 1774 | { 1775 | "nodeType": "list-item", 1776 | "data": { 1777 | }, 1778 | "content": [ 1779 | { 1780 | "nodeType": "paragraph", 1781 | "data": { 1782 | }, 1783 | "content": [ 1784 | { 1785 | "nodeType": "text", 1786 | "value": "separates content from presentation, so you can reuse your content repository for any device platform your heart desires. That way, you can COPE (\"create once, publish everywhere\").", 1787 | "marks": [ 1788 | ], 1789 | "data": { 1790 | } 1791 | } 1792 | ] 1793 | } 1794 | ] 1795 | }, 1796 | { 1797 | "nodeType": "list-item", 1798 | "data": { 1799 | }, 1800 | "content": [ 1801 | { 1802 | "nodeType": "paragraph", 1803 | "data": { 1804 | }, 1805 | "content": [ 1806 | { 1807 | "nodeType": "text", 1808 | "value": "offers webhooks that you can use to rebuild your static site in a fully automated fashion every time your content is modified.", 1809 | "marks": [ 1810 | ], 1811 | "data": { 1812 | } 1813 | } 1814 | ] 1815 | } 1816 | ] 1817 | } 1818 | ] 1819 | }, 1820 | { 1821 | "nodeType": "paragraph", 1822 | "data": { 1823 | }, 1824 | "content": [ 1825 | { 1826 | "nodeType": "text", 1827 | "value": "Extracted from the article ", 1828 | "marks": [ 1829 | ], 1830 | "data": { 1831 | } 1832 | }, 1833 | { 1834 | "nodeType": "hyperlink", 1835 | "data": { 1836 | "uri": "https://www.contentful.com/r/knowledgebase/contentful-api-cms-static-site-generators/" 1837 | }, 1838 | "content": [ 1839 | { 1840 | "nodeType": "text", 1841 | "value": "CMS-functionality for static site generators", 1842 | "marks": [ 1843 | ], 1844 | "data": { 1845 | } 1846 | } 1847 | ] 1848 | }, 1849 | { 1850 | "nodeType": "text", 1851 | "value": ". Read more about the ", 1852 | "marks": [ 1853 | ], 1854 | "data": { 1855 | } 1856 | }, 1857 | { 1858 | "nodeType": "hyperlink", 1859 | "data": { 1860 | "uri": "https://www.contentful.com/developers/docs/tools/staticsitegenerators/" 1861 | }, 1862 | "content": [ 1863 | { 1864 | "nodeType": "text", 1865 | "value": "static site generators supported by Contentful", 1866 | "marks": [ 1867 | ], 1868 | "data": { 1869 | } 1870 | } 1871 | ] 1872 | }, 1873 | { 1874 | "nodeType": "text", 1875 | "value": ".", 1876 | "marks": [ 1877 | ], 1878 | "data": { 1879 | } 1880 | } 1881 | ] 1882 | } 1883 | ] 1884 | } 1885 | }, 1886 | "author": { 1887 | "en-US": { 1888 | "sys": { 1889 | "type": "Link", 1890 | "linkType": "Entry", 1891 | "id": "15jwOBqpxqSAOy2eOO4S0m" 1892 | } 1893 | } 1894 | }, 1895 | "publishDate": { 1896 | "en-US": "2017-05-16T00:00+02:00" 1897 | }, 1898 | "tags": { 1899 | "en-US": ["javascript", "static-sites"] 1900 | } 1901 | } 1902 | } 1903 | ], 1904 | "assets": [ 1905 | { 1906 | "sys": { 1907 | "space": { 1908 | "sys": { 1909 | "type": "Link", 1910 | "linkType": "Space", 1911 | "id": "28p9vvm1oxuw" 1912 | } 1913 | }, 1914 | "id": "7orLdboQQowIUs22KAW4U", 1915 | "type": "Asset", 1916 | "createdAt": "2017-05-11T13:04:42.667Z", 1917 | "updatedAt": "2017-05-16T09:29:04.154Z", 1918 | "createdBy": { 1919 | "sys": { 1920 | "type": "Link", 1921 | "linkType": "User", 1922 | "id": "066RqBikAjzKy0SWUEtFvH" 1923 | } 1924 | }, 1925 | "updatedBy": { 1926 | "sys": { 1927 | "type": "Link", 1928 | "linkType": "User", 1929 | "id": "066RqBikAjzKy0SWUEtFvH" 1930 | } 1931 | }, 1932 | "publishedCounter": 3, 1933 | "version": 56, 1934 | "publishedBy": { 1935 | "sys": { 1936 | "type": "Link", 1937 | "linkType": "User", 1938 | "id": "066RqBikAjzKy0SWUEtFvH" 1939 | } 1940 | }, 1941 | "publishedVersion": 55, 1942 | "firstPublishedAt": "2017-05-11T13:05:17.053Z", 1943 | "publishedAt": "2017-05-16T09:29:04.140Z" 1944 | }, 1945 | "fields": { 1946 | "title": { 1947 | "en-US": "Sparkler" 1948 | }, 1949 | "description": { 1950 | "en-US": "John with Sparkler" 1951 | }, 1952 | "file": { 1953 | "en-US": { 1954 | "url": "//images.contentful.com/28p9vvm1oxuw/7orLdboQQowIUs22KAW4U/a97cd3b3415b51c5facfa6f4d184b650/matt-palmer-254999.jpg", 1955 | "details": { 1956 | "size": 2293094, 1957 | "image": { 1958 | "width": 3000, 1959 | "height": 2000 1960 | } 1961 | }, 1962 | "fileName": "matt-palmer-254999.jpg", 1963 | "contentType": "image/jpeg" 1964 | } 1965 | } 1966 | } 1967 | }, 1968 | { 1969 | "sys": { 1970 | "space": { 1971 | "sys": { 1972 | "type": "Link", 1973 | "linkType": "Space", 1974 | "id": "28p9vvm1oxuw" 1975 | } 1976 | }, 1977 | "id": "6Od9v3wzLOysiMum0Wkmme", 1978 | "type": "Asset", 1979 | "createdAt": "2017-05-15T12:05:53.234Z", 1980 | "updatedAt": "2017-05-15T13:36:58.905Z", 1981 | "createdBy": { 1982 | "sys": { 1983 | "type": "Link", 1984 | "linkType": "User", 1985 | "id": "066RqBikAjzKy0SWUEtFvH" 1986 | } 1987 | }, 1988 | "updatedBy": { 1989 | "sys": { 1990 | "type": "Link", 1991 | "linkType": "User", 1992 | "id": "066RqBikAjzKy0SWUEtFvH" 1993 | } 1994 | }, 1995 | "publishedCounter": 2, 1996 | "version": 22, 1997 | "publishedBy": { 1998 | "sys": { 1999 | "type": "Link", 2000 | "linkType": "User", 2001 | "id": "066RqBikAjzKy0SWUEtFvH" 2002 | } 2003 | }, 2004 | "publishedVersion": 21, 2005 | "firstPublishedAt": "2017-05-15T12:06:38.633Z", 2006 | "publishedAt": "2017-05-15T13:36:58.888Z" 2007 | }, 2008 | "fields": { 2009 | "title": { 2010 | "en-US": "Woman with black hat" 2011 | }, 2012 | "description": { 2013 | "en-US": "Woman wearing a black hat" 2014 | }, 2015 | "file": { 2016 | "en-US": { 2017 | "url": "//images.contentful.com/28p9vvm1oxuw/6Od9v3wzLOysiMum0Wkmme/95675d379a1284015a8210ca66cc53a5/cameron-kirby-88711.jpg", 2018 | "details": { 2019 | "size": 7316629, 2020 | "image": { 2021 | "width": 3000, 2022 | "height": 2000 2023 | } 2024 | }, 2025 | "fileName": "cameron-kirby-88711.jpg", 2026 | "contentType": "image/jpeg" 2027 | } 2028 | } 2029 | } 2030 | }, 2031 | { 2032 | "sys": { 2033 | "space": { 2034 | "sys": { 2035 | "type": "Link", 2036 | "linkType": "Space", 2037 | "id": "28p9vvm1oxuw" 2038 | } 2039 | }, 2040 | "id": "4NzwDSDlGECGIiokKomsyI", 2041 | "type": "Asset", 2042 | "createdAt": "2017-05-15T12:33:11.841Z", 2043 | "updatedAt": "2017-05-15T12:33:50.816Z", 2044 | "createdBy": { 2045 | "sys": { 2046 | "type": "Link", 2047 | "linkType": "User", 2048 | "id": "066RqBikAjzKy0SWUEtFvH" 2049 | } 2050 | }, 2051 | "updatedBy": { 2052 | "sys": { 2053 | "type": "Link", 2054 | "linkType": "User", 2055 | "id": "066RqBikAjzKy0SWUEtFvH" 2056 | } 2057 | }, 2058 | "publishedCounter": 1, 2059 | "version": 21, 2060 | "publishedBy": { 2061 | "sys": { 2062 | "type": "Link", 2063 | "linkType": "User", 2064 | "id": "066RqBikAjzKy0SWUEtFvH" 2065 | } 2066 | }, 2067 | "publishedVersion": 20, 2068 | "firstPublishedAt": "2017-05-15T12:33:50.801Z", 2069 | "publishedAt": "2017-05-15T12:33:50.801Z" 2070 | }, 2071 | "fields": { 2072 | "title": { 2073 | "en-US": "City" 2074 | }, 2075 | "description": { 2076 | "en-US": "City pictured from the sky" 2077 | }, 2078 | "file": { 2079 | "en-US": { 2080 | "url": "//images.contentful.com/28p9vvm1oxuw/4NzwDSDlGECGIiokKomsyI/d04a5154fa2e2ab02857950639325684/denys-nevozhai-100695.jpg", 2081 | "details": { 2082 | "size": 15736986, 2083 | "image": { 2084 | "width": 3992, 2085 | "height": 2992 2086 | } 2087 | }, 2088 | "fileName": "denys-nevozhai-100695.jpg", 2089 | "contentType": "image/jpeg" 2090 | } 2091 | } 2092 | } 2093 | }, 2094 | { 2095 | "sys": { 2096 | "space": { 2097 | "sys": { 2098 | "type": "Link", 2099 | "linkType": "Space", 2100 | "id": "28p9vvm1oxuw" 2101 | } 2102 | }, 2103 | "id": "4shwYI3POEGkw0Eg6kcyaQ", 2104 | "type": "Asset", 2105 | "createdAt": "2017-05-15T12:34:08.279Z", 2106 | "updatedAt": "2017-05-15T12:35:08.308Z", 2107 | "createdBy": { 2108 | "sys": { 2109 | "type": "Link", 2110 | "linkType": "User", 2111 | "id": "066RqBikAjzKy0SWUEtFvH" 2112 | } 2113 | }, 2114 | "updatedBy": { 2115 | "sys": { 2116 | "type": "Link", 2117 | "linkType": "User", 2118 | "id": "066RqBikAjzKy0SWUEtFvH" 2119 | } 2120 | }, 2121 | "publishedCounter": 1, 2122 | "version": 38, 2123 | "publishedBy": { 2124 | "sys": { 2125 | "type": "Link", 2126 | "linkType": "User", 2127 | "id": "066RqBikAjzKy0SWUEtFvH" 2128 | } 2129 | }, 2130 | "publishedVersion": 37, 2131 | "firstPublishedAt": "2017-05-15T12:35:08.292Z", 2132 | "publishedAt": "2017-05-15T12:35:08.292Z" 2133 | }, 2134 | "fields": { 2135 | "title": { 2136 | "en-US": "Man in the fields" 2137 | }, 2138 | "description": { 2139 | "en-US": "Tattooed man walking in a field" 2140 | }, 2141 | "file": { 2142 | "en-US": { 2143 | "url": "//images.contentful.com/28p9vvm1oxuw/4shwYI3POEGkw0Eg6kcyaQ/eeaa6df85fb4452ea69ad18c98ffc015/felix-russell-saw-112140.jpg", 2144 | "details": { 2145 | "size": 4539181, 2146 | "image": { 2147 | "width": 2500, 2148 | "height": 1667 2149 | } 2150 | }, 2151 | "fileName": "felix-russell-saw-112140.jpg", 2152 | "contentType": "image/jpeg" 2153 | } 2154 | } 2155 | } 2156 | } 2157 | ], 2158 | "locales": [ 2159 | { 2160 | "name": "U.S. English", 2161 | "code": "en-US", 2162 | "fallbackCode": null, 2163 | "default": true, 2164 | "contentManagementApi": true, 2165 | "contentDeliveryApi": true, 2166 | "optional": false, 2167 | "sys": { 2168 | "type": "Locale", 2169 | "id": "3Bb4qrZtqLzwN35dmTQ6a2", 2170 | "version": 0, 2171 | "space": { 2172 | "sys": { 2173 | "type": "Link", 2174 | "linkType": "Space", 2175 | "id": "28p9vvm1oxuw" 2176 | } 2177 | }, 2178 | "createdBy": { 2179 | "sys": { 2180 | "type": "Link", 2181 | "linkType": "User", 2182 | "id": "066RqBikAjzKy0SWUEtFvH" 2183 | } 2184 | }, 2185 | "createdAt": "2017-05-11T12:01:17Z", 2186 | "updatedBy": { 2187 | "sys": { 2188 | "type": "Link", 2189 | "linkType": "User", 2190 | "id": "066RqBikAjzKy0SWUEtFvH" 2191 | } 2192 | }, 2193 | "updatedAt": "2017-05-11T12:01:17Z" 2194 | } 2195 | } 2196 | ], 2197 | "webhooks": [], 2198 | "roles": [ 2199 | { 2200 | "name": "Author", 2201 | "description": "Allows editing of content", 2202 | "policies": [ 2203 | { 2204 | "effect": "allow", 2205 | "actions": ["create"], 2206 | "constraint": { 2207 | "and": [ 2208 | { 2209 | "equals": [ 2210 | { 2211 | "doc": "sys.type" 2212 | }, 2213 | "Entry" 2214 | ] 2215 | } 2216 | ] 2217 | } 2218 | }, 2219 | { 2220 | "effect": "allow", 2221 | "actions": ["read"], 2222 | "constraint": { 2223 | "and": [ 2224 | { 2225 | "equals": [ 2226 | { 2227 | "doc": "sys.type" 2228 | }, 2229 | "Entry" 2230 | ] 2231 | } 2232 | ] 2233 | } 2234 | }, 2235 | { 2236 | "effect": "allow", 2237 | "actions": ["update"], 2238 | "constraint": { 2239 | "and": [ 2240 | { 2241 | "equals": [ 2242 | { 2243 | "doc": "sys.type" 2244 | }, 2245 | "Entry" 2246 | ] 2247 | } 2248 | ] 2249 | } 2250 | }, 2251 | { 2252 | "effect": "allow", 2253 | "actions": ["create"], 2254 | "constraint": { 2255 | "and": [ 2256 | { 2257 | "equals": [ 2258 | { 2259 | "doc": "sys.type" 2260 | }, 2261 | "Asset" 2262 | ] 2263 | } 2264 | ] 2265 | } 2266 | }, 2267 | { 2268 | "effect": "allow", 2269 | "actions": ["read"], 2270 | "constraint": { 2271 | "and": [ 2272 | { 2273 | "equals": [ 2274 | { 2275 | "doc": "sys.type" 2276 | }, 2277 | "Asset" 2278 | ] 2279 | } 2280 | ] 2281 | } 2282 | }, 2283 | { 2284 | "effect": "allow", 2285 | "actions": ["update"], 2286 | "constraint": { 2287 | "and": [ 2288 | { 2289 | "equals": [ 2290 | { 2291 | "doc": "sys.type" 2292 | }, 2293 | "Asset" 2294 | ] 2295 | } 2296 | ] 2297 | } 2298 | } 2299 | ], 2300 | "permissions": { 2301 | "ContentModel": ["read"], 2302 | "Settings": [], 2303 | "ContentDelivery": [] 2304 | }, 2305 | "sys": { 2306 | "type": "Role", 2307 | "id": "3Bc1MhEh97SAPAubIwDlgq", 2308 | "version": 0, 2309 | "space": { 2310 | "sys": { 2311 | "type": "Link", 2312 | "linkType": "Space", 2313 | "id": "28p9vvm1oxuw" 2314 | } 2315 | }, 2316 | "createdBy": { 2317 | "sys": { 2318 | "type": "Link", 2319 | "linkType": "User", 2320 | "id": "066RqBikAjzKy0SWUEtFvH" 2321 | } 2322 | }, 2323 | "createdAt": "2017-05-11T12:01:17Z", 2324 | "updatedBy": { 2325 | "sys": { 2326 | "type": "Link", 2327 | "linkType": "User", 2328 | "id": "066RqBikAjzKy0SWUEtFvH" 2329 | } 2330 | }, 2331 | "updatedAt": "2017-05-11T12:01:17Z" 2332 | } 2333 | }, 2334 | { 2335 | "name": "Developer", 2336 | "description": "Allows reading Entries and managing API Keys", 2337 | "policies": [ 2338 | { 2339 | "effect": "allow", 2340 | "actions": ["read"], 2341 | "constraint": { 2342 | "and": [ 2343 | { 2344 | "equals": [ 2345 | { 2346 | "doc": "sys.type" 2347 | }, 2348 | "Entry" 2349 | ] 2350 | } 2351 | ] 2352 | } 2353 | }, 2354 | { 2355 | "effect": "allow", 2356 | "actions": ["read"], 2357 | "constraint": { 2358 | "and": [ 2359 | { 2360 | "equals": [ 2361 | { 2362 | "doc": "sys.type" 2363 | }, 2364 | "Asset" 2365 | ] 2366 | } 2367 | ] 2368 | } 2369 | } 2370 | ], 2371 | "permissions": { 2372 | "ContentModel": ["read"], 2373 | "Settings": [], 2374 | "ContentDelivery": "all" 2375 | }, 2376 | "sys": { 2377 | "type": "Role", 2378 | "id": "3Bdf87ZgV69DZme5Pll2MC", 2379 | "version": 0, 2380 | "space": { 2381 | "sys": { 2382 | "type": "Link", 2383 | "linkType": "Space", 2384 | "id": "28p9vvm1oxuw" 2385 | } 2386 | }, 2387 | "createdBy": { 2388 | "sys": { 2389 | "type": "Link", 2390 | "linkType": "User", 2391 | "id": "066RqBikAjzKy0SWUEtFvH" 2392 | } 2393 | }, 2394 | "createdAt": "2017-05-11T12:01:17Z", 2395 | "updatedBy": { 2396 | "sys": { 2397 | "type": "Link", 2398 | "linkType": "User", 2399 | "id": "066RqBikAjzKy0SWUEtFvH" 2400 | } 2401 | }, 2402 | "updatedAt": "2017-05-11T12:01:17Z" 2403 | } 2404 | }, 2405 | { 2406 | "name": "Editor", 2407 | "description": "Allows editing, publishing and archiving of content", 2408 | "policies": [ 2409 | { 2410 | "effect": "allow", 2411 | "actions": "all", 2412 | "constraint": { 2413 | "and": [ 2414 | { 2415 | "equals": [ 2416 | { 2417 | "doc": "sys.type" 2418 | }, 2419 | "Entry" 2420 | ] 2421 | } 2422 | ] 2423 | } 2424 | }, 2425 | { 2426 | "effect": "allow", 2427 | "actions": "all", 2428 | "constraint": { 2429 | "and": [ 2430 | { 2431 | "equals": [ 2432 | { 2433 | "doc": "sys.type" 2434 | }, 2435 | "Asset" 2436 | ] 2437 | } 2438 | ] 2439 | } 2440 | } 2441 | ], 2442 | "permissions": { 2443 | "ContentModel": ["read"], 2444 | "Settings": [], 2445 | "ContentDelivery": [] 2446 | }, 2447 | "sys": { 2448 | "type": "Role", 2449 | "id": "3BehG3o7XDtAd7i5NLYesC", 2450 | "version": 0, 2451 | "space": { 2452 | "sys": { 2453 | "type": "Link", 2454 | "linkType": "Space", 2455 | "id": "28p9vvm1oxuw" 2456 | } 2457 | }, 2458 | "createdBy": { 2459 | "sys": { 2460 | "type": "Link", 2461 | "linkType": "User", 2462 | "id": "066RqBikAjzKy0SWUEtFvH" 2463 | } 2464 | }, 2465 | "createdAt": "2017-05-11T12:01:17Z", 2466 | "updatedBy": { 2467 | "sys": { 2468 | "type": "Link", 2469 | "linkType": "User", 2470 | "id": "066RqBikAjzKy0SWUEtFvH" 2471 | } 2472 | }, 2473 | "updatedAt": "2017-05-11T12:01:17Z" 2474 | } 2475 | }, 2476 | { 2477 | "name": "Freelancer", 2478 | "description": "Allows only editing of content they created themselves", 2479 | "policies": [ 2480 | { 2481 | "effect": "allow", 2482 | "actions": ["create"], 2483 | "constraint": { 2484 | "and": [ 2485 | { 2486 | "equals": [ 2487 | { 2488 | "doc": "sys.type" 2489 | }, 2490 | "Entry" 2491 | ] 2492 | } 2493 | ] 2494 | } 2495 | }, 2496 | { 2497 | "effect": "allow", 2498 | "actions": ["create"], 2499 | "constraint": { 2500 | "and": [ 2501 | { 2502 | "equals": [ 2503 | { 2504 | "doc": "sys.type" 2505 | }, 2506 | "Asset" 2507 | ] 2508 | } 2509 | ] 2510 | } 2511 | }, 2512 | { 2513 | "effect": "allow", 2514 | "actions": ["read"], 2515 | "constraint": { 2516 | "and": [ 2517 | { 2518 | "equals": [ 2519 | { 2520 | "doc": "sys.type" 2521 | }, 2522 | "Entry" 2523 | ] 2524 | }, 2525 | { 2526 | "equals": [ 2527 | { 2528 | "doc": "sys.createdBy.sys.id" 2529 | }, 2530 | "User.current()" 2531 | ] 2532 | } 2533 | ] 2534 | } 2535 | }, 2536 | { 2537 | "effect": "allow", 2538 | "actions": ["update"], 2539 | "constraint": { 2540 | "and": [ 2541 | { 2542 | "equals": [ 2543 | { 2544 | "doc": "sys.type" 2545 | }, 2546 | "Entry" 2547 | ] 2548 | }, 2549 | { 2550 | "equals": [ 2551 | { 2552 | "doc": "sys.createdBy.sys.id" 2553 | }, 2554 | "User.current()" 2555 | ] 2556 | }, 2557 | { 2558 | "paths": [ 2559 | { 2560 | "doc": "fields.%.%" 2561 | } 2562 | ] 2563 | } 2564 | ] 2565 | } 2566 | }, 2567 | { 2568 | "effect": "allow", 2569 | "actions": ["delete"], 2570 | "constraint": { 2571 | "and": [ 2572 | { 2573 | "equals": [ 2574 | { 2575 | "doc": "sys.type" 2576 | }, 2577 | "Entry" 2578 | ] 2579 | }, 2580 | { 2581 | "equals": [ 2582 | { 2583 | "doc": "sys.createdBy.sys.id" 2584 | }, 2585 | "User.current()" 2586 | ] 2587 | } 2588 | ] 2589 | } 2590 | }, 2591 | { 2592 | "effect": "allow", 2593 | "actions": ["read"], 2594 | "constraint": { 2595 | "and": [ 2596 | { 2597 | "equals": [ 2598 | { 2599 | "doc": "sys.type" 2600 | }, 2601 | "Asset" 2602 | ] 2603 | }, 2604 | { 2605 | "equals": [ 2606 | { 2607 | "doc": "sys.createdBy.sys.id" 2608 | }, 2609 | "User.current()" 2610 | ] 2611 | } 2612 | ] 2613 | } 2614 | }, 2615 | { 2616 | "effect": "allow", 2617 | "actions": ["update"], 2618 | "constraint": { 2619 | "and": [ 2620 | { 2621 | "equals": [ 2622 | { 2623 | "doc": "sys.type" 2624 | }, 2625 | "Asset" 2626 | ] 2627 | }, 2628 | { 2629 | "equals": [ 2630 | { 2631 | "doc": "sys.createdBy.sys.id" 2632 | }, 2633 | "User.current()" 2634 | ] 2635 | }, 2636 | { 2637 | "paths": [ 2638 | { 2639 | "doc": "fields.%.%" 2640 | } 2641 | ] 2642 | } 2643 | ] 2644 | } 2645 | }, 2646 | { 2647 | "effect": "allow", 2648 | "actions": ["delete"], 2649 | "constraint": { 2650 | "and": [ 2651 | { 2652 | "equals": [ 2653 | { 2654 | "doc": "sys.type" 2655 | }, 2656 | "Asset" 2657 | ] 2658 | }, 2659 | { 2660 | "equals": [ 2661 | { 2662 | "doc": "sys.createdBy.sys.id" 2663 | }, 2664 | "User.current()" 2665 | ] 2666 | } 2667 | ] 2668 | } 2669 | } 2670 | ], 2671 | "permissions": { 2672 | "ContentModel": ["read"], 2673 | "Settings": [], 2674 | "ContentDelivery": [] 2675 | }, 2676 | "sys": { 2677 | "type": "Role", 2678 | "id": "3BfBcsMyyk2vTHgSvVE95c", 2679 | "version": 0, 2680 | "space": { 2681 | "sys": { 2682 | "type": "Link", 2683 | "linkType": "Space", 2684 | "id": "28p9vvm1oxuw" 2685 | } 2686 | }, 2687 | "createdBy": { 2688 | "sys": { 2689 | "type": "Link", 2690 | "linkType": "User", 2691 | "id": "066RqBikAjzKy0SWUEtFvH" 2692 | } 2693 | }, 2694 | "createdAt": "2017-05-11T12:01:17Z", 2695 | "updatedBy": { 2696 | "sys": { 2697 | "type": "Link", 2698 | "linkType": "User", 2699 | "id": "066RqBikAjzKy0SWUEtFvH" 2700 | } 2701 | }, 2702 | "updatedAt": "2017-05-11T12:01:17Z" 2703 | } 2704 | }, 2705 | { 2706 | "name": "Translator 1", 2707 | "description": "Allows editing of localized fields in the specified language", 2708 | "policies": [ 2709 | { 2710 | "effect": "allow", 2711 | "actions": ["read"], 2712 | "constraint": { 2713 | "and": [ 2714 | { 2715 | "equals": [ 2716 | { 2717 | "doc": "sys.type" 2718 | }, 2719 | "Entry" 2720 | ] 2721 | } 2722 | ] 2723 | } 2724 | }, 2725 | { 2726 | "effect": "allow", 2727 | "actions": ["read"], 2728 | "constraint": { 2729 | "and": [ 2730 | { 2731 | "equals": [ 2732 | { 2733 | "doc": "sys.type" 2734 | }, 2735 | "Asset" 2736 | ] 2737 | } 2738 | ] 2739 | } 2740 | }, 2741 | { 2742 | "effect": "allow", 2743 | "actions": ["update"], 2744 | "constraint": { 2745 | "and": [ 2746 | { 2747 | "equals": [ 2748 | { 2749 | "doc": "sys.type" 2750 | }, 2751 | "Entry" 2752 | ] 2753 | }, 2754 | { 2755 | "paths": [ 2756 | { 2757 | "doc": "fields.%.%" 2758 | } 2759 | ] 2760 | } 2761 | ] 2762 | } 2763 | }, 2764 | { 2765 | "effect": "allow", 2766 | "actions": ["update"], 2767 | "constraint": { 2768 | "and": [ 2769 | { 2770 | "equals": [ 2771 | { 2772 | "doc": "sys.type" 2773 | }, 2774 | "Asset" 2775 | ] 2776 | }, 2777 | { 2778 | "paths": [ 2779 | { 2780 | "doc": "fields.%.%" 2781 | } 2782 | ] 2783 | } 2784 | ] 2785 | } 2786 | } 2787 | ], 2788 | "permissions": { 2789 | "ContentModel": ["read"], 2790 | "Settings": [], 2791 | "ContentDelivery": [] 2792 | }, 2793 | "sys": { 2794 | "type": "Role", 2795 | "id": "3BgX7gCjpTS6ToMGo7tN6m", 2796 | "version": 0, 2797 | "space": { 2798 | "sys": { 2799 | "type": "Link", 2800 | "linkType": "Space", 2801 | "id": "28p9vvm1oxuw" 2802 | } 2803 | }, 2804 | "createdBy": { 2805 | "sys": { 2806 | "type": "Link", 2807 | "linkType": "User", 2808 | "id": "066RqBikAjzKy0SWUEtFvH" 2809 | } 2810 | }, 2811 | "createdAt": "2017-05-11T12:01:17Z", 2812 | "updatedBy": { 2813 | "sys": { 2814 | "type": "Link", 2815 | "linkType": "User", 2816 | "id": "066RqBikAjzKy0SWUEtFvH" 2817 | } 2818 | }, 2819 | "updatedAt": "2017-05-11T12:01:17Z" 2820 | } 2821 | }, 2822 | { 2823 | "name": "Translator 2", 2824 | "description": "Allows editing of localized fields in the specified language", 2825 | "policies": [ 2826 | { 2827 | "effect": "allow", 2828 | "actions": ["read"], 2829 | "constraint": { 2830 | "and": [ 2831 | { 2832 | "equals": [ 2833 | { 2834 | "doc": "sys.type" 2835 | }, 2836 | "Entry" 2837 | ] 2838 | } 2839 | ] 2840 | } 2841 | }, 2842 | { 2843 | "effect": "allow", 2844 | "actions": ["read"], 2845 | "constraint": { 2846 | "and": [ 2847 | { 2848 | "equals": [ 2849 | { 2850 | "doc": "sys.type" 2851 | }, 2852 | "Asset" 2853 | ] 2854 | } 2855 | ] 2856 | } 2857 | }, 2858 | { 2859 | "effect": "allow", 2860 | "actions": ["update"], 2861 | "constraint": { 2862 | "and": [ 2863 | { 2864 | "equals": [ 2865 | { 2866 | "doc": "sys.type" 2867 | }, 2868 | "Entry" 2869 | ] 2870 | }, 2871 | { 2872 | "paths": [ 2873 | { 2874 | "doc": "fields.%.%" 2875 | } 2876 | ] 2877 | } 2878 | ] 2879 | } 2880 | }, 2881 | { 2882 | "effect": "allow", 2883 | "actions": ["update"], 2884 | "constraint": { 2885 | "and": [ 2886 | { 2887 | "equals": [ 2888 | { 2889 | "doc": "sys.type" 2890 | }, 2891 | "Asset" 2892 | ] 2893 | }, 2894 | { 2895 | "paths": [ 2896 | { 2897 | "doc": "fields.%.%" 2898 | } 2899 | ] 2900 | } 2901 | ] 2902 | } 2903 | } 2904 | ], 2905 | "permissions": { 2906 | "ContentModel": ["read"], 2907 | "Settings": [], 2908 | "ContentDelivery": [] 2909 | }, 2910 | "sys": { 2911 | "type": "Role", 2912 | "id": "3Bi3CWAo8KktAQXSv6vl66", 2913 | "version": 0, 2914 | "space": { 2915 | "sys": { 2916 | "type": "Link", 2917 | "linkType": "Space", 2918 | "id": "28p9vvm1oxuw" 2919 | } 2920 | }, 2921 | "createdBy": { 2922 | "sys": { 2923 | "type": "Link", 2924 | "linkType": "User", 2925 | "id": "066RqBikAjzKy0SWUEtFvH" 2926 | } 2927 | }, 2928 | "createdAt": "2017-05-11T12:01:17Z", 2929 | "updatedBy": { 2930 | "sys": { 2931 | "type": "Link", 2932 | "linkType": "User", 2933 | "id": "066RqBikAjzKy0SWUEtFvH" 2934 | } 2935 | }, 2936 | "updatedAt": "2017-05-11T12:01:17Z" 2937 | } 2938 | }, 2939 | { 2940 | "name": "Translator 3", 2941 | "description": "Allows editing of localized fields in the specified language", 2942 | "policies": [ 2943 | { 2944 | "effect": "allow", 2945 | "actions": ["read"], 2946 | "constraint": { 2947 | "and": [ 2948 | { 2949 | "equals": [ 2950 | { 2951 | "doc": "sys.type" 2952 | }, 2953 | "Entry" 2954 | ] 2955 | } 2956 | ] 2957 | } 2958 | }, 2959 | { 2960 | "effect": "allow", 2961 | "actions": ["read"], 2962 | "constraint": { 2963 | "and": [ 2964 | { 2965 | "equals": [ 2966 | { 2967 | "doc": "sys.type" 2968 | }, 2969 | "Asset" 2970 | ] 2971 | } 2972 | ] 2973 | } 2974 | }, 2975 | { 2976 | "effect": "allow", 2977 | "actions": ["update"], 2978 | "constraint": { 2979 | "and": [ 2980 | { 2981 | "equals": [ 2982 | { 2983 | "doc": "sys.type" 2984 | }, 2985 | "Entry" 2986 | ] 2987 | }, 2988 | { 2989 | "paths": [ 2990 | { 2991 | "doc": "fields.%.%" 2992 | } 2993 | ] 2994 | } 2995 | ] 2996 | } 2997 | }, 2998 | { 2999 | "effect": "allow", 3000 | "actions": ["update"], 3001 | "constraint": { 3002 | "and": [ 3003 | { 3004 | "equals": [ 3005 | { 3006 | "doc": "sys.type" 3007 | }, 3008 | "Asset" 3009 | ] 3010 | }, 3011 | { 3012 | "paths": [ 3013 | { 3014 | "doc": "fields.%.%" 3015 | } 3016 | ] 3017 | } 3018 | ] 3019 | } 3020 | } 3021 | ], 3022 | "permissions": { 3023 | "ContentModel": ["read"], 3024 | "Settings": [], 3025 | "ContentDelivery": [] 3026 | }, 3027 | "sys": { 3028 | "type": "Role", 3029 | "id": "3BjfQSC3N7sqbZuUQmRC2a", 3030 | "version": 0, 3031 | "space": { 3032 | "sys": { 3033 | "type": "Link", 3034 | "linkType": "Space", 3035 | "id": "28p9vvm1oxuw" 3036 | } 3037 | }, 3038 | "createdBy": { 3039 | "sys": { 3040 | "type": "Link", 3041 | "linkType": "User", 3042 | "id": "066RqBikAjzKy0SWUEtFvH" 3043 | } 3044 | }, 3045 | "createdAt": "2017-05-11T12:01:17Z", 3046 | "updatedBy": { 3047 | "sys": { 3048 | "type": "Link", 3049 | "linkType": "User", 3050 | "id": "066RqBikAjzKy0SWUEtFvH" 3051 | } 3052 | }, 3053 | "updatedAt": "2017-05-11T12:01:17Z" 3054 | } 3055 | } 3056 | ] 3057 | } 3058 | -------------------------------------------------------------------------------- /gatsby-config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config({ 2 | path: `.env.${process.env.NODE_ENV}`, 3 | }); 4 | 5 | module.exports = { 6 | siteMetadata: { 7 | title: "Gatsby Contentful Starter", 8 | description: "Official Contentful Gatsby Starter", 9 | }, 10 | plugins: [ 11 | "gatsby-transformer-sharp", 12 | "gatsby-plugin-react-helmet", 13 | "gatsby-plugin-sharp", 14 | "gatsby-plugin-image", 15 | { 16 | resolve: "gatsby-source-contentful", 17 | options: { 18 | spaceId: process.env.CONTENTFUL_SPACE_ID, 19 | accessToken: process.env.CONTENTFUL_ACCESS_TOKEN, 20 | host: process.env.CONTENTFUL_HOST 21 | }, 22 | }, 23 | ], 24 | }; 25 | -------------------------------------------------------------------------------- /gatsby-node.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | exports.createPages = async ({ graphql, actions, reporter }) => { 4 | const { createPage } = actions 5 | 6 | // Define a template for blog post 7 | const blogPost = path.resolve('./src/templates/blog-post.js') 8 | 9 | const result = await graphql( 10 | ` 11 | { 12 | allContentfulBlogPost { 13 | nodes { 14 | title 15 | slug 16 | } 17 | } 18 | } 19 | ` 20 | ) 21 | 22 | if (result.errors) { 23 | reporter.panicOnBuild( 24 | `There was an error loading your Contentful posts`, 25 | result.errors 26 | ) 27 | return 28 | } 29 | 30 | const posts = result.data.allContentfulBlogPost.nodes 31 | 32 | // Create blog posts pages 33 | // But only if there's at least one blog post found in Contentful 34 | // `context` is available in the template as a prop and as a variable in GraphQL 35 | 36 | if (posts.length > 0) { 37 | posts.forEach((post, index) => { 38 | const previousPostSlug = index === 0 ? null : posts[index - 1].slug 39 | const nextPostSlug = 40 | index === posts.length - 1 ? null : posts[index + 1].slug 41 | 42 | createPage({ 43 | path: `/blog/${post.slug}/`, 44 | component: blogPost, 45 | context: { 46 | slug: post.slug, 47 | previousPostSlug, 48 | nextPostSlug, 49 | }, 50 | }) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | incoming-hooks = ["Contentful"] 3 | 4 | [template.environment] 5 | CONTENTFUL_SPACE_ID = "" 6 | CONTENTFUL_ACCESS_TOKEN = "" 7 | CONTENTFUL_PREVIEW_ACCESS_TOKEN = "" 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contentful-starter-gatsby-blog", 3 | "description": "Contentful Gatsby Starter Blog", 4 | "private": true, 5 | "version": "0.0.1", 6 | "author": "Contentful ", 7 | "bugs": { 8 | "url": "https://github.com/contentful/starter-gatsby-blog/issues" 9 | }, 10 | "dependencies": { 11 | "@contentful/rich-text-plain-text-renderer": "^15.12.1", 12 | "gatsby": "^5.5.0", 13 | "gatsby-plugin-image": "^3.5.0", 14 | "gatsby-plugin-react-helmet": "^6.5.0", 15 | "gatsby-plugin-sharp": "^5.5.0", 16 | "gatsby-source-contentful": "^8.5.0", 17 | "gatsby-transformer-sharp": "^5.5.0", 18 | "gh-pages": "^5.0.0", 19 | "lodash": "^4.17.21", 20 | "react": "^18.0.0", 21 | "react-dom": "^18.0.0", 22 | "react-helmet": "^6.1.0", 23 | "reading-time": "^2.0.0-1" 24 | }, 25 | "devDependencies": { 26 | "contentful-import": "^8.3.2", 27 | "gatsby-provision-contentful": "^0.0.5", 28 | "netlify-cli": "^10.0.0" 29 | }, 30 | "engines": { 31 | "node": ">=18.0.0" 32 | }, 33 | "homepage": "https://github.com/contentful/starter-gatsby-blog#readme", 34 | "keywords": [ 35 | "gatsby", 36 | "contentful", 37 | "starter" 38 | ], 39 | "license": "MIT", 40 | "main": "n/a", 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/contentful/starter-gatsby-blog.git" 44 | }, 45 | "scripts": { 46 | "dev": "gatsby develop", 47 | "build": "gatsby build", 48 | "heroku-postbuild": "gatsby build", 49 | "serve": "gatsby serve", 50 | "postinstall": "node ./bin/hello.js", 51 | "setup": "node ./bin/setup.js", 52 | "netlify:login": "netlify login", 53 | "netlify:deploy": "netlify deploy -d public -p", 54 | "gatsby-provision": "gatsby-provision-contentful --contentful-data-path='./contentful/export.json' --space-id=$CONTENTFUL_SPACE_ID --management-token=$CONTENTFUL_MANAGEMENT_TOKEN" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/contentful-labs/starter-gatsby-blog/568f1b417277da135c7054a9ef80bbc166e34610/screenshot.png -------------------------------------------------------------------------------- /src/components/article-preview.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { GatsbyImage } from 'gatsby-plugin-image' 4 | import { renderRichText } from 'gatsby-source-contentful/rich-text' 5 | 6 | import Container from './container' 7 | import Tags from './tags' 8 | import * as styles from './article-preview.module.css' 9 | 10 | const ArticlePreview = ({ posts }) => { 11 | if (!posts) return null 12 | if (!Array.isArray(posts)) return null 13 | 14 | return ( 15 | 16 | 35 | 36 | ) 37 | } 38 | 39 | export default ArticlePreview 40 | -------------------------------------------------------------------------------- /src/components/article-preview.module.css: -------------------------------------------------------------------------------- 1 | .article-list { 2 | margin: 0; 3 | padding: 0; 4 | list-style: none; 5 | display: grid; 6 | grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); 7 | grid-gap: var(--space-3xl); 8 | } 9 | 10 | .title { 11 | display: inline-block; 12 | font-size: var(--text-lg); 13 | margin-bottom: var(--space-xs); 14 | margin-top: var(--space-lg); 15 | border-bottom: 1.5px solid transparent; 16 | } 17 | 18 | .link { 19 | text-decoration: none; 20 | } 21 | 22 | .link:hover .title { 23 | border-bottom-color: var(--primary); 24 | } 25 | 26 | .meta { 27 | display: flex; 28 | justify-content: space-between; 29 | } 30 | -------------------------------------------------------------------------------- /src/components/container.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Container = ({ children, as = 'div' }) => { 4 | const Tag = as 5 | 6 | return ( 7 | 14 | {children} 15 | 16 | ) 17 | } 18 | 19 | export default Container 20 | -------------------------------------------------------------------------------- /src/components/footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import Container from './container' 4 | import * as styles from './footer.module.css' 5 | 6 | const Footer = () => ( 7 | 8 |
9 | Built with Contentful and{' '} 10 | Gatsby ·{' '} 11 | Source 12 |
13 |
14 | ) 15 | 16 | export default Footer 17 | -------------------------------------------------------------------------------- /src/components/footer.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | border-top: 1px solid var(--border); 3 | font-size: var(--text-sm); 4 | margin-top: var(--space-2xl); 5 | padding: var(--space-2xl) 0; 6 | text-align: right; 7 | } 8 | 9 | .container a { 10 | border-bottom: 1.5px solid transparent; 11 | font-weight: var(--medium); 12 | } 13 | 14 | .container a:hover { 15 | border-bottom-color: var(--primary); 16 | color: var(--primary); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/global.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Inter var'; 3 | font-weight: 100 900; 4 | font-display: swap; 5 | font-style: normal; 6 | font-named-instance: 'Regular'; 7 | src: url('/fonts/Inter-roman.var.woff2?v=3.19') format('woff2'); 8 | } 9 | 10 | @font-face { 11 | font-family: 'Inter var'; 12 | font-weight: 100 900; 13 | font-display: swap; 14 | font-style: italic; 15 | font-named-instance: 'Italic'; 16 | src: url('/fonts/Inter-italic.var.woff2?v=3.19') format('woff2'); 17 | } 18 | 19 | html { 20 | scroll-behavior: smooth; 21 | } 22 | 23 | html * { 24 | box-sizing: border-box; 25 | } 26 | 27 | body { 28 | background: var(--background); 29 | color: var(--text-color); 30 | font-family: var(--font-sans); 31 | font-size: 16px; 32 | font-weight: var(--body); 33 | line-height: var(--default); 34 | margin: 0; 35 | text-rendering: optimizeLegibility; 36 | -webkit-font-smoothing: antialiased; 37 | } 38 | 39 | img { 40 | display: block; 41 | width: 100%; 42 | } 43 | 44 | h1, 45 | h2, 46 | h3, 47 | h4, 48 | h5, 49 | h6 { 50 | font-weight: var(--semibold); 51 | line-height: var(--dense); 52 | } 53 | 54 | a { 55 | color: currentColor; 56 | text-decoration: none; 57 | } 58 | 59 | a:hover { 60 | color: var(--primary); 61 | } 62 | 63 | a.active { 64 | font-weight: var(--bold); 65 | } 66 | 67 | tt, 68 | code, 69 | kbd { 70 | background: var(--black-fade-5); 71 | padding-bottom: 0.2em; 72 | padding-top: 0.2em; 73 | } 74 | 75 | tt, 76 | code, 77 | kbd { 78 | font-family: var(--font-monospace); 79 | font-size: 90%; 80 | } 81 | 82 | tt, 83 | code { 84 | background-color: var(--black-fade-5); 85 | border-radius: var(--radius-sm); 86 | font-weight: var(--medium); 87 | padding: 0.2em 0; 88 | } 89 | 90 | code:before, 91 | code:after, 92 | tt:before, 93 | tt:after { 94 | content: ' '; 95 | letter-spacing: -0.2em; 96 | } 97 | 98 | pre, 99 | code { 100 | font-family: var(--font-monospace); 101 | } 102 | -------------------------------------------------------------------------------- /src/components/hero.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { GatsbyImage } from 'gatsby-plugin-image' 3 | import { renderRichText } from 'gatsby-source-contentful/rich-text' 4 | 5 | import * as styles from './hero.module.css' 6 | 7 | const Hero = ({ image, title, content }) => ( 8 |
9 | {image && ( 10 | 11 | )} 12 |
13 |

{title}

14 | {content && ( 15 |
{renderRichText(content)}
16 | )} 17 |
18 |
19 | ) 20 | 21 | export default Hero 22 | -------------------------------------------------------------------------------- /src/components/hero.module.css: -------------------------------------------------------------------------------- 1 | .hero { 2 | align-items: flex-end; 3 | display: flex; 4 | margin: 0 auto; 5 | max-width: var(--size-max-width); 6 | min-height: 200px; 7 | padding: 0 0 var(--size-gutter); 8 | position: relative; 9 | } 10 | 11 | .image { 12 | /* 13 | Ensure golden ratio for the hero size while limiting it to some extend to 14 | the viewport width 15 | */ 16 | height: 61.8vh; 17 | max-height: 560px; 18 | width: 100%; 19 | } 20 | 21 | .details { 22 | background: var(--background); 23 | left: 0; 24 | max-width: 40rem; 25 | width: 85%; 26 | padding: var(--space-2xl) var(--size-gutter) 0; 27 | position: absolute; 28 | } 29 | 30 | .title { 31 | letter-spacing: -0.012em; 32 | font-size: var(--text-title); 33 | font-weight: var(--extrabold); 34 | line-height: var(--solid); 35 | margin: 0 0 var(--space-xl); 36 | } 37 | 38 | .content { 39 | font-size: var(--text-lead); 40 | font-weight: var(--semibold); 41 | margin: 0; 42 | } 43 | 44 | @media (min-width: 480px) { 45 | .details { 46 | padding-top: var(--space-3xl); 47 | } 48 | } 49 | 50 | @media (min-width: 768px) { 51 | .hero { 52 | padding-top: var(--size-gutter); 53 | } 54 | 55 | .details { 56 | max-width: 44rem; 57 | padding: var(--space-3xl) var(--size-gutter) 0; 58 | } 59 | } 60 | 61 | @media (min-width: 1024px) { 62 | .image { 63 | margin-left: auto; 64 | width: 85%; 65 | } 66 | 67 | .details { 68 | left: var(--size-gutter); 69 | padding: var(--space-3xl) var(--size-gutter) 0 0; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/components/layout.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import './variables.css' 4 | import './global.css' 5 | import Seo from './seo' 6 | import Navigation from './navigation' 7 | import Footer from './footer' 8 | class Template extends React.Component { 9 | render() { 10 | const { children } = this.props 11 | 12 | return ( 13 | <> 14 | 15 | 16 |
{children}
17 |