Content calendars are an essential part of any content management toolkit. They display your content in a familiar calendar view, making it easy to see when content is being published.
Scheduling is also a breeze. Want to change when an item is published? Simply drag the item to the new date and you’re done.
A content calendar is a publisher-specific view of content. Traditionally, such a view would be part of the content management app. But with Contentful we don’t have to follow tradition. Rather than creating content management Swiss Army knives, we can create streamlined, uncluttered, fit-for-purpose interfaces.
We’ll be building it for a single content type called article.
Content calendars provide future publishing views of your content. They allow you to say: I want to publish this content on this date.
Out-of-the-box, Contentful doesn’t support future publishing. So, we will need to create a new property,
publishDate, to hold our future publishing date. When that date becomes due, we can then publish the item, either manually or via a triggered process.
publishDate property in place, we can now start building the calendar.
We’re going to use RiotJS to build the interface. I like Riot’s ‘simple and elegant component’ approach. Of course, you might prefer React, Angula, Ember, etc. But the interactions between the web app and Contentful will be largely the same.
Our interface consists of two main components:
All draft articles can be dragged between the pipeline and the calendar, where:
We’ll include published articles in the calendar so that we can see what’s been published recently. However, published articles will not be be draggable.
The pipeline and calendar components are wrapped in an app component just to keep things nice and tidy. It also houses our drag functionality. More on that later.
It’s going to be useful to understand how status is worked out in Contentful. If you look at your content in the Contentful app, you’ll see Draft, Published, Updated and Archived.
Each entry in Contentful has a set of system properties and a set of user-defined properties, the fields that you define in the content model. Contentful does not use a dedicated status property but determines an entry’s status by checking a number of system properties:
publishedVersion) then it’s in Draft
publishedAtdate and version is the same as
publishedVersionthen it’s Published
publishedAtdate and version is not the same as
publishedVersionthen it’s Updated. Note, the entry is still published, it’s just that the latest updates have not been published
archivedDatethen it is Archived.
We need to keep this in mind when getting the data for our components.
The pipeline component lists Draft articles that have not yet been assigned a future publishing date.
This is a pretty typical of a Riot component:
In the template, we’re actually calling another component to output the pipeline articles.
This allows us to reuse the same layout for both pipeline and calendar items.
items is a JSON array of our pipeline articles. The
each attribute is a loop function: Riot will output a
content-entry component for each member of the
It’s worth taking a quick look at the content entry component. It has no script but does add some vital attributes to the item:
It’s just outputting a simple Bulma card that only contains the article’s
title. Importantly, though, it adds a
published class if the article has a
sys.publishedAt property. We’ll be using this later when implementing the drag-and-drop update of the
We’re taking advantage of Riot’s event-driven functionality to get the pipeline data into our app. Here’s how it works.
If you look in app.js you’ll find the following:
1. Traps the
load_pipeline event raised when our pipeline component is first built.
publishedAt property (i.e. are Draft) and a publishDate property.
3. Triggers the
pipeline_loaded event, passing the articles returned by Contentful.
We use the
rawRequest method of the SDK as we can use the returned JSON directly when updating our pipeline output.
The pipeline component traps the
pipeline_loaded event and uses the passed articles to update itself:
This event-driven approach allows us to keep our components relatively simple and easier to reuse.
The calendar uses the same event-driven approach to build and load the calendar data.
buildCalendar function creates a JSON representation of a calendar (months and days). It then appends articles with a defined
publishDate property, or that have already been published, to the appropriate day.
The result is the following JSON structure:
Obviously, we have a maximum of 7 items in the
days array. The number of
weeks depends on the range we are displaying. The default is 4 weeks prior to the current date and 12 weeks after the current date.
The calendar data is then used to update the component, using the following template:
The output is split into two tables: one for headings and one for the actual days. This allows the days to be scrolled whilst keeping the headings stationary.
The template creates a table row for each
week and a table cell for each day. Each table cell will contain any articles that are being published on that day. The data attributes
data-date-local are added to help with updating the
publishDate when an article is dragged to a new position.
publishDate can be updated by editing the article in the Contentful app. But it would be much easier to just drag it to a new day in the calendar.
publishDate(target has a class of pipeline-items)
Now let’s translate these into Dragula:
Our calendar table’s cells have a day class. Our pipeline articles are wrapped in a
div with the pipeline-items class. So, let’s restrict drag-and-drop to articles that are children of these containers:
Now let’s narrow down where articles can be dropped. The target must have a current class (calendar table cells for today onwards) or a pipeline-items class:
And finally lets prevent articles with a published class from being selected at all:
Now let’s handle the dropping of an articles. Let’s start by checking if we actually need to do anything:
When an article is dropped onto a future day, let’s update the
publishDate. The article’s
id is on the element being dropped (
el). The new
publishDate value is stored as a data attribute on the table cell (
And when an article is dropped back into the pipeline then let’s clear its
In both cases, we trigger a
First we get a reference to our Contentful space and environment:
Now we can get the article using the passed
We’ve got the article so let’s update the
Let’s log the update or any error that’s occured:
The calendar needs to include an API token when making requests to the Content Management API. There are a couple of different ways of getting a token, depending on how you want to use your app.
You can create Personal Access Tokens in the Contentful app. They effectively give all users of the app the same permissions as you.
To create a Personal Access Token:
Your token will be generated and displayed in the response. Copy it into your config file.
Remember this token will be in your client-side code. You should not use it in publicly available apps.
The other option is to use OAuth tokens. These grant the same permissions to your app as the Contentful user. When the user opens your application the following steps takes place:
Your app can persist the local storage of the token to prevent having to go through the process every time the app is opened.
Before you can use OAuth tokens you need to register your application with Contentful.
When you’ve registered your app, update
config.js with the Client ID and the Redirect URI.
If you don’t specify a Personal Access Token in the
config.js file then the calendar app will use OAuth tokens.
A content calendar is about future publishing. To really complete the process we want to automate the publishing of articles based on their
This can be done by a regularly scheduled cloud function that publishes articles that have an appropriate
Here’s an example in Python that uses the Contentful Management SDK:
Import the relevant libraries
Set some constants: master is the default environment id.
Create the Contentful Management client
Get the entries with a
publishDate that is between now and the last time function ran.
Loop through the returned
articles and publish if necessary.
This is a very basic approach. You might want to add checking that linked entries and assets are also published and either reporting exceptions or publishing them too.
The content calendar is a great example of an app that provides limited functionality to a specific audience.
Traditional content management systems generally achieve this via plugins. This increases the complexity of the interface, introduces potential conflicts with other plugins and can negatively impact upgrade paths.
Contentful, with its excellent API support, allows us to quickly and easily build small, agile standalone apps. They are easier to learn, easier to use and easier to maintain.
The content manager’s toolkit is no longer a swag of competing plugins. But a suite of complementary, fit-for-purpose apps.