Concrete CMS block types installed by our Dreamrs package are placed in the packages/dreamrs/blocks
directory. Let’s create that directory:
cd public/packages/dreamrs
mkdir blocks
cd blocks
mkdir page_header
We’re choosing page_header
as our block type handle, because it matches the name that we want for this block. Like other things in Concrete, a block type has a handle. This is a lowercase-only, non-spaced string that describes the block type in the filesystem. Examples include "content", "youtube", "page_title", etc...
Icon
Next, we need to give the block type an icon, with a PNG file named icon.png
. The dimensions for this file should be 50x50 pixels. You can choose any image here, but I’m going to actually generate my image from Font Awesome icons using the excellent FA2PNG Tool. Since all the Concrete core icons use a specific shade of light blue (#2aa9dd), I’m going to choose a different color for the custom block types in this package. Let’s use #ff3334, which is derived from the Dreamrs logo, and base it on the Header icon. Here’s my generated icon:
Name this file icon.png
, and place it within the page_header
directory.
Controller
Next, we need to create the controller for the block type. Block type’s have controllers, just as packages do. The controller for a block type contains all logic that runs behind the scenes whenever a block is added, edited, viewed or saved/updated. Controllers also pass data into the add, edit and view templates for a block type.
Create a file named controller.php
in the page_header
directory.
cd page_header
touch controller.php
Edit this file, and first give it the required namespace.
<?php
namespace Concrete\Package\Dreamrs\Block\PageHeader
Just like the namespace of the PageTheme
class we created earlier, the namespace of the block type class starts with the package namespace we used before, adds Block
and the PascalCase’d version of the block type’s handle.
Next, let’s add this line to halt execution of the page if the script is run directly.
defined('C5_EXECUTE') or die('Access Denied.');
Now let’s import the base block type class that our block type controller must extend.
use Concrete\Core\Block\BlockController;
We’ll be extending this class. Now we will create the first version of our controller:
class Controller extends BlockController
{
protected $btInterfaceWidth = 470;
protected $btInterfaceHeight = 300;
protected $btCacheBlockOutput = true;
protected $btCacheBlockOutputOnPost = true;
protected $btCacheBlockOutputForRegisteredUsers = true;
protected $btTable = 'btDreamrsPageHeader';
protected $btDefaultSet = 'basic';
public function getBlockTypeDescription()
{
return t('Shows a Header at the top of a page.');
}
public function getBlockTypeName()
{
return t('Page Header');
}
public function add()
{
}
public function edit()
{
}
public function view()
{
}
}
Let’s walk through this. Controller
is the required name for this file, just like for a package controller. $btInterfaceWidth
and $btInterfaceHeight
control the width and the height of the block type’s add and edit interface window. This is the window displayed when adding or updating a block type through the CMS.
Next we have some caching settings for our block type. $btCacheBlockOutput
, $btCacheBlockOutputOnPost
and $btCacheBlockOutputForRegisteredUsers
control whether to cache block output, and if so, whether the block type output can be safely cached on POST requests, or even for registered users. If you aren’t sure about whether your block output can be cached, you can omit these settings, but in order to get good performance out of our site, I’m going to turn these on for block types I know can be cached (including this one.)
Next we have our $btTable
property. $btTable
maps to the database table we’re going to be using to save this block type’s data. We’re going to create a database table named btDreamrsPageHeader
to save this block type’s custom fields. More on this soon.
Next, we have the $btDefaultSet
property. This property determines what block type set the block type should go into. If you omit this, the block type will be listed at the end of block types. I’m choosing the Basic
block type set, because this is a pretty common block type in our site. If you want a full list of block type sets, you can find them in the BlockTypeSets
database table.
After this, you you can see five methods. The name and description methods should be pretty self-explanatory: we’re returning the name and description of our block type for use in various Concrete editing interfaces. And finally we have view()
, add()
, and edit()
methods. These are run when a block is viewed, added or edited (respectively). We will fill out these methods soon.
view.php, add.php and edit.php
Now, we need to add our various templates to the block type folder. A block must have three templates -- view.php which is rendered when a block is viewed in a site, add.php which is rendered when its added, and edit.php for when it is edited. Let’s create these empty files:
touch add.php
touch edit.php
touch view.php
Let’s just add some placeholder text into these files os we can verify they’re loading properly. In view.php
and edit.php
, let’s add this text:
<?php
defined('C5_EXECUTE') or die('Access Denied.');
?>
Hello World.
We’re going to do something a little different in add.php
. In most cases, the add and edit templates of block types are the same -- it’s just that edit.php
also contains data loaded from the block instance. So let’s not duplicate the template, because then we’re going to have to keep it in sync across both add.php
and edit.php
as we add features. Instead, let’s include the edit.php
template within the add.php
template. Add this to add.php
:
<?php
defined('C5_EXECUTE') or die('Access Denied.');
$view->inc(‘edit.php’);
Our experience with the inc()
method in our theme helps us here.
db.xml
At this point, let’s think a little about what type of data our block is going to store. Every block type that has specific data set when added or updated (which is the vast, vast majority of them) needs to have a custom database table, with its fields set up as columns in the table. We already have a name for our database table -- btDreamrsPageHeader
(we set it as the $btTable
property in our class. So what columns are we going to place in this table?
We want to allow the use of an optional background image, an optional title, as well as a checkbox to control whether the current page’s title is used (vs. the custom title.)
Image
Override Page Name?
Custom Page Header Title
What are the data types for these fields? For image, you might think it would be a string (a path to an image file) but in Concrete, whenever you’re working with an uploaded file/image, or a link to a page, or a user object, you’re going to want to user an integer to represent the item, because the primary key for these objects is an integer. Override Page Name is pretty clearly a boolean true or false, and custom Page Title is a string. Given this, I believe our database table should have the following columns.
- fID - integer
- overridePageName -- boolean
- customPageHeaderTitle - string
With that decided, we’re going to create a file name db.xml that describes this database table, in the Doctrine XML format. It is based off of Doctrine DBAL, and allows for cross-platform descriptions of types and nomenclatures. Create a file named db.xml
in the block directory:
touch db.xml
and place the following inside it:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.concrete5.org/doctrine-xml/0.5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.concrete5.org/doctrine-xml/0.5 http://concrete5.github.io/doctrine-xml/doctrine-xml-0.5.xsd">
<table name="btDreamrsPageHeader">
<field name="bID" type="integer" size="10">
<unsigned/>
<key/>
</field>
<field name="fID" type="integer" size="10">
<unsigned/>
</field>
<field name="customPageHeaderTitle" type="string" size="255">
<notnull/>
</field>
<field name="overridePageName" type="boolean">
<default value="0"/>
<notnull/>
</field>
</table>
</schema>
fID
maps to file ID, which is an integer. bID
stands for block ID -- any block type database table should have an unsigned integer named bID
in order to automatically save data when blocks are added, updated or deleted. The other columns match their original column names pretty close.
When the block type is installed, the database table will automatically be created. If at any point you add fields to this database table in the XML, an update to the package will automatically modify the schema of the database table.
We now have everything we need in our folder, so let’s install our block type. (Note: curious about what else a block type might contain? Check out (Anatomy of a Block Type)[https://documentation.concretecms.org/developers/working-with-blocks/creating-a-new-block-type/getting-started/anatomy-of-a-block].