Boards & Summary Templates

Improvements?

Let us know by posting here.

Boards are a way to aggregate content on your site.

Boards offer a dynamic way to group chronological content on the front-end of your site. Think of boards like page lists, express lists or calendar event lists – but with the ability to switch between different content form factors, to aggregate different types of content together in the same list, and more.

Before moving into Boards and how to develop custom Summary templates and manage these templates in Concrete, we will strongly encourage you to walk through with our Boards Editor’s documentation if you haven’t gone through with it as yet.

Using Summary Templates to Display Content

Summary templates let you choose how content is displayed. They disconnect content from specific Block types, allowing various blocks to use the same template. For instance, a single summary template can be utilized by both a page list and other blocks.

In the Atomik theme, installing the theme also sets up summary templates. The XML below showcases how to define these when adding your theme:

<summarytemplates>
    <template handle="blog_image_left" name="Blog Image Left" icon="blank.png" package="">
        <categories>
            <category handle="page"/>
        </categories>
        <fields>
            <field required="1">title</field>
            <field required="1">link</field>
            <field required="1">thumbnail</field>
            <field required="1">date</field>
        </fields>
    </template>
</summarytemplates>

After this XML is processed, a new summary template named "Blog Image Left" becomes available for pages that have a title, link, thumbnail, and date.

Categories in Summary

Every summary template can link to multiple categories. The default categories in Concrete version 9 are:

  1. Page
  2. Calendar Event

They determine which type of content the template relates to.

Summary Fields

Concrete includes built-in fields, defined during installation. These are in:

/path/to/install_directory/concrete/config/install/base/summary.xml

The sample fields:

<summaryfields>
    <field handle="title" name="Title" package=""/>
    <field handle="date" name="Date" package=""/>
    <field handle="date_start" name="Start Date" package=""/>
    <field handle="date_end" name="End Date" package=""/>
    <field handle="link" name="Link" package=""/>
    <field handle="description" name="Description" package=""/>
    <field handle="thumbnail" name="Thumbnail" package=""/>
    <field handle="categories" name="Categories" package=""/>
    <field handle="author" name="Author" package=""/>
</summaryfields>

These

When setting up your summary template, you can specify which fields are essential.

Template Application

After a template is defined in the system, it can be used across all linked categories, like calendar events or pages. This flexibility means a template might show content from both a calendar event and a page.

Here's a blog_entry_card template sample from Atomik Theme:

<div class="ccm-summary-template-blog-entry-thumbnail">
    <a href="<?=$link?>" class="card">
        <div class="position-relative">
            <div class="card-img-top ccm-summary-template-blog-entry-thumbnail-image-overlay"></div>
            <img class="card-img-top" src="<?=$thumbnail->getThumbnailURL('blog_entry_thumbnail')?>">
        </div>
        <div class="card-body">
            <h5 class="card-title"><?=$title?></h5>
            <?php if ($date) { ?>
                <p class="card-text text-center"><small class="text-muted"><?=date('F d, Y', (string) $date)?></small></p>
            <?php } ?>
        </div>
    </a>
</div>

Note: For any existing content when a new summary template is added, run the reindex content task to update the database with the new templates.

Board Templates

When making a board, you pick a template for it. Board templates, like the summary ones, are added via XML. Here's an example using the Atomik theme:

<boardtemplates>
    <template handle="blog" name="Blog"
              icon="blank.png" package="">
    </template>
</boardtemplates>

After adding the Atomik content, a "Blog" template is ready for use.

Driver for the Board

Board templates differ from summary templates because they require a driver class. The driver class tells the system about the board's slots and how content fills them. The Atomik theme has its driver setup, but if you're making your own, you might use code like this:

use Concrete\Core\Board\Template\Driver\Manager as BoardTemplateManager;
use Foo\Bar\Board\Template\Driver\FooBarDriver;

...

$boardTemplateManager = $this->app->make(BoardTemplateManager::class);
$boardTemplateManager->extend('foo_bar', function() {
    return $this->app->make(FooBarDriver::class);
});

This makes sure a template with the handle foo_bar uses FooBarDriver as its driver.

Template File Details

Let's look at a board template from the Atomik theme:

<?php defined('C5_EXECUTE') or die("Access Denied.");?>
<div class="container ccm-board-blog">
    <div class="row pb-4 mb-4">
        <div class="col-md-12 blog-featured-post">
            <?php
            $slot->display(1);
            ?>
            <hr class="d-md-none d-block mb-0">
        </div>
    </div>
    <div class="row gx-8">
        <div class="col-md-8">
            <?php
            if ($slot->hasContents(2)) {
            ?>
            <div class="row">
                <div class="col-md-12">
                    <?php
                    $slot->display(2);
                    ?>
                </div>
            </div>
            <?php } ?>
            <?php
            if ($slot->hasContents(3)) {
            ?>
            <div class="row">
                <div class="col-md-12">
                    <hr class="d-none d-md-block">
                    <?php
                    $slot->display(3);
                    ?>
                </div>
            </div>
            <?php } ?>
            <?php
            if ($slot->hasContents(4)) {
            ?>
            <div class="row">
                <div class="col-md-12">
                    <hr class="d-none d-md-block">
                    <?php
                    $slot->display(4);
                    ?>
                </div>
            </div>
            <?php } ?>
            <?php
            if ($slot->hasContents(5)) {
            ?>
            <div class="row">
                <div class="col-md-12">
                    <hr class="d-none d-md-block">
                    <?php
                    $slot->display(5);
                    ?>
                </div>
            </div>
            <?php } ?>
        </div>
        <div class="col-md-4 col-blog-sidebar mt-3 mt-md-0">
            <?php
            $stack = Stack::getByName('Blog Sidebar');
            if ($stack) {
                $stack->display();
            }
            ?>
        </div>
    </div>
</div>

This board template is standard HTML & PHP with styling from Bootstrap 5. The only unique part for boards is the $slot object, which interacts with the content. The board driver and this file work in tandem to show your board's content.

Simple Board Slot Templates

For every board, there are spaces called slots, marked like $slot->display(1). These slots hold content that follows a Slot Template.

Board slot templates are added with XML. Here's an example from the Atomik theme:

<boardslottemplates>
    <template handle="blog_image_left" name="Blog Image Left" icon="blank.png">
    </template>
    ...
</boardslottemplates>

Slot Template Drivers

Just like board templates, slot templates need a driver class. The Atomik theme sets up the "Blog Image Left" slot template with its driver. But if you're making your own, you might do:

use Concrete\Core\Board\Template\Slot\Driver\Manager as BoardSlotTemplateManager;

...

$boardSlotTemplateManager = $this->app->make(BoardSlotTemplateManager::class);
$boardSlotTemplateManager->extend('foo_bar_slot', function() {
    return $this->app->make(FooBarSlotDriver::class);
});

This makes sure the "Foo Bar Slot" slot template uses FooBarSlotDriver.

What's the job of these slot drivers? They tell the system about the board's content spaces and decide how content fits into them.

Slot Template File Examples

Look at these sample slot template files:

Blog Image Left

Location: concrete/themes/atomik/elements/boards/slots/blog_image_left.php

$slot->display(1);

Not much, right? Sometimes slot templates just show a single content piece.

Blog Two Up

<div class="row">
    <div class="col-md-6">
        <?php $slot->display(1); ?>
    </div>
    <div class="col-md-6">
        <?php $slot->display(2); ?>
    </div>
</div>

This one has two slots, shown as columns.

Blog Three Up

<div class="row">
    <div class="col-md-4"><?php $slot->display(1); ?></div>
    ...
</div>

This one has three slots.

To sum up: Boards use templates to arrange content. Sometimes, you might see content lined up one after the other. Other times, they might be side by side in columns.Boards are meant to be dynamic and flexible – but are guaranteed to always look great.

Blog Template Driver

How do boards consistently maintain such a polished appearance? It's all thanks to driver objects, which dictate the layout of board content. Examine the configuration of Atomik's Blog board:

The Concrete\Core\Board\Template\Driver\BlogDriver class informs Concrete that it itself contains 5 slots:

public function getTotalSlots(): int
{
    return 5;
}

Additionally, it controls the display of slot number 1 in a very particular way, by implementing the getLayoutPlanner method. This is an optional method available to the blog driver class.

public function getLayoutPlanner(): ?PlannerInterface
{
    return new SlotLayoutPlanner([
        '1' => ['blog_image_left']
    ]);
}

The SlotLayoutPlanner class is a simple class meant to tie a particular slot in a board template to a particular board slot template. What does this mean? It means that no matter how many times you regenerate the Atomik blog board, you'll aways get a left-image content element in the first slot, because the only slot template that layout planner will allow to be placed within the slot 1 is the blog_image_left slot template.

So let's look at the blog_image_left slot template driver, found at Concrete\Core\Board\Template\Slot\Driver\BlogImageLeftDriver. It informs the system that it contains one content slot:

public function getTotalContentSlots(): int
{
    return 1;
}

And it also informs the system that within its single content slow, only the content objects with the blog_image_left summary templates may be placed. This is through the use of the slot filterer object:

public function getSlotFilterer(): ?FiltererInterface
{
    $filterer = new SummaryObjectFilterer();
    $filterer->registerSlot(1, [
        'blog_image_left',
    ]);
    return $filterer;
}

This is similar to the board planner object, but it works with summary templates.

Let's contrast this with the Concrete\Core\Boared|Template\Slot\Driver\BlogThreeUpDriver class. When the slot template blog_three_up is placed within a board template slot, **three* content objects will be placed within its content slots:

public function getTotalContentSlots(): int
{
    return 3;
}

Additionally, not just any summary templates may be used for these content objects – only the blog_entry_card summary template will be used for the items in these content slots, because that's what the slot filterer decrees:

public function getSlotFilterer(): ?FiltererInterface
{
    $filterer = new SummaryObjectFilterer();
    $filterer->registerSlot(1, [
        'blog_entry_card',
    ]);
    $filterer->registerSlot(2, [
        'blog_entry_card',
    ]);
    $filterer->registerSlot(3, [
        'blog_entry_card',
    ]);
    return $filterer;
}

When you get down to the details of the blog_entry_card template, it's relatively straightforward. From concrete/themes/atomik/elements/summary/templates/blog_entry_card.php:

<?php defined('C5_EXECUTE') or die("Access Denied."); ?>
<div class="ccm-summary-template-blog-entry-thumbnail">
    <a href="<?=$link?>" class="card">
        <div class="position-relative">
            <div class="card-img-top ccm-summary-template-blog-entry-thumbnail-image-overlay"></div>
            <img class="card-img-top" src="<?=$thumbnail->getThumbnailURL('blog_entry_thumbnail')?>">
        </div>
        <div class="card-body">
            <h5 class="card-title"><?=$title?></h5>
            <?php if ($date) { ?>
                <p class="card-text text-center"><small class="text-muted"><?=date('F d, Y', (string) $date)?></small></p>
            <?php } ?>
        </div>
    </a>
</div>