Channels 101

Jeff Neitzel edited this page May 21, 2015 · 3 revisions


The concept of channels is a way to reimagine the publishing of posts in Ghost. Essentially a channel is a single flow of content which can be accessed from a given Ghost blog. This document aims to further explain the concept of channels, to outline the intended flexible structure, and discuss the potential for defaults and configuration options.

The Default Channels

The most obvious example of a channel in Ghost is the home page. This provides a channel of all published posts available on the blog, in reverse chronological order (newest first) with a matching RSS feed. It can be customised to show a different number of posts per page (PPP), and will paginate so that it is possible to view the entire history or archive of the channel.

This is the Index or Main channel - it's the main focus or purpose of the blog.

Ghost has two more dynamically generated 'classes' of channel built in. Individual Author channels, and Individual Tag channels. Each author on your blog will get their own channel, which shows only the posts written by this author. Likewise, each tag you create gets its own channel showing all of the posts which are associated with that tag. Each channel gets paginated with the blog-level posts-per-page setting, and has an RSS feed.

These are the default channels built into Ghost. They cannot currently be modified, disabled or added to in any way.

Channels as a Resource

From this description of what a channel is, we can build up the idea of a channel as a type of resource available in a blog, much like Posts, Tags, and Users.

A channel has:

  • Name (note: these map cleanly to contexts)
  • Base query
  • Base Route
  • Template
  • No. posts per page (PPP)
  • Paginated? Route
  • RSS feed? Route, PPP, full content?
  • Default? (or custom)
  • (Coming Soon) Affect on the sitemap
  • (Coming Soon) Structured data/meta data present on the front page
  • (Maybe) different PPP on first page

To expand this further out into examples, here's a breakdown of the values for the default channels:

Index channel:

  • Name: Index
  • Base Query: /api/posts/?status=published (this is the default status)
  • Base Route: /
  • Template: index.hbs (with home.hbs for the front page)
  • PPP: 6 (default) - comes from the single postsPerPage setting
  • Paginated: Yes, Route: /page/:num
  • RSS feed: Yes, Route: /rss/, PPP: 15, paginated, shows full post content
  • Default
  • Sitemap: Home page listed in pages

Individual Author channel:

  • Name: Author
  • Base Query: /api/posts/?author=:author_slug
  • Base Route: /author/:author_slug/
  • Template: author.hbs
  • PPP: 6 (default) - comes from the single postsPerPage setting
  • Paginated: Yes, Route: :author_base_route/page/:num/
  • RSS feed: Yes, Route: :author_base_route/rss/, PPP: 15, paginated, shows full post content
  • Default
  • Sitemap: First page listed in pages

The Individual Tag channel is identical, except it uses 'tag' everywhere the Author channel uses author.

If we reimagine channels as resources in this way, we can start to see how we could refactor this concept to become a new model and API endpoint in Ghost. The default channels would be fixtures, with some customisations possible via the Settings UI (like PPP), and the ability to add new custom channels either via a core UI or via an App.

The concept of the first or front page of a channel may need to be addressed more strongly across Ghost (i.e giving it its own context as opposed to being the absence of the paged context) as the first page has a nicer route, it goes into the sitemap, it could reasonably require a different PPP setting and also ought to have structured data to make it more shareable and possibly also the possibility of a custom template.

A Note on Next/Prev:

Channels make clear the need for us to enhance the 'prev and next post' feature, so that we can always provide this within the current channel, rather than always being scoped to the Index channel. This would require us to maintain some state about which channel we are coming from when viewing a post, and the best way to maintain this sort of state is via the URL.

When viewing a post inside of a channel, we should get the URL /path/to/channel/path/to/post/. The canonical URL for the post should always be just /path/to/post/. The urlFor functionality in Ghost would need updating to take the channel/context into account properly.

Customising Default Channels

Many of the requests we get for blog customisations centre around modifying channel properties. For the most part, the customisations are so specific and rare that whilst Ghost core should definitely support customising them via an App, adding a specific UI is unnecessary - for the purpose of this document, these features are referred to as No-UI. Certain properties unquestionably require a UI, and this next section clarifies what settings we have, as well as the ones we want to add.

Posts per page (PPP)

Currently we have a single global setting for PPP which applies across all pages of all channels. This is OK for a UI, but channels would give us a different value for index, author and tag channels which could use the default or be customised.

Another oft-requested, probably no-UI customisation, is to make the PPP value for the first page of a channel different to the subsequent paginated pages. A common example of this request is that the homepage show just a single post, but the subsequent pages show many.

Index channel Base Query

A very interesting power-user customisation request is the ability to modify the base query so that certain posts are excluded from the Index channel.

The common example of this is using a tag to create a category of posts that you only want to show in their own channel and not in the index. A UI for this would definitely be an advanced feature, but perhaps one that is worthwhile to convince business and enterprise users that Ghost has the power & flexibility they need.

The suggestion for this UI is to use a plain text field with gmail style syntax:

Filter: [tags: "-recipes", author: "-fred"]

This would exclude any posts with the tag 'recipes' or written by the author 'fred' from the Index channel. This should affect not just the homepage, but also the paginated pages of the Index channel and the RSS feed, and so on.

Because the tag and author channels are all dynamically generated to have a query that represents an individual resource, it doesn't make sense to provide the ability to customise those channels. However, there are slightly different use cases for disabling the channel for a resource...

Tags & Authors without a Channel

In the Tags 101 document on the Ghost wiki, one of the advanced features which is described is tag visibility, which would allow for setting a tag to be private. Private tags would not get their own channel (as well as not being output via the {{tags}} helper, and not being returned by the API by default). This is because there are many use-cases for using tags in ways which then don't make sense with a channel, such as using them to define post types or simply to create hierarchy.

Imagining a use-case for having authors without a channel is a little bit harder. Perhaps there might be a case for disabling authors where there are guest authors who only have a single post, or old authors which no longer write for a publication. This lack of a clear use-case means it's likely a no-UI feature for authors.

Customising Routes

Something we've always known we need to do, is make it possible to customise the routes which use English words, like the tag and author channel routes, and the pagination route used across all channels (/page/:num/). This is not just needed for translations, but also particularly for tags to help blogs with different taxonomy concepts. E.g. if you're a music blog, you might use tags to represent genres, and want the route to be /genre/:tag-slug/.

Some people also want to have the index channel live on a different route, and use the / route for a static page, which would become a configurable option with the channel concept.

### RSS Customisations

The RSS feeds are a hotbed for potential customisations. We use /rss/ as the default canonical route, but also provide /feed/ as a built in 301 redirect. Both the default and the aliases could be reasonably customised. The default PPP for an RSS feed is 15, we show the full content for each post, and we also paginate the RSS feed so that the entire channel archive is available. All of these are properties that might require customisation.

Refactoring for channels will allow us to create much more flexibility in the RSS feeds, meaning that through apps these will become highly customisable.

Creating Custom Channels

The awesome benefit from turning channels into a resource is the ability to define completely new channels. This could be built out of any valid query to the Post API endpoint. The idea would be to provide for fancy combinations like posts with certain tags, written by a certain person, published in a certain timeframe, and so on and so forth.

E.g. I could define a route on my blog of '/my-furry-friends/' and lists out all the posts that are written by me rather than one of my guest authors, and which have a tag of 'cats' or 'dogs'. My new channel will get fancy structured data, create a new page in my Sitemap, generate an RSS feed, work with pagination exactly how I'd expect, and get it's own context for use in themes - all with minimal need for code or a UI.

ghost.registerchannel({name: 'furries', route: '/my-furry-friends/', query: 'tags: "cats, dogs", authors: "hannah"', ppp: 10});

I now have a new context of 'furries' in my blog, the route outputs all my furry friends and I can automatically use furries.hbs to customise how my channel displays (with index.hbs as a fallback). Perhaps the front page gets its own template like furries-front.hbs.

Building archives

One of the ways this can be used is to build custom archives for a blog. I could define a new channel with a route of '/archives/' with a really high PPP of 100, or even 'all' and list out my posts in a different template to my normal Index channel.

ghost.registerchannel({name: 'archive', route: '/archive/', ppp: 'all', RSS: false});

Getting there...

Refactoring Ghost to have channels as a concept at the model-layer is far from straightforward, so rather than doing it in one big go, lets focus on abstracting out one thing at a time and providing for the most commonly requested customisation using the existing settings framework.

If we refactor the front controller, moving out the hard coded concepts into configuration & generation code, then eventually we should have abstracted out enough of the hardcoded behaviour that the switch to using a model rather than settings will be more manageable.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.