The OctoberCMS Ecosystem
OctoberCMS is a very powerful platform that deserves much more attention than it gets. Although it is slowly gaining popularity, it could be used by so many more. It really is more than a CMS...it is a CMS Builder. What I mean by this is that with October, it's easy to build a custom CMS for your clients so that they can easily maintain their websites, while at the same time keeping your code base lean and mean (and performant).
I think one of the challenges is language and documentation. OctoberCMS docs take a lot of getting used to, and once you really dive into the platform you find that many things are completely undocumented. Additionally, there has been very little "news" or "hype" around the platform over the past few years. On the one hand it's great that - as developers - we don't get breaking updates dumped on our heads every few years. On the other hand, it would be good to get some reassurance that the platform is around to stay for a long long time.
Another issue is that some of the concepts and language used to name the different features of October is confusing. This post is meant to share my understanding of what is what in Octoberland - but I am no expert, so I really look forward to develop this article further based on your comments. It is also not extensive, topics like layout placeholders and page variables etc. are not covered.
The CMS Area - Pages vs. Partials vs. Content
Layouts are simple - these provide the scaffolding for your site and you associate each page with one layout. Assets are also straightforwar - here you can upload any CSS/Fonts/Images/Javascript etc. files that you are using, and if you start with a theme, you will already have a bunch of stuff in here. Components are dynamic pieces of stuff that can be added to partials and pages...and content blocks. They basically come with any plugins that you install (and it doesn't take rocket science to develop your own plugin either).
But what about pages and partials and content? These three can be confusing. This is my understanding:
Pages are basically templates. You shouldn't really have content, such as text, in your pages - especially if you are planning on creating a multilingual website. A page may be one "actual" page that your visitors see, or it may be a template that is used for example to format blog posts.
Partials are reusable template pieces that can be shared across pages. For example, your site header and footer may be partials.
Content are "static"* files. They can be .htm files with html/css, they can be plain .txt files, or written in Markdown syntax (and saved with .md extension). Content files have limitations: you cannot use Twig, and you cannot include (most) components. They are great, however, for blocks of content that will be maintained by the site editor, because OctoberCMS provides several tools to do so. Additionally, the OctoberCMS Translate plugin fully supports content blocks, so you can easily provide several translations for any given piece of content.
*Note that you can actually inject stuff dynamically into content files through the use of "variables".
The OctoberCMS Pages Plugin
There are some plugins developed by the makers of OctoberCMS that arguably should really be a part of the base OctoberCMS installation. The Pages plugin is one of those. It basically adds wysiwyg editing functionality to your site which you can then use to easily edit your content, or possibly to configure it for use by the site editor(s). And this is where some of the confustion starts.
So you basically get a new backend "Pages" area:
Here you can also edit..."Pages" as well as "Content", and you have a new concept, "Snippets."
But the "Pages" here are not the same as the pages in the "CMS" area. I will call them "pagespages" from now on to differentiate them from the "Pages" you can create in the CMS area (the official docs often refer to them as "static pages", even though they are far from static).
So, pagespages are not the same as CMS pages. In fact, any pagespage you create you can then access from the "Content" page in the CMS area. You can also edit any pagescontent from there as well.
Look, I have a page called "test-pages-page" under Pages/Pages:
I also have some content:
You can see both of these under CMS / Content:
Very confusing. However, there is a point to all of this. In fact this is where the "CMS Builder" functionality comes in. If you - as a developer - restrict your content editors' access accounts so that they only see the "Pages" menu item, then their editing experience will be very simple and straightforward. And you, as a developer, can completely customize their editing experience - without writing any plugins or diving deep into CMS code. This is where "Placeholders" and "Snippets" and "Syntax Fields" come into play. Let me introduce each one with an example.
Placeholders
Placeholders allow pages to inject content to the layout. Placeholders are defined in the layout templates with the{% placeholder %}
tag.
(from the official docs)
Placeholders defined in the layout are automatically detected by the Static Pages plugin. The Edit Static Page form displays a tab for each placeholder defined in the layout used by the page. (from the Pages plugin docs)
So let's try it out.
For our example, let's say we are building a site where the site maintainers/editors can publish short stories. We'll build a simple layout using the grid system with help from css-tricks.com to look like this:
Title | Info |
Text | |
Footer |
So we create a layout and drag-and-drop the Pages / Static page component into it:
We also add a "placeholder" to the layout, so our final layout looks like this (CSS omitted):
<!DOCTYPE html>
<html>
<head>
{% styles %}
</head>
<body>
<header>
Short Stories
</header>
<main>
{% component 'staticPage' %}
</main>
<aside>
<pre>{% placeholder info default title="Info" type="text" %}
Let us know of any information you know about this short story!
{% endplaceholder %}</pre>
</aside>
<footer>
© My Awesome Self
</footer>
{% scripts %}
</body>
</html>
And voila, on the pagespage you now have an extra tab where you can enter extra "information" about the short story:
And, once saved, it looks like this:
Snippets
Snippets are much like CMS Components, in fact any CMS component can easily become a snippet:
Any component can be registered as a snippet and be used in Static Pages. To register a snippet, add the registerPageSnippets() method to your plugin class in the registration file.
from the Pages plugin docs
One difference - and again, this is a little unintuitive, is that you drag-and-drop CMS components onto a CMS page, but you simply "click" a snippet to add it to a pagespage. If you try to drag-and-drop a snippet onto a pagespage, you'll be left scratching your head:
So let's say that we want to add a snippet to the header area in our CMS layout. This is possible! Replace the static "Short Stories" text in your layout with an "html" type placeholder as follows:
<header>
{% placeholder header default title="Header" type="html" %}
My Favorite Short Stories
{% endplaceholder %}</pre>
</header>
Then, add an "Embedded Gallery" component (note: you'll have to install the November Gallery plugin for this!) to the new "Header" tab of your pagespage:
You can set all properties just as if you'd dropped it onto a CMS page!
What was the point of all of this? Well, now your user can easily select any gallery to display on the given page without having to mess around in the CMS area!
Custom Page a.k.a. "Syntax" Fields
Here is where stuff gets even more interesting. You can actually configure a form that your content editors can fill in with whatever structures information you need to generate the given page. You can add the following types of fields to the pagespage:
- Text - Single line input for smaller blocks of text.
- Textarea - Multiple line input for larger blocks of text.
- Dropdown - Renders a dropdown form field.
- Radio - Renders a radio form field.
- Variable - Renders the form field type exactly as defined in the type attribute. This tag will simply set a variable and will render in view mode as an empty string.
- Richeditor - Text input for rich content (WYSIWYG).
- Markdown - Text input for Markdown content.
- Media Finder - File selector for media library items. This tag value will contain the relative path to the file.
- File Upload - File uploader input for files. This tag value will contain the full path to the file.
Repeaters are not yet supported.
Let's take a look, continuing our above example. Let's say we wish to replace our static footer with the name of the author of the text. Replace the footer in your layout with:
<footer>
© {text name="author" label="Author"}Unknown{/text}
</footer>
And now if you go back to the pagespage, you see a new tab, with a new text input field:
Save it all, and you can see that the footer now shows the author:
But that's just a start. We can allow our editor to add a banner to each page:
{variable name="banner" label="Banner" tab="Header" type="mediafinder" mode="image"}{/variable}
And we can replace our whole "info" block with structured information, presenting our editor with a drop-down menu for the genre, and a date picker for the date of first publication!
So we update our CMS layout as follows:
description = "Demo for placeholders"
[staticPage]
useContent = 1
default = 1
==
{variable name="cover" label="Cover Image" tab="Structured Info" type="mediafinder" mode="image"}{/variable}
{variable name="author" label="Author" tab="Structured Info" type="text"}Unknown{/variable}
{variable name="storyTitle" label="Story Title" tab="Structured Info" type="text"}Unknown{/variable}
{variable name="originalCopyrightYear" label="Original copyright year" tab="Structured Info" type="text"}Unknown{/variable}
{variable name="Genre" label="Genre" tab="Structured Info" type="dropdown" options="Science Fiction|Fantasy|Horror"}Unknown{/variable}
{variable name="firstPublished" label="First Published" tab="Structured Info" type="datepicker" mode="date" format="Y-m"}Unknown{/variable}
<!DOCTYPE html>
<html>
<head>
{% styles %}
</head>
<body>
<header>
{% placeholder header default title="Header" type="html" %}
My Favorite Short Stories
{% endplaceholder %}</pre>
</header>
<main>
{% component 'staticPage' %}
</main>
<aside>
<div>
<dl>
<dt>Title:</dt><dd>{{ storyTitle }}</dd>
<dt>Author:</dt><dd>{{ author }}</dd>
{% if originalCopyrightYear %}<dt>Title:</dt><dd>{{ originalCopyrightYear }}</dd>{% endif %}
{% if genre %}<dt>Genre:</dt><dd>{{ genre }}</dd>{% endif %}
{% if firstPublished %}<dt>First published:</dt><dd>{{ firstPublished }}</dd>{% endif %}
</dl>
<i>{% placeholder info default title="Info" type="text" %}Let us know of any information you know about this short story!{% endplaceholder %}</i>
</div>
<img src="{{ cover|media }}" style="float: right; max-height: 200px; margin-left: 3em;" />
</aside>
<footer>
© {{ author }}
</footer>
{% scripts %}
</body>
</html>
And now our editor sees this on the "Pagespage":
We "re-use" our "Info" syntax field for a synapsis of the story:
And once published, it looks like:
And now that we have "structured" data about each short story, we could "easily" create a page that lists all of our short stories in a table that can be searched/filtered/ordered by title, author, date, etc...
You can see the final "website" at novembergallery.zenware.io/day-builders
Related posts: