An extension for using roots with the Contentful CMS API.
Note: This project is in early development, and versioning is a little different. Read this for more details.
We love static sites. They're fast, resilient, simple, and cheap.
However, managing and updating content on a static site is a pain. This extension allows you to load data from Contentful's API into your roots project for use in your view templates during compilation. Non-developers get an easy way to publish and manage content, while developers can still build static sites in a sane way.
- make sure you are in your roots project directory
npm install roots-contentful --save
- modify your
app.coffee
file to include the extension, as such
contentful = require 'roots-contentful'
# ...
module.exports =
extensions: [
contentful
access_token: 'YOUR_ACCESS_TOKEN'
space_id: 'xxxxxx'
locale: 'tlh'
locales_prefix:
tlh: 'klingon_'
content_types:
blog_posts:
id: 'xxxxxx'
template: 'views/_post.jade'
filters: { 'fields.environment[in]': ['staging', 'production'] }
path: (e) -> "blogging/#{e.category}/#{slugify(e.title)}"
write: 'data.json'
sort: compareFunction
transform: transformFunction
press_links:
id: 'xxxxxx'
]
# ...
A contentful
view helper object will be passed into every view containing your content. Each content type will be set as a property on contentful
using the name
option in your app.coffee
configuration. For example with the app.coffee
file above, you can access the blog posts like this:
h1 Hello World
ul
- for post in contentful.posts
li
h2= post.title
p= markdown(post.body)
Note: for the above markdown
function to parse your post.body
, you need to install a markdown parser for roots to work with. Using marked as an example, do the following:
npm install marked --save
And then in app.coffee
:
contentful = require 'roots-contentful'
marked = require 'marked'
locals:
markdown: marked
See the roots documentation for more details.
Note: if you have Links in your content more than 10 levels deep (the max for the include
parameter), then unresolved links can be returned.
If a template
option is defined for a Content Type in app.coffee
, roots will compile a single page view for each entry in that Content Type collection. The entry will also have a _url
key that returns the path to the single page view (so you can create links on an index page for example).
Contentful's documentation shows the API response when fetching an entry. Your content fields are nested in a fields
key on the entry
object. As a convenience, the entry object roots-contentful makes available in your views will have the fields
key's value set one level higher on the object. System metadata remains accessible on the sys
key and roots-contentful will raise an error if you have a field named sys
. Inside your views, the entry object will have this structure:
"entry": {
"title": "Wow. Such title. Much viral",
"author": "The Doge of Venice"
# ... the rest of the fields
"sys": {
"type": "Entry",
"id": "cat"
# ...
}
}
And can be accessed in your view like this:
h2= entry.title
p= markdown(entry.body)
if entry.image
img(src!= asset(entry.image)
Required. Your Contentful Delivery access token (API key).
Required. The space ID containing the content you wish to retrieve.
Optional. (Boolean) Allows you use the Contentful Preview API. Also able to be accessed by setting the environment variable CONTENTFUL_ENV
to "develop"
(preview api) or "production"
(default cdn).
Locales allow you to request your content in a different language, tlh
is Klingon.
Optional. (String or Array) Defines locale for all content_types.
String: 'tlh'
Array: ['en-es', 'tlh']
Wildcard: '*'
- grabs all locales from contentful
Optional. (String) Define content_types locale, will override global locale. Add locale: 'tlh'
to any of your content types and you'll retrieve that post in the specific locale.
Optional. (Object) Defines the prefix given to a group of locales.
locales_prefix:
'tlh': 'klingon_'
'en-es': 'spanish_'
Lets say you have 3 global locales defined, ['tlh', 'en-us', 'en-es']
, and above is our defined locales_prefix. Since we did not declare 'en-us'
you can access it with contentful.en_us_blog_posts
. Similarly you can access each preix according to what you've set in the object above. 'tlh'
would be accessible by contentful.klingon_blog_posts
and en-es
by contentful.spanish_blog_posts
.
An object whose key-value pairs correspond to a Contentful Content Types. Each
content type's entries will be set on the contentful
locals object using
the key used in the config.
Each object in the content_types array can have the following properties:
Required. The Content Type's ID on Contentful.
Optional. This is the name of the key the entries will be attached to on the contentful
object in your views. Defaults to a pluralized, underscored representation of the Content Type name (e.g. 'Blog Post' => contentful.blog_posts
)
Optional. Path relative to the roots project of a template for a single entry view. Each entry in the Content Type will be passed into the template in an entry
variable. If not given, the Content Type will not be compiled into single entry views and will only be attached to the contentful
view helper object.
Optional. Takes an object with different search and filter criteria. See examples of how to structure the object in Contentful's docs.
For example, the following limits the API response to 5 entries, filters by the feature
field and orders items in the response by the sys.createdAt
property. Notice the minus sign, which reverses the sort order.
content_types:
blog_posts:
id: 'xxxxxx'
template: 'views/_post.jade'
path: (entry) -> "blog/#{entry.permalink}"
filters: {
'limit': '5',
'fields.feature': 'true',
'order': '-sys.createdAt'
}
transform: transformFunction
Optional. Provide a function that returns a string of the relative path to the output file for a given entry without the extension. First argument passed into the function is the entry. Default is <name>/<slug>
where slug
is the slugified output of the entry's displayField
(a property of the Content Type), and name
is the provided name
option above or the default value. This option is ignored if no template
is given.
If the function returns an array of paths, the given entry is compiled to multiple files. The array of paths can be accessed with entry._urls
. For instance, the configuration below outputs each blog post entry into two folders, en
and fr
, for i18n purposes:
content_types:
blog_posts:
id: 'xxxxxx'
template: 'views/_post.jade'
path: (e) -> ("#{lang}/#{slugify(e.title)}" for lang in ['en', 'fr'])
Optional. Provide the relative path to the output file that will hold the JSON data of the current content type. The top level JSON object will be an array.
Optional. Provide a function to transform (map) every entry in a content type. The transformed data is then added to the jade locals variable and written to JSON (if the write property is provided). Transform can also return a when.js promise.
transformFunction = (entry) ->
delete entry.myProperty
entry
Optional. Provide a standard compare function that will sort all the data of a content type. The data is sorted after it is transformed and before it is provided to the jade locals variable or written as JSON.
compareFunction = (a, b) ->
# 0 => a and b are equal
# -1 or less => a is before b
# 1 or more => a is after b
a.number - b.number
roots-contentful also provides a convenient view helper called asset
that allows you to pass in the asset object returned from their API and returns the url. You can also pass in options that will be appended to the image url as a a query string that allows you to specificy size and quality params as documented here.
- for post in contentful.posts
img(src!= asset(post.image, {w: 100, h: 100, q: 50}))
- Details on the license can be found here
- Details on running tests and contributing can be found here