Extending eZ Studio with new blocks

The eZ Studio's Landing Page feature lets you compose powerful, dynamic pages from easily customizable blocks. You can find more about Landing Pages in our documentation: https://doc.ez.no/display/USER/Creating+content+-+Landing+Page.

While a number of useful, universal blocks are provided out-of-the-box, sometimes you might need a new Landing Page block that has not been implemented yet. If it is a block specific for your project, you have to build a definition for it with PHP.

In this tutorial I explain how to do just that. I assume here you have your own bundle that extends eZ Studio. If not, please generate a new one with the following command:

php app/console generate:bundle (the command should run from the eZ Platform root folder).

If you need more information about bundles please check the documentation page about bundles: https://doc.ez.no/display/DEVELOPER/Bundles.

All the changes described below that are required to create a new block will be done inside your bundle folder.

Block definition

Without specifying the block definition, eZ Studio won't be able to handle dynamic block configuration that will come after saving information in the block config form. The block definition requires at least two methods to be implemented: getTemplateParameters and createBlockDefinition.

The first method is responsible for passing data from the block definition (which acts like a view) to a template.

The second method creates the block definition. There you will define your block structure.

If you want block validation, you will need to implement a third method called checkAttributesStructure, where you can validate the required block fields.

The explanation above might look a bit enigmatic without seeing the code, so let's take a look at an example:

<?php
namespace My\BlockBundle\Block;
use EzSystems\LandingPageFieldTypeBundle\Exception\InvalidBlockAttributeException;
use EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Definition\BlockDefinition;
use EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Definition\BlockAttributeDefinition;
use EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Model\AbstractBlockType;
use EzSystems\LandingPageFieldTypeBundle\FieldType\LandingPage\Model\BlockValue;
class CustomBlock extends AbstractBlockType
{
    /*
     * With this method you can return values that will be used in the template as variables.
     */
    public function getTemplateParameters(BlockValue $blockValue)
    {        
        return [
            'block' => json_encode($blockValue->getAttributes())
        ];
    }
    
    /*
     * Creates block definition with fields
     */
    public function createBlockDefinition()
    {
        return new BlockDefinition(
            'custom', // Block type (unique)
            'Custom Block', // Name of block
            'default', // Block category (currently unsupported)
            'bundles/ezsystemslandingpagefieldtype/images/thumbnails/tag.svg', // block thumbnail; will be used in the eZ Studio blocks sidebar
            [], // extra views - the hardcoded template
            [
                new BlockAttributeDefinition(
                    'content', // Attribute's ID (unique)
                    'Content', // Attribute' name
                    'text', // Attribute's type
                    '/[^\\s]/', // regex for frontend validation
                    'The content value should be a text', // regex validation fail message
                    true, // is field required?
                    false, // should this attribute input be displayed inline to the previous?
                    [], // default value
                    [] // available options (only for select and multiple field types)
                ),
                
                new BlockAttributeDefinition(
                    'contentStyle',
                    'Content Style',
                    'select',
                    '',
                    'Please, select a style',
                    false,
                    false,
                    ['default'],
                    [
                        'default'   => 'Default style',
                        'flat'      => 'Flat style',
                        'rounded'   => 'Rounded style'
                    ]
                ),
            ]
        );
    }
    
    /*
     * Validates user input from the block configuration form
     */
    public function checkAttributesStructure(array $attributes)
    {
        if (!isset($attributes['content'])) {
            throw new InvalidBlockAttributeException(
                $this->getBlockDefinition()->getName(),
                'content',
                'Content must be set.'
            );
        }
    }
}
CustomBlock.php

The code above should be placed in the following location: Block/CustomBlock.php.

custom-block-on-the-block-lis

As you probably noticed, defining a new block's structure is pretty straightforward. You create a new BlockDefinition instance containing some BlockAttributeDefinition instances. There are many types of block attributes:

  • integer
  • string
  • url
  • text
  • embed
  • select
  • multiple

If you create a block attribute with types: integer, stringurl, the user will see an input field on the frontend side. In case of defining a block attribute definition with embed type, the user will see a button leading to settings (widget).

embed-block-with-settings

When the button is clicked, the Universal Discovery Widget will appear and there the user can choose a Content item that should be added to the block as its content.

universal-discovery-widget

The text block attribute will provide a textarea field where users will be able to input more text. When choosing either a select type or a multiple type, the user will see a select/dropdown field and will be able to choose one of predefined values. See the code example above to see how to define select field options.

Another parameter worth looking at is the extra views param. This allows you to hardcode a template in the PHP code instead of providing a template config. However, please keep in mind that this is not the recommended way and we advise you to use the presented template configuration.

Block template

The Block template is needed to display block content on a webpage. It is a template for content selected from the eZ Studio block configuration popup. This makes the block template a mandatory requirement in order to show content on your site.

block-config-popup

For simplicity's sake, we'll define a simple layout with a static header containing a block name and a dynamic content that will come from the block's configuration popup fields.

The sample template might look like this:

<h3>My Custom block</h3>
{{ block }}
custom.html.twig
rendered-block

The template code will be stored in the Resources/views/ folder in a file named custom.html.twig.

Block extension

A block definition and a block template are not enough to have the eZ Studio app already extended. You have to do two more things. One of them is defining a block extension class that will provide block configuration to the eZ Studio app.

<?php
namespace My\BlockBundle\DependencyInjection;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\Yaml\Yaml;
class CustomBlockExtension extends Extension implements PrependExtensionInterface
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);
        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');
    }
    public function prepend(ContainerBuilder $container)
    {
        $configFile = __DIR__ . '/../Resources/config/blocks.yml';
        $config = Yaml::parse(file_get_contents($configFile));
        $container->prependExtensionConfig('ez_systems_landing_page_field_type', $config);
        $container->addResource(new FileResource($configFile));
    }
}
CustomBlockExtension.php

The code above should be placed in the following location: DependencyInjection/MyBlockExtension.php.

In the extension class you have to define at least the load method. It is used to provide information about your custom block definition class. If you have block templates defined with the config, then you can attach it to the eZ Studio app by creating a prepend method. It loads your block templates config (in the example above the block template config is stored in the file blocks.yml). The config is prepended to the ez_systems_landing_page_field_type config.

Block configuration files

The only thing left to do is to create block configuration files, one with services config and another with templates config.

Block services config

The file is located under Resources/config/services.yml.

The basic config file looks like this:

services:
    my.block.custom:
        class: My\BlockBundle\Block\CustomBlock
        tags:
            - { name: landing_page_field_type.block_type, alias: custom }
services.yml

Block templates config

The file is located under Resources/config/blocks.yml:

blocks:
    custom:
        views:
            custom:
                template: MyBlockBundle::custom.html.twig
                name: My Custom Block view
blocks.yml

Each block can have multiple templates. Each template should be defined under views root. In the example above, there is only one block view template defined called custom.

Summary

And that's all there is to it. I hope that you will find this post useful and that it will help you build new, amazing blocks for your Landing Pages.

Using the code from this article you will be able to create Landing Page blocks that use default UI for blocks in eZ Studio. The creation of custom user interfaces by extending JS views in eZ Studio is not possible yet.

As a bonus, if you want to have base files for the Landing Page block bundle, then you might want to fork this repository from github created by one of eZ Developers: https://github.com/kamilmusial/EzLandingPageBlockTemplateBundle.

Leave us a comment
blog comments powered by Disqus