Adding Documentation to your Theme

Another new feature in Concrete CMS 9 that we're pleased with is "Theme Documentation". Theme documentation is an interactive guide to how your theme looks, how to put aspects of it together, and a place to test blocks within it.

Overview

Theme documentation can be accessed from Dashboard > Pages and Themes > Themes:

#

It has a table of contents:

#

And it can be navigated in a number of ways:

#

Example of Documentation: Atomik

The quickest way to understand how Concrete themes can provide documentation is to look at a working example. Fortunately, we have one – the Atomik theme that ships with Concrete 9.

In Concrete\Theme\Atomik\PageTheme we've implemented the getDocumentationProvider method. Implementing this method tells Concrete that the theme has documentation.

This theme has to return an object of the type Concrete\Core\Page\Theme\Documentation\DocumentationProviderInterface. This interface has a few required methods:

DocumentationProviderInterface

installSupportingElements()

This method is run when the documentation is installed.

clearSupportingElements()

This is the method that's run when the documentation is uninstalled.

finishInstallation()

This method is run when the documentation is finished installing.

getPages()

This method returns an array of Concrete\Core\Page\Theme\Documentation\DocumentationPageInterface pages.

Atomik Implementation

The Atomik implementation of the DocumentationProviderInterface can be found at Concrete\Core\Page\Theme\Documentation\AtomikDocumentationProvider. This class is what's provided in the Concrete\Theme\Atomik\PageTheme class:

public function getDocumentationProvider(): ?DocumentationProviderInterface
{
    return new AtomikDocumentationProvider($this);
}

Here's what the Atomik documentation provider does:

installSupportingElements()

This method is responsible for doing the following:

  • Disabling Cache
  • Creating a new file manager folder for documentation assets
  • Create new support objects for Form demonstration purposes
  • Installs Atomik demonstration containers
  • Imports demonstration files
  • Creates demonstration Express objects
  • Creates a demonstration calendar

(Note: this doesn't happen automatically. Check the Atomik documentation provider to see examples of how it actually imports files, creates Express objects and more.)

clearSupportingElements()

Here's a method that takes care of uninstalling any support elements when the documentation is uninstalled. Uninstalling documentation is actually a very simple things to do through the UI, because we realized that this is the kind of operation that theme developers are likely to be doing a lot, as they finalize content around how their theme works and what types of functionality they'd like to demonstrate in their documentation.

In the Atomik documentation provider, this method does the following:

  • Deletes any custom Express objects and their data
  • Deletes the Atomik Documentation file manager folder
  • Deletes any files added by the documentation sample content
  • Deletes the Atomik sample calendar

finishInstallation()

Anything that has to happen after all supporting elements are created and pages are created should happen here. This includes

  • Updating some blocks to point to newly created Express objects
  • Updating calendar blocks to point to newly created Calendar objects

getPages()

Finally, we get to the most important method of the documentation provider class – the getPages() method. This method returns an array of DocumentationPageInterface classes, which are basically just wrapper objects that specify the name of a documentation page to create, and where to get their content XML. Content Import/Interchange Format is used throughout Concrete as to describe the content and structure of entire Concrete sites. This file format is simply XML, with nodes that Concrete uses to install bits of functionality. This XML format is used extensively during installation.

Example

The best way to see this is with an example. Here's the getPages() method in the Atomik documentation provider:

/**
 * @return DocumentationPageInterface[]
 */
public function getPages(): array
{
    $pages = [
        new ThemeDocumentationPage($this->theme, 'Overview', 'overview.xml'),
        // Snipped for this example
        new BedrockDocumentationPage('Typography', 'typography.xml'),
    ];

    return $pages;
}

Here, we're using the ThemeDocumentationPage class, which let's us know that the "Overview" page in our documentation is actually defined within our theme. Next, we're using the BedrockDocumentationPage class, which tells Concrete that the Typography page contents are actually coming from the core. This is useful because it means that theme developers don't have to actually write every single documentation page in their theme - some of the elements that are shared ship with the Core.

Let's look at the Overview page content. The method points to an overview.xml file. Since this is being specified using the ThemeDocumentationPage class, it means that we have to look into the Atomik theme for this content. It will be present at /concrete/themes/atomik/documentation/overview.xml.

<concrete5-cif version="1.0">
    <area name="Main">
        <blocks>
            <block type="content" name="" >
                <data table="btContentLocal">
                    <record>
                        <content><![CDATA[
                        <div class="mt-5">
                            <h1>Overview</h1>
                            <p>Welcome to the Atomik theme.</p>
                        </div>]]></content>
                    </record>
                </data>
            </block>
            <block type="core_theme_documentation_toc" name="">
                <style>
                    <customClass>mb-5</customClass>
                </style>
            </block>
        </blocks>
    </area>
</concrete5-cif>

Unlike most examples of Content XML, we don't have a <page> node here. That's because the ThemeDocumentationPage already informs Concrete that we're making a page. All we have to do is provided area and block content here.

Add Documentation to Our Theme

With all that out of the way, here's a simple example of theme-specific documentation. I'll walk through every portion of it.

PageTheme Modifications

Let's use a PHP 7 anonymous class to deliver our documentation. This isn't how I'd advocate for doing it in production, but I don't want to get bogged down working with class autoloading, etc... So in this case an anonymous class will help us get going quickly.

public function getDocumentationProvider(): ?DocumentationProviderInterface
{
    $theme = $this;
    return new class($theme) implements DocumentationProviderInterface {

        protected $theme;

        public function __construct($theme)
        {
            $this->theme = $theme;
        }

        public function clearSupportingElements(): void
        {
            $importer = new ContentImporter();
            $documentation = FileFolder::getNodeByName('Flintstone Documentation');
            if ($documentation) {
                $documentation->delete();
            }
            $importer->deleteFilesByName(['fred.jpg', 'wilma.jpg', 'dino.gif']);
        }

        public function installSupportingElements(): void
        {
            $importer = new ContentImporter();
            $filesystem = new Filesystem();
            $root = $filesystem->getRootFolder();
            $filesystem->addFolder($root, 'Flintstone Documentation');

            $importer->importFiles(
                $this->theme->getThemeDirectory() .
                DIRECTORY_SEPARATOR .
                DIRNAME_THEME_DOCUMENTATION .
                DIRECTORY_SEPARATOR .
                'files'
            );

            $importer->moveFilesByName(['fred.jpg', 'wilma.jpg', 'dino.gif'], 'Flintstone Documentation');

        }

        public function finishInstallation(): void
        {
            // Nothing here.
        }

        public function getPages(): array
        {
            return [
                new ThemeDocumentationPage($this->theme, 'Overview', 'overview.xml'),
                new BedrockDocumentationPage('Typography', 'typography.xml'),
                new BedrockDocumentationPage('Components', 'components.xml'),
                new ThemeDocumentationPage($this->theme, 'Owl Carousel', 'owl_carousel.xml'),
            ];
        }

    };
}

Next, we create a documentation/ folder within the theme directory, and a files/ directory within it. We place our fred.jpg, wilma.jpg and dino.gif demonstration files within the files/ directory, and create two new XML files, overview.xml and owl_carousel.xml, within the documentation/ directory. It now looks like this:

#

And those files look like this:

overview.xml

<concrete5-cif version="1.0">
    <area name="Main">
        <blocks>
            <block type="content" name="" >
                <data table="btContentLocal">
                    <record>
                        <content><![CDATA[
                        <div class="mt-5">
                            <h1>Overview</h1>
                            <p>Welcome to the Flintstone theme! Yabba dabba doo!</p>
                        </div>]]></content>
                    </record>
                </data>
            </block>
            <block type="core_theme_documentation_toc" name="">
                <style>
                    <customClass>mb-5</customClass>
                </style>
            </block>
        </blocks>
    </area>
</concrete5-cif>

owl_carousel.xml

<concrete5-cif version="1.0">
<area name="Main">
    <blocks>
        <block type="core_theme_documentation_breadcrumb" name="">
            <style>
                <backgroundColor/>
                <backgroundRepeat/>
                <backgroundSize/>
                <backgroundPosition/>
                <borderWidth/>
                <borderColor/>
                <borderStyle/>
                <borderRadius/>
                <baseFontSize/>
                <alignment/>
                <textColor/>
                <linkColor/>
                <paddingTop/>
                <paddingBottom/>
                <paddingLeft/>
                <paddingRight/>
                <marginTop/>
                <marginBottom/>
                <marginLeft/>
                <marginRight/>
                <rotate/>
                <boxShadowHorizontal/>
                <boxShadowVertical/>
                <boxShadowBlur/>
                <boxShadowSpread/>
                <boxShadowColor/>
                <customClass>mt-5</customClass>
                <customID/>
                <customElementAttribute/>
                <hideOnExtraSmallDevice/>
                <hideOnSmallDevice/>
                <hideOnMediumDevice/>
                <hideOnLargeDevice/>
            </style>
        </block>
        <block type="content" name="">
            <data table="btContentLocal">
                <record>
                    <content><![CDATA[<h1>Owl Carousel</h1>

Our flintstone theme makes use of Owl Carousel for its Image Slider. Check its documentation for customization options.

]]> <![CDATA[2]]> <![CDATA[4000]]> <![CDATA[500]]> <![CDATA[0]]> <![CDATA[0]]> <![CDATA[0]]> <![CDATA[0]]> {ccm:export:file:fred.jpg} <![CDATA[]]> <![CDATA[0]]> <![CDATA[0]]> <![CDATA[0]]> {ccm:export:file:wilma.jpg} <![CDATA[]]> <![CDATA[0]]> <![CDATA[1]]> <![CDATA[0]]> {ccm:export:file:dino.gif} <![CDATA[]]> <![CDATA[0]]> <![CDATA[2]]>

Finally, when we install the documentation we should have a useful mix of our own custom pages and those provided by Bedrock:

#

#

Work In Progress

Theme documentation is a work in progress; as a feature it works and is helpful, but there's still a lot of code for developers to write themselves (or copy from Atomik) if they want to demonstrate all of what Concrete can do within their theme. We're actively exploring ways of making this easier for developers to support, and would be interested in documentation feedback in the forums.

Download the Source

The source for this, and all other Bedrock theme work described in this section, can be found on GitHub in https://github.com/concrete5/theme_bedrock_documentation.