Netgen Layouts Documentation¶
Netgen Layouts is an easy and flexible way of managing all page layouts on your website. Use it to manage and maintain the website layout structure in a slick and agile but at the same time extensible way.
Note
This documentation assumes you have a working knowledge of the Symfony Framework. If you’re not familiar with Symfony, please start with reading the Quick Tour from the Symfony documentation.
Note
Netgen Layouts does not force or otherwise define where your configuration, code, or templates live. Examples in this documentation will use YAML as a configuration format, but you can ofcourse use any format available in Symfony and you can place templates in folder structure that fits your needs best.
Reference¶
Reference¶
Install instructions¶
To install Netgen Layouts, you need to have an existing Symfony full stack installation. For example, Netgen Layouts can be installed on eZ Platform, Sylius or Symfony Demo app.
Use Composer¶
Add the following to your composer.json
. During installation, you will be
asked for username and password to packagist.netgen.biz
, make sure you have
them ready:
{
"repositories": [
{ "type": "composer", "url": "https://packagist.netgen.biz" }
]
}
Execute the following from your installation root:
$ composer require netgen/block-manager
Note
If you’re installing Netgen Layouts on eZ Platform, execute the following instead:
$ composer require netgen/block-manager-ezpublish netgen/platform-ui-layouts-bundle
Note
If you use Netgen Admin UI, you can also install netgen/admin-ui-layouts-bundle package and activate NetgenBundleAdminUILayoutsBundleNetgenAdminUILayoutsBundle bundle in your AppKernel to enable integration of Netgen Layouts into Netgen Admin UI.
Activate the bundles¶
Add the following bundles to your kernel:
new Knp\Bundle\MenuBundle\KnpMenuBundle(),
new FOS\HttpCacheBundle\FOSHttpCacheBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
new Netgen\Bundle\CoreUIBundle\NetgenCoreUIBundle(),
new Netgen\Bundle\ContentBrowserBundle\NetgenContentBrowserBundle(),
new Netgen\Bundle\ContentBrowserUIBundle\NetgenContentBrowserUIBundle(),
new Netgen\Bundle\BlockManagerBundle\NetgenBlockManagerBundle(),
new Netgen\Bundle\BlockManagerUIBundle\NetgenBlockManagerUIBundle(),
new Netgen\Bundle\BlockManagerAdminBundle\NetgenBlockManagerAdminBundle(),
Note
If you’re installing Netgen Layouts on eZ Platform, activate the following bundles too:
new Netgen\Bundle\EzPublishBlockManagerBundle\NetgenEzPublishBlockManagerBundle(),
new Netgen\Bundle\PlatformUILayoutsBundle\NetgenPlatformUILayoutsBundle(),
Add the following bundle to your kernel only for dev environment:
new Netgen\Bundle\BlockManagerDebugBundle\NetgenBlockManagerDebugBundle(),
Import database tables¶
Execute the following from your installation root to import Netgen Layouts database tables:
$ php app/console doctrine:migrations:migrate --configuration=vendor/netgen/block-manager/migrations/doctrine.yml
Routing and assets¶
Add the following routes to your main routing config file:
netgen_block_manager:
resource: "@NetgenBlockManagerBundle/Resources/config/routing.yml"
prefix: "%netgen_block_manager.route_prefix%"
netgen_content_browser:
resource: "@NetgenContentBrowserBundle/Resources/config/routing.yml"
prefix: "%netgen_content_browser.route_prefix%"
Run the following from your installation root to symlink assets:
$ php app/console assets:install --symlink --relative
Note
If you’re installing Netgen Layouts on eZ Platform, you also need to dump Assetic assets:
$ php app/console assetic:dump
Adjusting your full views¶
All of your full views need to extend ngbm.layoutTemplate
variable (see
below for example). If layout was resolved, this variable will hold the name of
the template belonging to the resolved layout. In case when layout was not
resolved, it will hold the name of your main pagelayout template (the one your
full views previously extended). This makes it possible for your full view
templates to be fully generic, that is, not depend whether there is a resolved
layout or not:
{% extends ngbm.layoutTemplate %}
{% block content %}
{# My full view code #}
{% endblock %}
Adjusting your base pagelayout template¶
To actually display the resolved layout template in your page, you need to
modify your main pagelayout template to include a Twig block named layout which
wraps everything between your opening and closing <body>
tag:
<body>
{% block layout %}
{# Other Twig code #}
{% block content %}{% endblock %}
{# Other Twig code #}
{% endblock %}
</body>
There are two goals to achieve with the above Twig block:
- If no layout could be resolved for current page, your full view templates will just keep on working as before
- If layout is resolved, it will use the
layout
block, in which casecontent
Twig block and other Twig code will not be used. You will of course need to make sure that in this case, all your layouts have a full view block in one of the zones which will display yourcontent
Twig block from full view templates
Configuring the pagelayout¶
As written before, Netgen Layouts replaces the pagelayout in your full views
with its dynamic variable called ngbm.layoutTemplate
. It basically injects
itself between rendering of your full view and your pagelayout. Since your full
views do not extend from your main pagelayout any more, Netgen Layouts needs to
know what was your original full view to fallback to it. You can configure your
pagelayout in Netgen Layouts config like this:
netgen_block_manager:
pagelayout: '@App/pagelayout.html.twig'
Note
If you’re installing Netgen Layouts on eZ Platform, your main pagelayout is taken from existing eZ Platform configuration, so you can skip this step.
Update Varnish VCL configuration¶
To enable caching and later cache clearing of block and layout HTTP caches, you
will need to use Varnish. To make the cache clearing work, you need to modify
your Varnish VCL and add the following rules somewhere in your vcl_recv
function.
Note
If you’re using eZ Platform and the VCL supplied by it, the best place
to put this is in ez_purge
function (which is called from vcl_recv
),
right after if (req.http.X-Location-Id) { ... }
block.
For Varnish 3:
if (req.http.X-Layout-Id) {
ban( "obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
}
error 200 "Banned";
}
if (req.http.X-Block-Id) {
ban( "obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
}
error 200 "Banned";
}
For Varnish 4 and later:
if (req.http.X-Layout-Id) {
ban("obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
}
return (synth(200, "Banned"));
}
if (req.http.X-Block-Id) {
ban("obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
}
return (synth(200, "Banned"));
}
Configuration reference¶
Layout type configuration¶
The following lists all available layout type configuration options:
netgen_block_manager:
layout_types:
# Layout type identifier
my_layout:
# The switch to enable or disable showing of the layout type in the interface
enabled: true
# Layout type name, required
name: 'My layout'
# A collection of zones available in the layout type, required
zones:
# Zone identifier
left:
# Zone name, required
name: 'Left'
# List of block definitions which are allowed in the zone
allowed_block_definitions: []
right:
name: 'Right'
allowed_block_definitions: [title]
Block definition configuration¶
The following lists all available block definition configuration options:
netgen_block_manager:
block_definitions:
# Block definition identifier
my_block:
# The switch to enable or disable showing of all block types
# related to block definition in the interface
enabled: true
# Identifier of a handler which the block definition will use.
# The value used here needs to be the same as the identifier
# specified in handler tag in Symfony DIC.
# If undefined, the handler with the same identifier as the
# block definition itself will be used.
handler: ~
# Block definition name, required
name: 'My block'
# The list of collections the block has. Only one collection named
# "default" is supported for now. Omit the config to disable the collection.
collections:
default:
# The list of valid items for the collection. Use null to
# allow all items, an empty array to disable adding manual
# items, and a list of items to limit the collection to
# to only those items.
valid_item_types: null
# The list of valid query types for the collection. Use null to
# allow all query types, an empty array to disable dynamic collections,
# and a list of query types to limit the collection to
# only those query types.
valid_query_types: null
# This controls which forms will be available to the block.
# You can either enable the full form, or content and design forms.
# If full form is enabled, all block options in the right sidebar
# in the Block Manager app will be shown at once, otherwise,
# Content and Design tabs will be created in the right sidebar
forms:
full:
enabled: true
type: Netgen\BlockManager\Block\Form\FullEditType
design:
enabled: false
type: Netgen\BlockManager\Block\Form\DesignEditType
content:
enabled: false
type: Netgen\BlockManager\Block\Form\ContentEditType
# The list of all view types in a block definition, required
view_types:
# View type identifier
my_view_type:
# Switch to control if the view type is shown in the interface or not
enabled: true
# View type name, required
name: 'My view type'
# The list of allowed item view types for this block view type
item_view_types:
# Item view type identifier
my_item_view_type:
# Switch to control if the item view type is shown in the interface or not
enabled: true
# Item view type name, required
name: 'My item view type'
# Use this configuration to control which block parameters will be displayed
# when editing a block in specified view type. Use null to display all
# parameters, an empty array to hide all parameters and a list of parameter
# names to list specific parameters to show. You can also prefix the parameter
# with exclamation mark to exclude it.
valid_parameters: null
Block type and block type group configuration¶
The following lists all available block type and block type group configuration options:
netgen_block_manager:
block_types:
# Block type identifier
my_block_type:
# The switch to enable or disable showing the block type in the interface
enabled: true
# Block type name, if undefined, will use the name of a block definition
# with the same identifier as the block type itself.
name: ~
# Block definition identifier of the block type, if undefined, will use the
# block definition with the same identifier as the block type itself.
definition_identifier: ~
# Default values for the block
defaults:
# Default name (label) of the block
name: ''
# Default view type of the block. If empty, will use the first available view type.
view_type: ''
# Default item view type of items inside the block. If empty, will use the first
# available item view type in regards to chosen block view type.
item_view_type: ''
# Default values for block parameters
parameters:
param1: value1
param2: value2
block_type_groups:
# Block type group identifier
my_group:
# The switch to enable or disable showing the block type group in the interface
enabled: true
# Block type group name, required
name: 'My group'
# List of block types to show inside the group
block_types: [my_type_1, my_type_2]
View layer¶
All entities in Netgen Layouts (layouts, blocks, mapping rules, forms) are rendered through the same API called the view layer. View layer revolves around three concepts:
- View
- Match configuration
- View context
View¶
All entities in Netgen Layouts specify their own view, which defines what
variables will be available in the Twig templates when the entity is rendered.
For example, layout view (identified by layout_view
identifier), specifies
that all layout templates will have a variable called layout
, which will
hold the currently rendered template, while item view (identified by
item_view
identifier), specifies that all block item templates will have two
variables: item
, which holds the currently rendered block item, and
view_type
, which will hold the item view type with which the item is
rendered.
There are a handful predefined views available in Netgen Layouts, but most interesting ones are ofcourse views for rendering layouts, blocks and block items. The other ones are used by administration interface of Netgen Layouts (list of mappings, layouts and shared layouts) and are rarely needed, e.g. when developing custom mapping targets and conditions.
Match configuration¶
Match configuration is a single rule which specifies which template will be rendered for a specific view when all configured conditions are met. Those conditions are called matches, and services which perform the matching process are called matchers.
View context¶
View context is a set of match configuration rules and each view context is used in different parts of Netgen Layouts. View contexts are what makes it possible for example to use different templates for frontend and backend for your layout types and blocks. Match configurations in a view context are processed sequentially and template from first configuration that matches the rules will be used to render the value.
Netgen Layouts uses four view contexts to render its’ templates: default
for
rendering the frontend templates, api
for rendering the Block Manager app
templates and admin
and value
view contexts for rendering the
administration interface.
Configuring the view layer¶
The following configuration shows an example on how to configure layout_view
and block_view
views, specifying some match rules in two different view
contexts (default
and api
).
netgen_block_manager:
view:
# The identifier of the view
layout_view:
# The name of the view context
default:
# The identifier of a match configuration (unused, but needs to specified in YAML)
# The first match configuration for which all conditions match will be picked up and used to render the view
layout_1:
# The template that will be used when conditions specified below match
template: "@App/layout/layout_1.html.twig"
# The list of custom parameters that will be injected in the template if the conditions specified below match
parameters:
param1: value1
param2: value2
# The list of conditions that will need to match for this rule to be used
match:
layout\type: layout_1
layout_2:
template: "@App/api/layout/layout_2.html.twig"
match:
layout\type: layout_2
block_view:
api:
title:
template: "@App/block/title.html.twig"
match:
block\definition: title
text:
template: "@App/api/block/text.html.twig"
match:
block\definition: text
Tip
If you use eZ Platform, view configuration is siteaccess aware. This means you can have different templates for different siteaccesses or siteaccess groups for the same block views or layout types.
For example, you can use the following config to use two different templates
for my_layout
layout type for eng
and cro
siteaccesses:
netgen_block_manager:
system:
eng:
view:
layout_view:
default:
my_layout:
template: "@App/layouts/my_layout_eng.html.twig"
match:
layout\type: my_layout
cro:
view:
layout_view:
default:
my_layout:
template: "@App/layouts/my_layout_cro.html.twig"
match:
layout\type: my_layout
Custom view contexts¶
If, for some reason, you would want to render some layouts or blocks by hand in your PHP code, you can create custom view contexts (and custom templates, ofcourse) on the fly and use them directly when rendering the layouts or blocks, without touching and compromising the existing frontend templates.
So for example, to render a block view with your custom view context called
my_context
, you would define a configuration similar to this:
netgen_block_manager:
view:
block_view:
my_context:
title:
template: "@App/block/my_context/title.html.twig"
match:
block\definition: title
and then somewhere in your controller:
$block = $this->get('netgen_block_manager.api.service.block')->loadBlock(42);
return $this->get('netgen_block_manager.view.builder')->buildView($block, 'my_context');
Note
You don’t need to return Symfony Response
object from your controllers,
because Netgen Layouts will create and render it on the fly with a built in
event listener.
List of built in views¶
The following lists all built in views with their identifiers, supported interfaces and the list of variables available in the rendered template.
block_view
¶
This view is used to render entities implementing
Netgen\BlockManager\API\Values\Block\Block
interface.
Variable name | Description |
---|---|
block |
The block which is being rendered |
layout_view
¶
This view is used to render entities implementing
Netgen\BlockManager\API\Values\Layout\Layout
interface.
Variable name | Description |
---|---|
layout |
The layout which is being rendered |
Warning
Frontend templates for layouts (default
context) are an exception and
are not rendered through the Netgen Layouts view layer. Instead, they are
rendered by extending from a special ngbm.layoutTemplate
variable,
available in your full view templates. Because of that, in frontend layout
templates, layout
variable is not available. Instead, the rendered
layout is accessed by using ngbm.layout
variable.
item_view
¶
This view is used to render entities implementing
Netgen\BlockManager\Item\ItemInterface
interface.
Variable name | Description |
---|---|
item |
The item which is being rendered |
view_type |
Item view type that the item is being rendered with |
parameter_view
¶
This view is used to render entities implementing
Netgen\BlockManager\Parameters\ParameterValue
interface.
Variable name | Description |
---|---|
parameter |
The parameter which is being rendered |
Note
While rendering other views will throw an exception if there is no template
match in requested view context, this view will fallback to the default
view context. This is due to the fact that in most of the cases, rendering
a block parameter will look exactly the same in the backend and in the
frontend.
This makes it possible to only specify one match configuration rule (in the
default
context) and one template to render the parameter in any view
context.
placeholder_view
¶
This view is used to render entities implementing
Netgen\BlockManager\API\Values\Block\Placeholder
interface.
Variable name | Description |
---|---|
placeholder |
The placeholder which is being rendered |
block |
The block to which the rendered placeholder belongs |
rule_condition_view
¶
This view is used to render entities implementing
Netgen\BlockManager\API\Values\LayoutResolver\Condition
interface.
Variable name | Description |
---|---|
condition |
The condition which is being rendered |
rule_target_view
¶
This view is used to render entities implementing
Netgen\BlockManager\API\Values\LayoutResolver\Target
interface.
Variable name | Description |
---|---|
target |
The target which is being rendered |
List of built in matchers¶
The following lists all built in view matchers. As a rule of thumb, all matchers accept either a scalar or an array of scalars as their value. If an array is provided, the matcher will match if any of the values in the provided array is matched.
Note
While you can use all matchers in all views, most of the combinations do not
make sense and will simply not match. For example, using layout\type
matcher in block_view
view will never match since block_view
renders
a block, while layout\type
matches on layout type of a rendered layout.
block\definition
¶
Matches on block definition of the rendered block. Used in block_view
view.
netgen_block_manager:
view:
block_view:
default:
title:
template: '@App/block/title.html.twig'
match:
block\definition: title
block\view_type
¶
Matches on view type of the rendered block. Used in block_view
view.
Tip
This matcher is usually used with block\definition
matcher to specify
for which exactly block and view type will the template be used.
netgen_block_manager:
view:
block_view:
default:
title\title:
template: '@App/block/title.html.twig'
match:
block\definition: title
block\view_type: title
layout\type
¶
Matches on layout type of the rendered layout. Used in layout_view
view.
netgen_block_manager:
view:
layout_view:
default:
layout_3:
template: '@App/layout/layout_3.html.twig'
match:
layout\type: layout_3
item\value_type
¶
Matches on the type of rendered item. Used in item_view
view.
netgen_block_manager:
view:
item_view:
default:
my_item:
template: '@App/item/my_item.html.twig'
match:
item\value_type: my_item
item\view_type
¶
Matches on item view type of the rendered item. Used in item_view
view.
Tip
This matcher is usually used with item\value_type
matcher to specify
for which exactly item and item view type will the template be used.
netgen_block_manager:
view:
item_view:
default:
my_item\standard_with_intro:
template: '@App/item/my_item.html.twig'
match:
item\value_type: my_item
item\view_type: standard_with_intro
parameter\type
¶
Matches on type of the rendered parameter. Used in parameter_view
view.
netgen_block_manager:
view:
parameter_view:
default:
link:
template: '@App/parameter/link.html.twig'
match:
parameter\type: link
rule_condition\type
¶
Matches on type of the rendered condition. Used in rule_condition_view
view.
netgen_block_manager:
view:
rule_condition_view:
default:
query_parameter:
template: '@App/condition/query_parameter.html.twig'
match:
rule\condition_type: query_parameter
rule_target\type
¶
Matches on type of the rendered target. Used in rule_target_view
view.
netgen_block_manager:
view:
rule_target_view:
default:
route:
template: '@App/target/route.html.twig'
match:
rule\target_type: route
form\type
¶
Matches on type of the Symfony form which is rendered. Used in form_view
view.
Tip
This matcher is usually used with other form\*
matchers if you wish, for
example, to separate templates for rendering block create and block edit
forms.
netgen_block_manager:
view:
form_view:
default:
layout\edit:
template: '@App/layout/edit.html.twig'
match:
form\type: Netgen\BlockManager\Layout\Form\EditType
form\block\definition
¶
Matches on block definition of a block which is edited through the Symfony form.
Used in form_view
view.
netgen_block_manager:
view:
form_view:
default:
block\title\edit:
template: '@App/block/title/edit.html.twig'
match:
form\type: Netgen\BlockManager\Block\Form\FullEditType
form\block\definition: title
form\query\type
¶
Matches on query type of a query which is edited through the Symfony form. Used
in form_view
view.
netgen_block_manager:
view:
form_view:
default:
query\my_query\edit:
template: '@App/query/my_query/edit.html.twig'
match:
form\type: Netgen\BlockManager\Collection\Query\Form\FullEditType
form\query\type: my_query
form\config\config_key
¶
Matches on the config key of a config which is edited through the Symfony form.
Used in form_view
view.
Tip
This matcher is usually used with form\config\value_type
matcher because
most of the time, forms for rendering different aspects of value
configuration will be different.
netgen_block_manager:
view:
form_view:
default:
config\block\http_cache\edit:
template: '@App/config/block/http_cache/edit.html.twig'
match:
form\type: Netgen\BlockManager\Config\Form\EditType
form\config\value_type: Netgen\BlockManager\API\Values\Block\Block
form\config\config_key: http_cache
form\config\value_type
¶
Matches on the type of value for which the config is edited through the Symfony
form. Used in form_view
view.
Tip
This matcher is usually used with form\config\config_key
matcher because
most of the time, forms for rendering different aspects of value
configuration will be different.
netgen_block_manager:
view:
form_view:
default:
config\block\http_cache\edit:
template: '@App/config/block/http_cache/edit.html.twig'
match:
form\type: Netgen\BlockManager\Config\Form\EditType
form\config\value_type: Netgen\BlockManager\API\Values\Block\Block
form\config\config_key: http_cache
Parameter types¶
When adding a parameter to the block or a query in your block definition or a query type, you can specify the parameter name, the type of the parameter and parameter options. This is achieved through an object called parameter builder which is provided to you in your block definition or a query type.
Parameter builder has methods for working with the parameters: adding, removing and fetching the parameters as well as setting parameter options.
When adding parameters to block definitions or queries, referencing the parameter type is done by specifying FQCN of the parameter type.
Common parameter options¶
In addition to the list of options specific to each parameter type, all types
have four common options: required
, default_value
, group
and
label
.
required
¶
type: bool
, required: No, default value: false
Specifies if the parameter value is required or not.
default_value
¶
type: mixed
, required: No, default value: null
Specifies the default value of the parameter.
group
¶
type: array
of string
values, required: No, default value: array()
Specifies the parameter group. Groups are a generic concept when working with parameters. Netgen Layouts does not enforce or otherwise define how you use parameter groups. Internally, Netgen Layouts uses groups in block definitions to specify to which form (content or design) the parameter belongs and in query types to specify which parameters are advanced and hidden by default.
label
¶
type: string
, false
or null
, required: No, default value: null
Specifies the human readable name of the parameter. In Netgen Layouts this label is for example used in Symfony forms which edit blocks or queries.
Tip
Use false
to disable parameter label in Symfony forms. If null
is
specified, the label will be generated by using Symfony translation
component from the parameter name.
List of built in parameter types¶
The following lists all parameter types built into Netgen Layouts together with the options available in each parameter type.
BooleanType
¶
Identifier | boolean |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\BooleanType |
Valid value | A boolean |
Compound\BooleanType
¶
Identifier | compound_boolean |
Available options | |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\Compound\BooleanType |
Valid value | A boolean |
This parameter is a special one in a manner that it can hold other parameters.
The main purpose of the parameter is not functional, but presentational. That is, it allows building Symfony forms that have parameters which can be shown and hidden by checking and un-checking the checkbox input.
The value of the parameter is preserved with all other parameters and its sub-parameters in a flat list, without hierarchy.
ChoiceType
¶
Identifier | choice |
Available options | |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\ChoiceType |
Valid value | One (or more) of the values specified in options option |
multiple
¶type: bool
, required: No, default value: false
Specifies if the parameter type will accept multiple values.
options
¶type: non empty array
or callable
, required: Yes
Specifies the list of values allowed in the parameter. The list of values needs to be a hash where keys represent option label and value represents option value.
If callback is used, the callback needs to return the array in the same format.
EmailType
¶
Identifier | email |
Class | Netgen\BlockManager\Parameters\ParameterType\EmailType |
Valid value | A valid e-mail address |
HtmlType
¶
Identifier | html |
Class | Netgen\BlockManager\Parameters\ParameterType\HtmlType |
Valid value | A valid HTML markup |
This parameter type represents a valid HTML markup. The reason why there’s a custom parameter type is HTML filtering. This parameter type will filter all input to get a valid HTML markup which is free of potential security issues, like XSS.
IdentifierType
¶
Identifier | identifier |
Class | Netgen\BlockManager\Parameters\ParameterType\IdentifierType |
Valid value | A string matching the ^[A-Za-z]([A-Za-z0-9_])*$ regex |
IntegerType
¶
Identifier | integer |
Available options | |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\IntegerType |
Valid value | An integer |
ItemLinkType
¶
Identifier | item_link |
Available options | |
Class | Netgen\BlockManager\Parameters\ParameterType\ItemLinkType |
Valid value | The identifier of an existing value in the form
value_type://value , for example ezlocation://42 |
LinkType
¶
Identifier | link |
Available options | |
Class | Netgen\BlockManager\Parameters\ParameterType\LinkType |
Valid value | A structure containing valid URL, e-mail address, phone
number or internal link, together with link suffix and
target. This structure is represented by
Netgen\BlockManager\Parameters\Value\LinkValue object. |
This parameter type represents a link. Multiple link types are supported:
- URL
- E-mail address
- Phone number
- Internal link (link to valid value of an item)
NumberType
¶
Identifier | number |
Available options | |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\NumberType |
Valid value | Any number (integer or float) |
RangeType
¶
Identifier | range |
Available options | |
Overridden options | |
Class | Netgen\BlockManager\Parameters\ParameterType\RangeType |
Valid value | An integer |
This parameter type represents a range. While in IntegerType
, min
and
max
options are optional, here, they are required.
TextLineType
¶
Identifier | text_line |
Class | Netgen\BlockManager\Parameters\ParameterType\TextLineType |
Valid value | A string without line breaks |
TextType
¶
Identifier | text |
Class | Netgen\BlockManager\Parameters\ParameterType\TextType |
Valid value | A multi-line string |
UrlType
¶
Identifier | url |
Class | Netgen\BlockManager\Parameters\ParameterType\UrlType |
Valid value | A valid URL |
ContentTypeType
¶
Identifier | ez_content_type |
Available options | |
Class | Netgen\BlockManager\Ez\Parameters\ParameterType\ContentTypeType |
Valid value | One (or more) of the valid eZ Platform content types |
This parameter allows to input one or more existing eZ Platform content types as its value. The parameter automatically shows the list of all content types in eZ Platform.
This parameter type is available only if using Netgen Layouts on top of eZ Platform.
LocationType
¶
Identifier | ezlocation |
Class | Netgen\BlockManager\Ez\Parameters\ParameterType\LocationType |
Valid value | ID of an existing eZ Platform location |
This parameter allows to input the ID of an existing eZ Platform location as its value.
This parameter type is available only if using Netgen Layouts on top of eZ Platform.
TagsType
¶
Identifier | eztags |
Available options | |
Class | Netgen\BlockManager\Ez\Parameters\ParameterType\TagsType |
Valid value | Array of IDs of existing tags in Netgen Tags bundle |
This parameter allows to input the list of existing IDs of tags available in Netgen Tags.
Requires Netgen Tags Bundle to be activated work.
min
¶type: int
, required: No, default value: null
Specifies the minimum number of tags that can be set as value. Use null
to
disable the limit.
max
¶type: int
, required: No, default value: null
Specifies the maximum number of tags that can be set as value. Use null
to
disable the limit.
Twig usage¶
Netgen Layouts includes a number of Twig functions and tags to make it easier to work with layouts and blocks in your Twig templates.
Some of the functions are used by the frontend and backend layout and block templates, while others are used exclusively in the administration interface of Netgen Layouts.
List of built in Twig functions¶
The following lists all Twig functions built into Netgen Layouts.
ngbm_render_block
¶
This function is used to render a block:
{{ ngbm_render_block(block) }}
This will render the provided block in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the block template:
{# layout.html.twig #}
{{ ngbm_render_block(block, {'the_answer': 42}) }}
{# block.html.twig #}
{{ the_answer }}
Finally, you can render the block with a view context different from the current one:
{{ ngbm_render_block(block, {}, 'my_context') }}
ngbm_render_layout
¶
Warning
Since all frontend templates for layouts use ngbm.layout
variable to
access the layout instead of the default layout
variable, this function
cannot be used by default to render the layouts in the frontend (i.e. in
default
view context). Instead, it is available for usage in
Netgen Layouts administration interface and for rendering the layout with
custom view contexts.
This function is used to render a layout:
{{ ngbm_render_layout(layout) }}
This will render the provided layout in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the layout template:
{# my.html.twig #}
{{ ngbm_render_layout(layout, {'the_answer': 42}) }}
{# layout.html.twig #}
{{ the_answer }}
Finally, you can render the layout with a view context different from the current one:
{{ ngbm_render_layout(layout, {}, 'my_context') }}
ngbm_render_item
¶
This function is used to render a block item.
In addition to the item you’re rendering, you need to provide the item view type with which you wish to render the item:
{{ ngbm_render_item(item, 'overlay') }}
This will render the provided item in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the item template:
{# block.html.twig #}
{{ ngbm_render_item(item, 'overlay', {'the_answer': 42}) }}
{# item.html.twig #}
{{ the_answer }}
Tip
Normally, parameters provided here are not transferred to content views in
eZ Platform, but only to the item template, which in case of eZ Platform is
only a proxy to eZ content view layer. However, you can use a special
parameter called ezparams
whose contents will be transferred. For
example:
{# block.html.twig #}
{{ ngbm_render_item(item, 'overlay', {'ezparams': {'the_answer': 42}}) }}
{# overlay.html.twig from eZ Platform #}
{{ the_answer }}
Finally, you can render the item with a view context different from the current one:
{{ ngbm_render_item(item, 'overlay', {}, 'my_context') }}
ngbm_render_parameter
¶
This function is used to render a parameter:
{{ ngbm_render_parameter(parameter) }}
This will render the provided parameter in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the parameter template:
{# block.html.twig #}
{{ ngbm_render_parameter(parameter, {'the_answer': 42}) }}
{# parameter.html.twig #}
{{ the_answer }}
Finally, you can render the parameter with a view context different from the current one:
{{ ngbm_render_parameter(parameter, {}, 'my_context') }}
ngbm_render_placeholder
¶
This function is used to render a placeholder of a container block.
To render the placeholder, you need to provide the block from which you want to render the placeholder, as well as placeholder identifier:
{{ ngbm_render_placeholder(block, 'left') }}
This will render the provided placeholder in the view context of the template
from which you called the function or in the default
view context if the
calling template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the placeholder template:
{# block.html.twig #}
{{ ngbm_render_placeholder(block, 'left', {'the_answer': 42}) }}
{# placeholder.html.twig #}
{{ the_answer }}
Finally, you can render the placeholder with a view context different from the current one:
{{ ngbm_render_placeholder(block, 'left', {}, 'my_context') }}
ngbm_render_rule
¶
This function is used to render a rule:
{{ ngbm_render_rule(rule) }}
This will render the provided rule in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the rule template:
{# my.html.twig #}
{{ ngbm_render_rule(rule, {'the_answer': 42}) }}
{# rule.html.twig #}
{{ the_answer }}
Finally, you can render the rule with a view context different from the current one:
{{ ngbm_render_rule(rule, {}, 'my_context') }}
ngbm_render_rule_target
¶
This function is used to render a rule target:
{{ ngbm_render_rule_target(target) }}
This will render the provided target in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the target template:
{# my.html.twig #}
{{ ngbm_render_rule_target(target, {'the_answer': 42}) }}
{# target.html.twig #}
{{ the_answer }}
Finally, you can render the target with a view context different from the current one:
{{ ngbm_render_rule_target(target, {}, 'my_context') }}
ngbm_render_rule_condition
¶
This function is used to render a rule condition:
{{ ngbm_render_rule_condition(condition) }}
This will render the provided condition in the view context of the template from
which you called the function or in the default
view context if the calling
template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the condition template:
{# my.html.twig #}
{{ ngbm_render_rule_condition(condition, {'the_answer': 42}) }}
{# condition.html.twig #}
{{ the_answer }}
Finally, you can render the condition with a view context different from the current one:
{{ ngbm_render_rule_condition(condition, {}, 'my_context') }}
ngbm_render_value_object
¶
This function is used to render any value object supported by Netgen Layouts view layer. All other rendering extensions reuse this function in one form or another.
To render the value object (for example, a block), just transfer it to the function:
{{ ngbm_render_value_object(block) }}
This will render the provided value object in the view context of the template
from which you called the function or in the default
view context if the
calling template is not rendered by the Netgen Layouts view layer.
You can transfer a list of custom parameters to the function, which will be injected as variables into the template:
{# my.html.twig #}
{{ ngbm_render_value_object(block, {'the_answer': 42}) }}
{# block.html.twig #}
{{ the_answer }}
Finally, you can render the value object with a view context different from the current one:
{{ ngbm_render_value_object(block, {}, 'my_context') }}
ngbm_item_path
¶
This function is used to generate an URL to an item from your CMS.
Note
To be able to generate the URL, you need to implement an instance of
Netgen\BlockManager\Item\ValueUrlBuilderInterface
for your item.
To generate the URL, you can simply call the function with the item in question:
<a href="{{ ngbm_item_path(item) }}">{{ item.name }}</a>
If you do not have access to the item object, you can generate the URL with ID and value type of the item:
<a href="{{ ngbm_item_path(42, 'ezlocation') }}">{{ 'My item' }}</a>
Alternatively, you can use a special format used by Netgen Layouts in the form
of an URI scheme value_type://value_id
:
<a href="{{ ngbm_item_path('ezlocation://42') }}">{{ 'My item' }}</a>
ngbm_ezcontent_name
¶
This function is used to retrieve the name of a content from eZ Platform by its ID.
Note
This function exists because eZ Platform does not provide a way to retrieve the content name from its ID. The function is only available if Netgen Layouts is installed on top of eZ Platform.
To retrieve the content name, call the function with the content ID:
{{ ngbm_ezcontent_name(42) }}
If the content with provided ID does not exist, an empty string will be returned.
ngbm_ezlocation_path
¶
This function is used to retrieve the names of all parent locations for eZ Platform location with provided ID.
Note
This function exists because eZ Platform does not provide a way to retrieve the location name from its ID. The function is only available if Netgen Layouts is installed on top of eZ Platform.
To retrieve the names of parent locations, call the function with the location ID:
{% set names = ngbm_ezlocation_path(42) %}
{{ names|join(' / ') }}
This will return the array of parent names, starting from the top most parent.
If the location with provided ID does not exist, null
will be returned.
ngbm_ez_content_type_name
¶
This function is used to retrieve the name of a content type from eZ Platform by its identifier.
Note
This function exists because eZ Platform does not provide a way to retrieve the content type name from its identifier. The function is only available if Netgen Layouts is installed on top of eZ Platform.
To retrieve the content type name, call the function with the content type identifier:
{{ ngbm_ez_content_type_name('article') }}
If the content type with provided identifier does not exist, an empty string will be returned.
List of built in Twig tags¶
The following lists all Twig tags built into Netgen Layouts.
ngbm_render_zone
¶
This tag is used to render an entire layout zone. Since zones do not have their own template, this tag simply renders all blocks one after another.
Note
Examples below show usage as if the tag is used in frontend layout templates
which load the zone from ngbm.layout
variable available in those
templates. This does not mean you can’t transfer an instance of
Netgen\BlockManager\API\Values\Layout\Zone
object manually.
To render a zone, you can simply call the tag with the zone in question:
{% ngbm_render_zone ngbm.layout.zone('left') %}
This will render the provided zone in the default
view context.
You can also render the zone with your own custom view context:
{% ngbm_render_zone ngbm.layout.zone('left') context='my_context' %}
Warning
When rendering a zone with a custom view context, all blocks and block items which do not specify custom view context will be rendered with the view context you provided. You need to make sure all your blocks and block items have the templates for specified view context, otherwise, you will get an exception while rendering the page.
List of built in Twig global variables¶
The following lists all Twig global variables built into Netgen Layouts.
ngbm
¶
This global variable has a couple of purposes, three main ones being:
- to provide currently resolved layout to frontend layout templates
- to provide currently resolved layout template to your full views
- to provide a way to load Netgen Layouts configuration in Twig templates without having to write custom code
The following is a list of variables available:
ngbm.layout
This variable holds the layout resolved in the current request (instance ofNetgen\BlockManager\API\Values\Layout\Layout
). It is mostly used in frontend layout templates to access the layout and render the zones.
ngbm.layoutView
This variable holds the layout view of the layout resolved in current request (instance ofNetgen\BlockManager\View\View\LayoutViewInterface
). You can use it to access any data from the view, like the view context or the name of the layout template. If no layout was resolved, this variable will be set tofalse
and if layout resolving process was not ran, it will be set tonull
.
ngbm.rule
This variable holds the rule (instance ofNetgen\BlockManager\API\Values\LayoutResolver\Rule
) that was used to resolve the layout for the current request. You can use it to access targets and conditions that were responsible for resolving the layout.
ngbm.config
This variable is an instance of
Netgen\Bundle\BlockManagerBundle\Configuration\ConfigurationInterface
. It makes it possible to access configuration of Netgen Layouts (basically, any container parameter which name starts withnetgen_block_manager.
).For example, to access a container parameter called
netgen_block_manager.some_config
, you can usengbm.config.parameter('some_config')
.Tip
In cases when Netgen Layouts is used on top of eZ Platform, you can use this variable to access siteaccess aware container parameters too. For example, accessing parameter called
netgen_block_manager.cro.some_value
can be done with the same code as before:ngbm.config.parameter('some_config')
.In addition to container parameters, this variable makes it possible to access the list of entities provided by Netgen Layouts, like block definitions, query types and so on. You can access them by using the same
ngbm.config.parameter()
function call as before, with the parameter names specified below:
block_definitions
- Provides a list of all block definitionsblock_types
- Provides a list of all block typesblock_type_groups
- Provides a list of all block type groupslayout_types
- Provides a list of all layout typesquery_types
- Provides a list of all query typesvalue_types
- Provides a list of all value typestarget_types
- Provides a list of all target typescondition_types
- Provides a list of all condition typesparameter_types
- Provides a list of all parameter types
ngbm.layoutTemplate
This variable is a shortcut to access the template name of the layout resolved in current request (available asngbm.layoutView.template
). You will mostly use this variable in your full views to extend from instead of your default pagelayout. Using this variable starts the layout resolving process if it was not ran already. In case when no layout was resolved, this variable holds the name of your default pagelayout, so your full views can fallback to it without need to modify them.
ngbm.pageLayoutTemplate
This variable holds the name of your default pagelayout which you configured inside Netgen Layouts. It is mostly used to extend from in frontend layout templates, so those templates can fallback to it to render the page head, header, footer and so on.
ngbm_admin
¶
This global variable is used by the administration interface of Netgen Layouts. Currently, only one variable is available:
ngbm_admin.pageLayoutTemplate
This variable holds the name of the pagelayout template for the admin interface. The idea behind it is that you can change the pagelayout of the administration interface without having to change the administration templates themselves. This can be achieved by setting this variable to a desired template name before admin interface is rendered (e.g. in an event listener).
Events¶
Netgen Layouts dispatches some events in a lifecycle of displaying the page with a resolved layout that you can listen to and act upon.
The following lists all available events.
ngbm.view.build_view¶
Event class: Netgen\BlockManager\Event\CollectViewParametersEvent
This event will be dispatched when the view of a value object is being built. It can be used to inject custom variables into the view before the view is built.
For example, you can use the following to inject a variable into the block view:
public function onBuildView(CollectViewParametersEvent $event)
{
$view = $event->getView();
if (!$view instanceof BlockViewInterface) {
// Do nothing if the view does not belong to a block
return;
}
if ($view->getContext() !== 'default') {
// Do nothing if the view context is not for the frontend
return;
}
$event->addParameter('the_answer', 42);
}
ngbm.view.render_view¶
Event class: Netgen\BlockManager\Event\CollectViewParametersEvent
This event will be dispatched when the view of a value object is being rendered. It can be used to inject custom variables into the view before the view is sent to Twig for rendering.
The example for injecting a variable into the view is the same as with
build_view
event.
ngbm.admin.match¶
Event class: Netgen\Bundle\BlockManagerAdminBundle\Event\AdminMatchEvent
This event will be dispatched when the request is matched as being a Netgen Layouts admin interface request. It is usually used if you want to override the pagelayout of Netgen Layouts admin interface for integrating it in other admin panels.
Symfony services¶
Netgen Layouts provides a number of Symfony services that you can use to work in PHP code with layouts, blocks, collections, queries and so on.
API services¶
The following services are the entry point and central place for all operations with any entity available in Netgen Layouts. It is used by Netgen Layouts itself to render the layouts in the frontend, in REST API used in Block Manager app and in the admin interface. Basically, they provide a CRUD interface to work with layouts, blocks, collections and layout resolver rules.
Service name | Description |
---|---|
netgen_block_manager.api.service.layout |
CRUD operations for layouts |
netgen_block_manager.api.service.block |
CRUD operations for blocks |
netgen_block_manager.api.service.collection |
CRUD operations for collections |
netgen_block_manager.api.service.layout_resolver |
CRUD operations for layout resolver rules |
View API¶
The following services can be used to manually render any entity in Netgen Layouts.
Service name | Description |
---|---|
netgen_block_manager.view.view_builder |
Used to manually build the view for the entity. |
netgen_block_manager.view.view_renderer |
Used to manually render the entity view once it has been built. |
netgen_block_manager.view.renderer |
Shortcut service that uses view builder and view renderer services to render the provided entity. |
Block items¶
The following services can be used to manually load and build block items to inject them into blocks and link to them.
Service name | Description |
---|---|
netgen_block_manager.item.item_builder |
Used to manually build the item from the provided entity. |
netgen_block_manager.item.item_loader |
Used to manually load the item from provided value type and value ID. |
netgen_block_manager.item.url_builder |
Used to build the URL to the item. |
Registries¶
A number of registries is provided so you can access the list of all available block definitions, query types and so on.
Service name | Description |
---|---|
netgen_block_manager.block.registry.block_definition |
Used to access all available block definitions |
netgen_block_manager.block.registry.block_type |
Used to access all available block types |
netgen_block_manager.block.registry.block_type_group |
Used to access all available block type groups |
netgen_block_manager.collection.registry.query_type |
Used to access all available query types |
netgen_block_manager.parameters.registry.parameter_type |
Used to access all available parameter types |
netgen_block_manager.item.registry.value_type |
Used to access all available value types |
netgen_block_manager.layout.registry.layout_type |
Used to access all available layout types |
netgen_block_manager.layout.resolver.registry.target_type |
Used to access all available target types |
netgen_block_manager.layout.resolver.registry.condition_type |
Used to access all available condition types |
Shortcut services¶
Some of the entities that can be accessed through one of the above registries
can also be accessed directly by using a unique service name. All of those
service names are in the form of <prefix>.<identifier>
. Prefix is the same
for all entities that implement the same interface, while identifier identifies
a single entity.
The following table lists all available service prefixes:
Entity type | Service prefix |
---|---|
Block definition | netgen_block_manager.block.block_definition. |
Block type | netgen_block_manager.block.block_type. |
Block type group | netgen_block_manager.block.block_type_group. |
Query type | netgen_block_manager.collection.query_type. |
Value type | netgen_block_manager.item.value_type. |
Layout type | netgen_block_manager.layout.layout_type. |
As an example, if you wish to load the service for title
block definition,
you would use a service name
netgen_block_manager.block.block_definition.title
.
Other services¶
The following lists various other useful services which can be used by client code:
Service name | Description |
---|---|
netgen_block_manager.http_cache.client |
Provides APIs for invalidating layout and block HTTP caches |
netgen_block_manager.configuration |
Provides a way to access Netgen Layouts configuration values |
netgen_block_manager.collection.result_loader |
Generates the collection result (items) from a provided collection |
netgen_block_manager.layout.resolver |
Exposes APIs to manually run the layout resolving process on a request |
eZ Publish specific services¶
The following lists various useful services available when Netgen Layouts is installed on top of eZ Publish.
Service name | Description |
---|---|
netgen_block_manager.ezpublish.content_provider |
Used to extract current content and location for use by contextual blocks and queries |
Upgrades¶
Upgrades¶
Upgrading from 0.7.0 to 0.8.0¶
Upgrade composer.json
¶
In your composer.json
file, upgrade the version of netgen/block-manager
package to ~0.8.0
and run the composer update
command.
Note
If you have Netgen Layouts installed on eZ Platform, the package name will
be netgen/block-manager-ezpublish
.
Note
Integrations into Netgen Admin UI and eZ Platform UI also have separate
packages whose versions need to be bumped to ~0.8.0
.
Activate required bundles¶
Version 0.8 requires FOS HTTP Cache Bundle to be activated, so if not already
present, activate FOS\HttpCacheBundle\FOSHttpCacheBundle
in your kernel.
Database migration¶
Run the following command from the root of your installation to execute migration to version 0.8 of Netgen Layouts:
$ php app/console doctrine:migrations:migrate --configuration=vendor/netgen/block-manager/migrations/doctrine.yml
Upgrading Netgen Content Browser¶
Netgen Content Browser version 0.8 was also automatically installed. Be sure to read its upgrade instructions too, to make sure you custom code keeps working.
Updates to Varnish VCL¶
To enable caching and later cache clearing of block and layout HTTP caches, you
will need to use Varnish. To make the cache clearing work, you need to modify
your Varnish VCL and add the following rules somewhere in your vcl_recv
function. If you’re using eZ Platform and the VCL supplied by it, the best place
to put this is in ez_purge
function (which is called from vcl_recv
),
right after if (req.http.X-Location-Id) { ... }
block.
For Varnish 3:
if (req.http.X-Layout-Id) {
ban( "obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
}
error 200 "Banned";
}
if (req.http.X-Block-Id) {
ban( "obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
}
error 200 "Banned";
}
For Varnish 4 and later:
if (req.http.X-Layout-Id) {
ban("obj.http.X-Layout-Id ~ " + req.http.X-Layout-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for layout with ID " + req.http.X-Layout-Id;
}
return (synth(200, "Banned"));
}
if (req.http.X-Block-Id) {
ban("obj.http.X-Block-Id ~ " + req.http.X-Block-Id);
if (client.ip ~ debuggers) {
set req.http.X-Debug = "Ban done for block with ID " + req.http.X-Block-Id;
}
return (synth(200, "Banned"));
}
Breaking changes¶
The following breaking changes were made in version 0.8 of Netgen Layouts. Follow the instructions to upgrade your code to this newer version.
Some classes and interfaces had their namespaces changed. Update your custom code working with these classes and interfaces. The following table lists the old and the new interface and class names:
Old FQCN New FQCN Netgen\BlockManager\API\Values\Page\Block
Netgen\BlockManager\API\Values\Block\Block
Netgen\BlockManager\API\Values\Page\Placeholder
Netgen\BlockManager\API\Values\Block\Placeholder
Netgen\BlockManager\API\Values\Page\CollectionReference
Netgen\BlockManager\API\Values\Block\CollectionReference
Netgen\BlockManager\API\Values\Page\BlockCreateStruct
Netgen\BlockManager\API\Values\Block\BlockCreateStruct
Netgen\BlockManager\API\Values\Page\BlockUpdateStruct
Netgen\BlockManager\API\Values\Block\BlockUpdateStruct
Netgen\BlockManager\API\Values\Page\Layout
Netgen\BlockManager\API\Values\Layout\Layout
Netgen\BlockManager\API\Values\Page\Zone
Netgen\BlockManager\API\Values\Layout\Zone
Netgen\BlockManager\API\Values\Page\LayoutCreateStruct
Netgen\BlockManager\API\Values\Layout\LayoutCreateStruct
Netgen\BlockManager\API\Values\Page\LayoutUpdateStruct
Netgen\BlockManager\API\Values\Layout\LayoutUpdateStruct
Notable change is the
Block
interface since it’s used inNetgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface::getDynamicParameters
. method. You will need to modify your custom block definition handlers to use the new interface.In
Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface::getDynamicParameters
method, a second, unused, parameter called$parameters
was removed from the interface. Remove it from your custom block definition handlers.A new method called
isContextual
was added toNetgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface
interface. The purpose of this method is to signal to the system when the query acts as a contextual query, i.e., if it depends on data from current request to run. You need to add this method to your custom query type handlers.The following is the method signature as well as an example implementation:
/** * Returns if the provided query is dependent on a context, i.e. current request. * * @param \Netgen\BlockManager\API\Values\Collection\Query $query * * @return bool */ public function isContextual(Query $query);
public function isContextual(Query $query) { return $query->getParameter('use_current_location')->getValue() === true; }
BlockDefinitionHandlerInterface::hasCollection
method has been removed. From now on, specifying that the block has a collection is done through configuration. The following shows the old and new way of specifying that the block has a collection:// Old way <?php namespace MyApp\Block\BlockDefinition\Handler; use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler; class MyBlockHandler extends BlockDefinitionHandler { /** * Returns if this block definition should have a collection. * * @return bool */ public function hasCollection() { return true; } }
# New way netgen_block_manager: block_definitions: my_block: collections: ~
Netgen\BlockManager\Layout\Resolver\TargetTypeInterface::provideValue
method has a changed signature. From now on, SymfonyRequest
object is provided as a parameter to the method, so there’s no need to manually fetch the current request from the request stack. The new interface looks like this:/** * Provides the value for the target to be used in matching process. * * @param \Symfony\Component\HttpFoundation\Request $request * * @return mixed */ public function provideValue(Request $request);
Netgen\BlockManager\Layout\Resolver\ConditionTypeInterface::matches
method has a changed signature. From now on, SymfonyRequest
object is provided as a first parameter to the method, so there’s no need to manually fetch the current request from the request stack. The new interface looks like this:/** * Returns if this request matches the provided value. * * @param \Symfony\Component\HttpFoundation\Request $request * @param mixed $value * * @return bool */ public function matches(Request $request, $value);
Netgen\BlockManager\Traits\RequestStackAwareTrait
trait has been removed. Inject the request stack service directly into the constructor.If using Netgen Layouts with eZ Publish 5, instead of redefining the alias for the content provider service, you now have to redefine the alias for newly introduced content extractor service.
# Before netgen_block_manager.ezpublish.content_provider: alias: netgen_block_manager.ezpublish.content_provider.ez5_request
# After netgen_block_manager.ezpublish.content_extractor: alias: netgen_block_manager.ezpublish.content_extractor.ez5_request
netgen_block_manager.google_maps_api_key
configuration was renamed tonetgen_block_manager.api_keys.google_maps
. The following shows an example of the old and new configs:# Old config netgen_block_manager: google_maps_api_key: MY_API_KEY
# New config netgen_block_manager: api_keys: google_maps: MY_API_KEY
standard
item view type is always added to all view types automatically. However, this was not true for view types that specified custom item view types. You had to specifystandard
item view type manually if you wanted to use it. From now on,standard
item view type will be added in those cases too. If you wish to disable it, you can do so like this:netgen_block_manager: block_definitions: my_block: view_types: my_view_type: item_view_types: standard: enabled: false
User policies are introduced. To be able to manage user policies in legacy administration interface of eZ Publish, you need to activate the provided
nglayouts
legacy extension. If you’re using eZ Platform UI, policy management is available automatically.Custom items can now be added to blocks manually, instead of just being able to return them from query types. Make sure to implement the
Netgen\BlockManager\Item\ValueLoaderInterface
for your custom items, as well as Content Browser backend, and then activate the value type in configuration:netgen_block_manager: items: value_types: my_value_type: name: 'My value type'
Cookbook¶
Cookbook¶
Netgen Layouts provides around 10 officially supported extension points, ranging from simpler ones like creating custom layout types to advanced and rarely used ones like creating custom conditions and targets for layout matching process. The following chapters will go through each of these extension points and detail the process of creating your own layout types, blocks, query types and so on.
Creating a custom layout type¶
Netgen Layouts ships with some layout types (from simple ones with one zone, to more complicated ones with 8 zones), which are readily available to be used in your own projects without any additional configuration.
If none of those layout types satisfy your needs, you can create your own. Creating your own layout type is split into three parts:
- Basic configuration
- Creating frontend and backend Twig templates for rendering the layout
- Connecting the templates with your layout type
We will demonstrate the process by creating a simple layout type with two zones:
left
and right
.
Basic configuration¶
To register a new layout type in Netgen Layouts, add the following YAML config:
netgen_block_manager:
layout_types:
my_layout:
name: 'My layout'
zones:
left:
name: 'Left'
right:
name: 'Right'
This specifies that our new layout type has a my_layout
identifier, that its
human readable name is My layout
and that it has two zones, left
and
right
.
Creating frontend and backend Twig templates¶
Netgen Layouts uses two separate templates to render the layout on the frontend and in the backend. By default, all frontend templates and backend templates for built-in layout types are based on Bootstrap grid. Backend templates, used by the Block Manager app, will be rendered correctly as long as you provide the valid Bootstrap markup. On the other hand, to render frontend templates correctly, you will need to include CSS for Bootstrap Grid component in your site.
Note
It is possible to switch all frontend templates to use a different grid like Flexbox, however at this time, only Bootstrap implementation of templates is available.
Creating a backend template¶
Backend templates for layout types usually do not have any logic, apart from
HTML markup that specifies where each of the layout type zones goes within
Bootstrap grid. If needed, backend templates can access the currently rendered
layout with a Twig variable named layout
.
The following template shows an example of how would the backend template look for layout type we configured earlier:
{# @App/layouts/api/my_layout.html.twig #}
<div class="row">
<div class="col-md-8">
<div data-zone="left"></div>
</div>
<div class="col-md-4">
<div data-zone="right"></div>
</div>
</div>
It is important for backend templates to have <div data-zone="my_zone"></div>
element with correct zone identifier (replacing my_zone
) for every zone
configured.
Creating a frontend template¶
Frontend templates are usually more complicated, since they need to provide the code to actually render layout zones by themselves.
All markup in frontend templates for your layout types needs to be inside a Twig
block named layout
and they always need to extend a special
ngbm.pageLayoutTemplate
variable available in the template. This variable
will always hold the name of the main pagelayout of your app, which is either
configured manually through Netgen Layouts configuration, or in some cases
picked up automatically from available configuration of your app (if using
eZ Platform for example).
This template has access to currently rendered layout via Twig variable named
ngbm.layout
and each zone is rendered via ngbm_render_zone
Twig tag.
Since zone does not have a template of its own, this tag simply renders all
blocks one after another. Accessing a zone via ngbm.layout.zone('left')
function will either return the requested zone, or the zone in a shared layout
if one is configured for the specified zone, so no additional code is required
to handle zones connected to shared layouts.
The complete frontend template for your custom layout type with two zones might look something like this:
{# @App/layouts/my_layout.html.twig #}
{% extends ngbm.pageLayoutTemplate %}
{% block layout %}
<div class="container">
<div class="row">
<div class="col-lg-8">
{% if ngbm.layout.zone('left') is not empty %}
{% ngbm_render_zone ngbm.layout.zone('left') %}
{% endif %}
</div>
<div class="col-lg-4">
{% if ngbm.layout.zone('right') is not empty %}
{% ngbm_render_zone ngbm.layout.zone('right') %}
{% endif %}
</div>
</div>
</div>
{% endblock %}
Connecting the templates with your layout type¶
To activate the frontend and backend templates you defined, you will need to configure them through the view layer configuration. Read up on what a view layer is and the corresponding terminology in documentation specific to view layer itself.
Currently, two matchers are implemented in the view layer for layout view:
layout\type
- Matches on layout type of a layoutlayout\shared
- Matches on “shared” flag of a layout
Most of the time, you will use layout\type
matcher for configuring templates
for your custom layout types. The reason for this is that shared layouts are
never rendered directly on the frontend so there is no really need for using
layout\shared
matcher. The reason for its existence is that it is used in
the administration interface of Netgen Layouts.
The following is an example config that enables the two templates we created:
netgen_block_manager:
view:
layout_view:
default:
my_layout:
template: "@App/layouts/my_layout.html.twig"
match:
layout\type: my_layout
api:
my_layout:
template: "@App/layouts/api/my_layout.html.twig"
match:
layout\type: my_layout
api_version: 1
At this point, your new layout type is ready for usage.
Creating a custom block¶
Similar to layout types, when creating a custom block, you need a bit of configuration and some templates, but since blocks almost always need some custom logic, you will also need to create a PHP class that will handle custom functionalities of a block. In the following examples, we will show creating a custom block that can render a Markdown document.
When creating a custom block, you will often run into two entities mentioned in code and configuration: a block definition, and a block type. Before you actually create a custom block, it is important to understand a difference between a block definition and a block type.
Difference between block definition & block type¶
Block definition is the central entity you will be creating when creating a custom block. As the name implies, block definition defines how your custom block behaves. This includes specifying what parameters will the block have and what type are they of and if the block has a collection or not. It also gives you a possibility to write your own custom behaviour for a block, based on block parameters. In case of container blocks, it specifies which placeholders the container block has.
Each block definition can have multiple block types. Block type is nothing more than a starting configuration used when creating a block in a layout. In Block Manager app, block types are what is shown on the left side and what you drag and drop to a zone in a layout. Creating block types for a certain block definition requires only a couple of lines of configuration where you would specify starting values for block label, block view, block item view and block parameters.
Once you create a block in a layout, it doesn’t store the information from which block type it was created, it only stores the block definition. When you think about it, this makes sense. Since block type is a starting configuration for a block you’re adding to a layout, and that configuration can change in the lifecycle of a block, there is no benefit in storing the information which block type was used to create the block. On the other hand, block definition needs to be stored because it defines how block parameters will be validated, what custom behaviour the block has and so on.
Configuring a new block definition¶
To register a new block definition in Netgen Layouts, you will need the following configuration:
netgen_block_manager:
block_definitions:
my_markdown:
name: 'My markdown block'
view_types:
my_markdown:
name: 'My markdown block'
This configuration example adds a new block definition with my_markdown
identifier, which as a human readable name My markdown block
and has one
view type, also called my_markdown
.
View type is nothing more than an identifier of a template which will be used to render the block. Every block definition needs at least one view type.
Note
By convention, in built in blocks, if a block definition has only one view type, like above, that view type will have the same identifier as the block definition itself.
Creating a PHP service for a block definition¶
Every block definition needs a single PHP class that specifies the entire
behaviour of a block. This class needs to implement
Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandlerInterface
interface which specifies a number of methods for you to implement. To simplify
implementing new block definitions, an abstract class exists
(Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler
) which has
all of those methods implemented with default and empty implementations,
reducing the need for writing boilerplate code.
Let’s create a basic block definition handler class:
<?php
namespace AppBundle\Block\BlockDefinition\Handler;
use Netgen\BlockManager\API\Values\Block\Block;
use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;
class MyMarkdownHandler extends BlockDefinitionHandler
{
/**
* Builds the parameters by using provided parameter builder.
*
* @param \Netgen\BlockManager\Parameters\ParameterBuilderInterface $builder
*/
public function buildParameters(ParameterBuilderInterface $builder)
{
}
/**
* Returns the array of dynamic parameters provided by this block definition.
*
* @param \Netgen\BlockManager\API\Values\Block\Block $block
*
* @return array
*/
public function getDynamicParameters(Block $block)
{
}
}
Specifying block parameters¶
First method we will look at is buildParameters
method. By using an object
called parameter builder and adding parameter specifications to it, this method
will specify which parameters your custom block will have. Details on how the
parameter builder works, what parameter types exist and how to implement custom
parameter type are explained in dedicated chapter.
Let’s add a custom parameter to our block which will serve as an input for raw Markdown content:
use Netgen\BlockManager\Parameters\ParameterType;
public function buildParameters(ParameterBuilderInterface $builder)
{
$builder->add('content', ParameterType\TextType::class);
}
If you open Block Manager app, you will notice that all blocks have at least
CSS class
, CSS ID
and Set container
parameters. These three parameters
are nothing special, they are exactly the same as any other parameter in any
block. They can be added to your block definition handler manually, but in order
to reduce the amount of code you need to write, there is a handy method called
buildCommonParameters
in BlockDefinitionHandler
abstract class, which
will add those three parameters for you. So finally, our buildParameters
method will look like this:
public function buildParameters(ParameterBuilderInterface $builder)
{
$builder->add('content', ParameterType\TextType::class);
$this->buildCommonParameters($builder);
}
Notice that we didn’t specify the human readable labels for the parameters.
That’s because they are generated automatically via translation system. To
create the correct labels for your block parameters, you need to add one string
to ngbm
translation catalog for every parameter in your block, (excluding
the ones provided by buildCommonParameters
method, as they already have
translation strings) with the format block.<block_definition>.<parameter_name>
where block_definition
and parameter_name
are placeholders that need to
be replaced with correct values. So, for our custom Markdown block definition,
the translation file would look something like this:
block.my_markdown.content: 'Content'
Custom block behaviour¶
Second method in our handler example above is called getDynamicParameters
.
This method is used for your own custom logic. Anything goes in this method. You
can inject dependencies into your block definition handler, use them here, do
some processing based on provided instance of a block or some other parameters
you provide when rendering a block manually and so on.
After all processing is done, this method needs to return the array with key/value pairs which will be injected into template when block is rendered. Each of these values can either be a regular scalar, array, object and so on, or it can be a closure, which will transparently be called to calculate the value at the moment the parameter is used inside the block template.
In case of our Markdown handler, we will need to inject a Markdown parser into
our handler, and use it in this method to parse the raw Markdown into HTML. We
will be using Michelf\MarkdownInterface
, Markdown parser which is already
pre-installed with Netgen Layouts:
/**
* @var \Michelf\MarkdownInterface
*/
protected $markdownParser;
public function __construct(MarkdownInterface $markdownParser)
{
$this->markdownParser = $markdownParser;
}
public function getDynamicParameters(Block $block)
{
$rawContent = $block->getParameter('content')->getValue();
return array(
'html' => $this->markdownParser->transform($rawContent),
);
}
Defining the Symfony service for our handler¶
To connect the created handler with block definition configuration, we need to register the handler in Symfony DIC. We also need to specify a service for Markdown parser we used in the handler:
services:
app.markdown:
class: Michelf\MarkdownExtra
app.block.block_definition.handler.markdown:
class: AppBundle\Block\BlockDefinition\Handler\MyMarkdownHandler
arguments:
- "@app.markdown"
tags:
- { name: netgen_block_manager.block.block_definition_handler, identifier: my_markdown }
This configuration is a fairly regular specification of services in Symfony,
however, to correctly recognize our PHP class as a block definition handler, we
need to tag it with netgen_block_manager.block.block_definition_handler
tag
and attach to it an identifier
key with a value which equals to the
identifier of block definition we configured at the beginning (in this case
my_markdown
).
Specifying block view templates¶
Every view type in your block definition needs to have two templates, one for
frontend and one for backend. If you remember, we specified that our
my_markdown
block definition has one view type, also called my_markdown
.
Frontend block template¶
Let’s create a template for displaying the block in the frontend with
my_markdown
view type. Every frontend template for the block needs to extend
from @NetgenBlockManager/block/block.html.twig
and all content of the
template needs to be inside Twig block called content
. The currently
rendered block is accessible via block
variable which you can use to access
block parameters specified in the handler as well as any dynamic parameters in
the block.
Tip
View type templates for built in block definitions are also a great source of inspiration, so make sure to give them a look.
Our frontend template for the Markdown block definition will simply output the parsed Markdown which is provided by the handler:
{# @App/blocks/my_markdown/my_markdown.html.twig #}
{% extends '@NetgenBlockManager/block/block.html.twig' %}
{% block content %}
{{ block.dynamicParameter('html')|raw }}
{% endblock %}
Backend block template¶
As for backend, in this specific case, the template will look almost the same (since all we want is to render the parsed Markdown), save for the different template used to extend from.
In general, all backend templates need to extend from
@NetgenBlockManager/api/block/block.html.twig
(notice that this template is
different from the frontend base template, this one is in an api
folder).
In most cases, backend template will be simpler than the frontend one, without
any design specific markup and so on. Everything you can use in frontend
templates is also available here, meaning that you can use the block
variable to access the block and its parameters.
Going back to our example backend template, it will look like this:
{# @App/blocks/api/my_markdown/my_markdown.html.twig #}
{% extends '@NetgenBlockManager/api/block/block.html.twig' %}
{% block content %}
{{ block.dynamicParameter('html')|raw }}
{% endblock %}
Connecting the templates with your block definition¶
To activate the frontend and backend templates you defined, you will need to configure them through the view layer configuration. Read up on what a view layer is and the corresponding terminology in documentation specific to view layer itself.
Currently, two matchers are implemented in the view layer for block view:
block\definition
- Matches on block definition of a blockblock\view_type
- Matches on view type of a block
If you are creating a block which will only have a single view type, you can
omit the block\view_type
matcher and use only block\definition
matcher,
which will make sure that templates you defined will be applied to any future
view types of your block automatically.
The following is an example config that enables the two templates we created:
netgen_block_manager:
view:
block_view:
default:
my_markdown:
template: "@App/blocks/my_markdown/my_markdown.html.twig"
match:
block\definition: my_markdown
# View type matcher is optional
block\view_type: my_markdown
api:
my_markdown:
template: "@App/blocks/api/my_markdown/my_markdown.html.twig"
match:
block\definition: my_markdown
# View type matcher is optional
block\view_type: my_markdown
api_version: 1
The following configuration shows how you can specify a fallback template that will be applied to all block view types that do not specify their own template rules:
netgen_block_manager:
view:
block_view:
default:
my_markdown:
template: "@App/block/my_markdown.html.twig"
match:
block\definition: my_block
api:
my_markdown:
template: "@App/api/block/my_markdown.html.twig"
match:
block\definition: my_block
Note
Take care to specify the fallback rule at the bottom of all other rules, since the first rule that matches will be used when searching for templates.
After you have defined the configuration for the view layer, your block is ready for usage.
Defining block types for your block definition¶
Remember block types and how we said that block types are a starting configuration for a block definition? Remember how we said that block types are the thing that is shown on the left hand side in the Block Manager app?
When you create a custom block definition, Netgen Layouts internally creates for you a single block type with the same name as block definition with empty default configuration, and adds it to a block type group called “Custom blocks”. This is to enable the block definition to be displayed in the interface so you can actually add it to a layout.
If you want to create another starting configuration for your block definition, you can do so by configuring an additional block type which will also be automatically added to a “Custom blocks” group. For example:
netgen_block_manager:
block_types:
my_markdown_v2:
name: 'My Markdown block with default title'
definition_identifier: my_markdown
defaults:
parameters:
content: '# Some default title'
This configuration defines a block type with my_markdown_v2
identifier,
which sets a default value for content
parameter.
If you want to define some other group where your block type should live, you
can do so. In that case, the block type will not be shown in the Custom blocks
group, but in the group you specified. You can use the configuration similar to
this:
netgen_block_manager:
block_type_groups:
my_group:
name: 'My group'
block_types: [my_markdown_v2, second_block_type, other_block_type]
Tip
Once you start adding more and more block types for your block definition, you might decide that you no longer need the automatically created block type with empty configuration. In that case, you might want to simply disable it:
netgen_block_manager:
block_types:
my_markdown:
enabled: false
Creating a custom container block¶
Containers are blocks like any other, so everything described in chapter on creating a custom block applies here.
The difference between regular blocks and containers is that containers can contain other blocks. This is achieved by using something called a placeholder, which is nothing more than a label which is applied to every child block in a container. When rendering the container, you can use this label to specify where in the template will all blocks with that label be rendered. As with zones, all blocks with a specified label will be rendered one after another.
Think of a placeholder in the container block as a lightweight variant of a zone in a layout. When you drag and drop a new block to a placeholder in a container, placeholder label is automatically assigned to the new block so it can be rendered when requested.
Note
Currently, it is not possible to add containers within containers.
Container definition handler¶
When creating a custom block definition handler for a container block, instead
of extending from Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler
abstract class, you will extend from
Netgen\BlockManager\Block\BlockDefinition\ContainerDefinitionHandler
which
is also an abstract class. What is left for you to do is to define which
placeholders your new container block has by implementing
getPlaceholderIdentifiers
method. For example, Two columns
container
block provided by Netgen Layouts looks like this:
<?php
namespace Netgen\BlockManager\Block\BlockDefinition\Handler\Container;
use Netgen\BlockManager\Block\BlockDefinition\ContainerDefinitionHandler;
class TwoColumnsHandler extends ContainerDefinitionHandler
{
/**
* Returns placeholder identifiers.
*
* @return array
*/
public function getPlaceholderIdentifiers()
{
return array('left', 'right');
}
}
Twig templates¶
Frontend Twig templates for container blocks have all the same features as Twig
templates for regular blocks. The only difference is that in templates for
container blocks, you can use ngbm_render_placeholder
Twig function to
render all blocks which are located in specified placeholder.
For example, frontend Twig template for Two columns
block provided by
Netgen Layouts looks like this:
{% extends '@NetgenBlockManager/block/block.html.twig' %}
{% block content %}
<div class="row">
<div class="col-md-6">
{{ ngbm_render_placeholder(block, 'left') }}
</div>
<div class="col-md-6">
{{ ngbm_render_placeholder(block, 'right') }}
</div>
</div>
{% endblock %}
As for backend Twig templates for container blocks, they are similar to backend Twig templates for layout types. They do not need custom logic to render the blocks, except specific HTML elements used as markers for block placeholders:
{% extends '@NetgenBlockManager/api/block/block.html.twig' %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div data-placeholder="left" data-receiver></div>
</div>
<div class="col-md-6">
<div data-placeholder="right" data-receiver></div>
</div>
</div>
{% endblock %}
Basically, for every placeholder in your container block, you need to have a
<div data-placeholder="my_placeholder" data-receiver></div>
element with a
placeholder identifier in data-placeholder
attribute.
Adding collections to blocks¶
As you are probably aware, blocks can have collections attached to them, which provide block items to the block. Block definition can control if the block can have a collection by specifying it in the block definition configuration. By default, blocks do not use collections, so if you want to allow your custom block definitions to use collections, configure it like this:
netgen_block_manager:
block_definitions:
my_block:
collections: ~
Once you specify that your block definition can use collections, all blocks will automatically be created with a manual collection attached to the block.
Note
Currently, every block can have only one collection with an identifier named
default
.
You can also configure which items and query types are valid for a collection. That way, you can specify, for example, that your collection can only have manual items or only a dynamic collection, or any combination of two.
To specify which item are allowed within the collection, use the config similar to this:
netgen_block_manager:
block_definitions:
my_block:
collections:
default:
# Set to an empty array to disable manual collection
valid_item_types: [my_item_type]
To specify which query types are allowed within the collection, use the config similar to this:
netgen_block_manager:
block_definitions:
my_block:
collections:
default:
# Set to an empty array to disable dynamic collection
valid_query_types: [my_query]
Accessing the collection and items in the template¶
The collection provides the block items to the block in form of a collection
result set object (Netgen\BlockManager\Collection\Result\ResultSet
) which is
a collection of result objects (Netgen\BlockManager\Collection\Result\Result
)
which themselves are a simple wrapper around the single item in a collection.
In the Twig templates for your block views, all collection results are
accessible through collections
variable, which you can use to iterate over
the items in a collection and render them:
{% for result in collections.default %}
{{ ngbm_render_item(result.item, block.itemViewType) }}
{% endfor %}
When rendering an item, you need to specify with which item view type it will be
rendered (block.itemViewType
in the above example). As with block view types,
item view type is nothing more than an identifier of a template which will be
used to render the item. Every block view type can define which item view types
it supports with the configuration like this:
netgen_block_manager:
block_definitions:
my_block:
name: 'My block'
view_types:
first:
name: 'First'
item_view_types:
item_type_1:
name: 'Item type 1'
item_type_2:
name: 'Item type 2'
second:
name: 'Second'
With this, we specified that first
block view type supports item_type_1
and item_type_2
item view types, while second
block view type does not
specify any specific item view types.
For every view type, an item view type called standard
will be added
automatically to configuration. This is to make it easier to create item view
type templates for simpler blocks. If you wish to disable this standard
item
view type, you can do so like this:
netgen_block_manager:
block_definitions:
my_block:
view_types:
my_view_type:
item_view_types:
standard:
enabled: false
Tip
In your Twig templates for block view types, you can ofcourse choose not to use
the item view type stored in a block (block.itemViewType
), but use a
hardcoded one, or mix the hardcoded item view type with the one stored in a
block and so on.
Adding custom view types to blocks¶
Just as you can specify that your own custom blocks have certain view types, you can extend existing blocks to add your own view types to them. Configuration to do so is completely the same as with adding a custom block.
When adding a block view type, you need to specify them in block definition settings, as well as configure the template used for them (both for frontend and backend).
The following configuration shows an example how to add a custom view type to
existing list
block definition called list_numbered
and how to specify
the templates that will be used:
netgen_block_manager:
block_definitions:
list:
view_types:
list_numbered:
name: 'List (numbered)'
view:
block_view:
default:
list\numbered:
template: "@App/block/list/list_numbered.html.twig"
match:
block\definition: list
block\view_type: list_numbered
api:
list\numbered:
template: "@NetgenBlockManager/api/block/list/list.html.twig"
match:
block\definition: list
block\view_type: list_numbered
Note that template rule for backend (api
context) reuses the template for
list view type already available in the list
block. This is to make sure
that both list
and list_numbered
view types are displayed in the same
way in the backend. This is of course optional as you can specify your own
template for the backend.
Adding a custom item view type¶
You can also add custom item view types to every existing block and its view types.
The following configuration shows an example how to add a custom item view type
called standard_with_intro
to already existing block view type in list
block definition:
netgen_block_manager:
block_definitions:
list:
view_types:
list:
item_view_types:
standard_with_intro:
name: 'Standard (with intro)'
You can specified the template to be used like this:
netgen_block_manager:
view:
item_view:
default:
my_item\standard_with_intro:
template: "@App/items/my_item/standard_with_intro.html.twig"
match:
item\value_type: my_item
item\view_type: standard_with_intro
api:
my_item\standard_with_intro:
template: "@App/items/api/my_item/standard_with_intro.html.twig"
match:
item\value_type: my_item
item\view_type: standard_with_intro
Tip
If using Netgen Layouts in eZ Platform, every content view type is also a
valid item view type. Because of that, you don’t need to duplicate templates
from eZ Platform in Netgen Layouts to display eZ locations. For example, if
you have a listitem
content view type for your content, you can use it
just by specifying that some of your block view types have a listitem
item view type and selecting in the block configuration in the right sidebar.
Note
In case of eZ Platform, it is always a good idea to specify a fallback content view template that will be applied to all content types, since Netgen Layouts does not limit which eZ content types can be added to blocks.
Disabling existing view types in blocks¶
You can disable any existing block view types or item view types to stop them from showing up in Block Manager app.
The following configuration shows an example how to disable a block view type:
netgen_block_manager:
block_definitions:
list:
view_types:
some_view_type:
enabled: false
The following configuration shows an example how to disable an item view type:
netgen_block_manager:
block_definitions:
list:
view_types:
list:
item_view_types:
some_item_view_type:
enabled: false
Note that when you disable a block view type or an item view type, they will still be used by the rendering engine. However, you will not be able to save the block configuration any more in Block Manager app until you change the (item) view type to some other enabled one.
Creating custom query types¶
When installed on pure Symfony, Netgen Layouts comes with no built in query types which means you can only use manual collections in your blocks.
Tip
If you install Netgen Layouts on eZ Platform, you will get a single query type that will be automatically used when switching a collection from manual to dynamic in blocks.
When you implement your own query types, you will be able to use dynamic collections, and if you implement more than one query type, you will be able to select which query type to use when switching to dynamic collection in a block.
To implement a query type, you need a bit of configuration and a PHP class that will handle custom functionalities of a query type. In the following examples, we will show creating a custom query type that will use eZ search engine to search for items by text.
Configuring a new query type¶
To register a new query type in Netgen Layouts, you will need the following configuration:
netgen_block_manager:
query_types:
my_search:
name: 'My search'
This configuration specifies a new query type with my_search
identifier and
My search
human readable name.
Creating a PHP service for a query type¶
Every query type needs a single PHP class that specifies the entire behaviour of
a query type. This class needs to implement
Netgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface
interface
which specifies a number of methods for you to implement.
Let’s create a basic query type handler class:
<?php
namespace AppBundle\Collection\QueryType\Handler;
use Netgen\BlockManager\API\Values\Collection\Query;
use Netgen\BlockManager\Collection\QueryType\QueryTypeHandlerInterface;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;
class MySearchHandler implements QueryTypeHandlerInterface
{
/**
* Builds the parameters by using provided parameter builder.
*
* @param \Netgen\BlockManager\Parameters\ParameterBuilderInterface $builder
*/
public function buildParameters(ParameterBuilderInterface $builder)
{
}
/**
* Returns the values from the query.
*
* @param \Netgen\BlockManager\API\Values\Collection\Query $query
* @param int $offset
* @param int $limit
*
* @return mixed[]
*/
public function getValues(Query $query, $offset = 0, $limit = null)
{
}
/**
* Returns the value count from the query.
*
* @param \Netgen\BlockManager\API\Values\Collection\Query $query
*
* @return int
*/
public function getCount(Query $query)
{
}
/**
* Returns the limit internal to this query.
*
* @param \Netgen\BlockManager\API\Values\Collection\Query $query
*
* @return int
*/
public function getInternalLimit(Query $query)
{
}
/**
* Returns if the provided query is dependent on a context, i.e. current request.
*
* @param \Netgen\BlockManager\API\Values\Collection\Query $query
*
* @return bool
*/
public function isContextual(Query $query)
{
}
}
Specifying query type parameters¶
First method we will look at is buildParameters
method. By using an object
called parameter builder and adding parameter specifications to it, this method
will specify which parameters your custom query type will have. Details on how
the parameter builder works, what parameter types exist and how to implement
custom parameter type are explained in dedicated chapter.
Let’s add a couple of custom parameters to our query type which will serve as an input for search text:
use Netgen\BlockManager\Parameters\ParameterType;
public function buildParameters(ParameterBuilderInterface $builder)
{
$builder->add('search_text', ParameterType\TextType::class);
$builder->add('limit', ParameterType\IntegerType::class);
}
Notice that we didn’t specify the human readable labels for the parameters.
That’s because they are generated automatically via translation system. To
create the correct labels for your query type parameters, you need to add one
string to ngbm
translation catalog for every parameter in your query type
with the format query.<query_type>.<parameter_name>
where query_type
and
parameter_name
are placeholders that need to be replaced with correct values.
So, for our custom search query type, the translation file would look something like this:
query.my_search.search_text: 'Search text'
query.my_search.limit: 'Limit'
Fetching the items¶
Second method in our handler example above is called getValues
. This method
is used for fetching the items from a query.
This method needs to return the array of domain objects that will be automatically converted to block items.
Tip
In case of eZ Platform, query types can return the list of eZ ContentInfo
or Location
value objects.
/**
* @const int
*/
const DEFAULT_LIMIT = 25;
/**
* @var \eZ\Publish\API\Repository\SearchService
*/
protected $searchService;
public function __construct(SearchService $searchService)
{
$this->searchService = $searchService;
}
public function getValues(Query $query, $offset = 0, $limit = null)
{
$searchResult = $this->searchService->findLocations(
$this->buildQuery($query)
);
return array_map(
function (SearchHit $searchHit) {
return $searchHit->valueObject;
},
$searchResult->searchHits
);
}
public function getInternalLimit(Query $query)
{
$limit = $query->getParameter('limit')->getValue();
if (!is_int($limit)) {
return self::DEFAULT_LIMIT;
}
return $limit >= 0 ? $limit : self::DEFAULT_LIMIT;
}
/**
* Builds the query from current parameters.
*
* @param \Netgen\BlockManager\API\Values\Collection\Query $query
* @param bool $buildCountQuery
*
* @return \eZ\Publish\API\Repository\Values\Content\LocationQuery
*/
protected function buildQuery(Query $query, $buildCountQuery = false)
{
$locationQuery = new LocationQuery();
$criteria = array(
new Criterion\FullText($query->getParameter('search_text')->getValue()),
new Criterion\Visibility(Criterion\Visibility::VISIBLE),
);
$locationQuery->filter = new Criterion\LogicalAnd($criteria);
$locationQuery->limit = 0;
if (!$buildCountQuery) {
$locationQuery->limit = $this->getInternalLimit($query);
}
return $locationQuery;
}
As you can see, getValues
method simply builds a location query for eZ
search engine and returns the list of found eZ locations. Conversion to block
items is handled automatically by Netgen Layouts.
Note
Notice that we didn’t use $offset
and $limit
parameters which were
provided to getValues method. These parameters are provided as a
placeholder for future updates and are currently unused by the system and
will always be equal to their default values.
Fetching the item count¶
To retrieve the item count from the query type, we use the getCount
method:
public function getCount(Query $query)
{
$searchResult = $this->searchService->findLocations(
$this->buildQuery($query, true)
);
return $searchResult->totalCount;
}
Contextual queries¶
Notice how we implemented above a method called getInternalLimit
to
calculate the correct limit which will be applied to eZ search query. While it
can obviously be used by the query type handler itself, the purpose of this
method is to signal to the system how much items the collection would have if it
was ran. This information is used when displaying a block in Block Manager app
whose collection is a contextual one.
A contextual query is a query which needs the current context (i.e. current request) to run. Think of a situation where you have a layout with a block which shows top 5 items from the category it is applied to. Contextual query removes the need to create five different layouts for five different categories just so you can change the parent category from which to fetch the items. Instead, in a contextual query, you will take the currently displayed item and use it as the parent, making it possible to have only one layout for all five different categories.
In order for the system to work properly with contextual queries, two methods are used:
isContextual
method, which signals to the system if the query is contextual or not. Most of the time, this method will return a value of a boolean parameter specified inside of the query which decides if a query is contextual or not, for example:public function isContextual(Query $query) { return $query->getParameter('use_current_location')->getValue() === true; }
already described
getInternalLimit
method, used by the block with a contextual query so it can display a preview of how the block would look like.
In our case, we will simply return false
from isContextual
method:
public function isContextual(Query $query)
{
return false;
}
Defining the Symfony service for our handler¶
To connect the created handler with query type configuration, we need to register the handler in Symfony DIC:
services:
app.collection.query_type.handler.my_search:
class: AppBundle\Collection\QueryType\Handler\MySearchHandler
arguments:
- "@ezpublish.api.service.search"
tags:
- { name: netgen_block_manager.collection.query_type_handler, type: my_search }
This configuration is a fairly regular specification of services in Symfony,
however, to correctly recognize our PHP class as a query type handler, we need
to tag it with netgen_block_manager.collection.query_type_handler
tag and
attach to it an type
key with a value which equals to the identifier of
query type we configured at the beginning (in this case my_search
).
After this, our query type is ready for usage.
Creating custom parameter types¶
Netgen Layouts ships with around 15 parameter types which you can use in your custom blocks and queries and which fulfill most of the everyday usecases.
However, if you need custom validation on some of your parameters, or custom display in Symfony forms, you need to create a custom parameter type.
We will show how to create a custom parameter type on an example of a parameter which stores and validates dates in Google Analytics format.
There are two parts to implementing a parameter type in Netgen Layouts:
- Implementing a main parameter type service which deals with parameter value conversion and validation
- Implementing a form mapper which takes care of creating a Symfony form for the parameter type
Implementing the parameter type service¶
Parameter type service needs to implement
Netgen\BlockManager\Parameters\ParameterTypeInterface
. Since this interface
has a handful of methods, there is a handy abstract class from which you can
extend so you don’t have to write boilerplate code in your parameter types.
This cuts down the number of required methods to implement to 2. Let’s create an empty class extending the abstract class:
<?php
namespace AppBundle\Parameters\ParameterType;
use Netgen\BlockManager\Parameters\ParameterInterface;
use Netgen\BlockManager\Parameters\ParameterType;
class GoogleAnalyticsDateType extends ParameterType
{
/**
* Returns the parameter type identifier.
*
* @return string
*/
public function getIdentifier()
{
}
/**
* Returns constraints that will be used to validate the parameter value.
*
* @param \Netgen\BlockManager\Parameters\ParameterInterface $parameter
* @param mixed $value
*
* @return \Symfony\Component\Validator\Constraint[]
*/
protected function getValueConstraints(ParameterInterface $parameter, $value)
{
}
}
As you can see, only two methods need to be implemented for basic parameter type
to work: getIdentifier
, which should return a unique identifier of a
parameter type, and getValueConstraints
, which should return an array of
Symfony validator constraints which validate the value.
Note
Every parameter type needs to allow null
as its value, since parameters
are by default optional in blocks and queries. Because of that, take care
not to include constraints which validate that the value is not null
,
like NotNull
or NotBlank
.
To specify constraints when the parameter is required, you can use the
getRequiredConstraints
method. It already has a default implementation
which you can override to suit your needs.
Let’s implement getIdentifier
and getValueConstraints
methods:
/**
* Returns the parameter type identifier.
*
* @return string
*/
public function getIdentifier()
{
return 'ga_date';
}
/**
* Returns constraints that will be used to validate the parameter value.
*
* @param \Netgen\BlockManager\Parameters\ParameterInterface $parameter
* @param mixed $value
*
* @return \Symfony\Component\Validator\Constraint[]
*/
protected function getValueConstraints(ParameterInterface $parameter, $value)
{
return array(
new Constraints\Type(array('type' => 'string'))
);
}
With the above implementation, we specified that the unique identifier of our
parameter type is ga_date
and that the value of the parameter should be a
string.
This is a good time to add any custom validations you want, for example, you can implement a validator and a constraint that validates the date as a Google Analytics date.
The purpose of other methods in ParameterTypeInterface
is detailed below:
configureOptions
This method uses the Symfony OptionsResolver component to specify any custom options your parameter type might have. Here, you can use the full power of the component to define required and optional options, custom validation and so on. For example, you might specify the minimum year your parameter accepts and then use the option in
getValueConstraints
method to modify the value constraints accordingly. An example implementation might look like this:public function configureOptions(OptionsResolver $optionsResolver) { $optionsResolver->setDefault('min_year', null); $optionsResolver->setRequired(array('min_year')); $optionsResolver->setAllowedTypes('min_year', array('int', 'null')); }
getConstraints
This method by default takes the constraints fromgetValueConstraints
andgetRequiredConstraints
method, and merges them together. Notice that this method has public visibility, whilegetValueConstraints
andgetRequiredConstraints
are protected. This means that this method is the one used by Netgen Layouts when validating the parameter value and if you override it, you will effectively override any constraints implemented in the two protected methods.
toHash
This method is responsible for converting the parameter value to a hash format (scalar or an array of scalars). Since every parameter value is stored in the database encoded into JSON, this method must not return any data that cannot be safely encoded into JSON. Default implementation does not convert the value and simply returns it as is.
fromHash
This method does the opposite oftoHash
method. That is, it converts the JSON decoded data stored in the database to a value that will be used by the rest of Netgen Layouts code as well as your custom code. This can be anything really: a scalar, an array, on object or a whole object graph. Default implementation does not convert the value and simply returns it as is.
createValueFromInput
This method is used if you wish to provide a couple of ways for your users to specify the value of a parameter when creating a block or a query. For example, this method can be used to allow the users to provide a date as a string as well as an instance of aDateTime
class. By default, this method forwards the call tofromHash
method, which means users can provide the value of the parameter in the format which is equal to the format stored in the database.
isValueEmpty
This method is used to signal to the system when the value of the parameter is considered empty. For example, a date can be empty if the value of the parameter isnull
or an empty string. By default, this method usesempty
PHP language construct to check emptiness of the value.
Implementing the form mapper¶
Form mapper object is responsible for specifying how the parameter will look
like on a Symfony form. The interface
Netgen\BlockManager\Parameters\Form\MapperInterface
provides three methods
for you to implement. There is also an abstract class which you can extend to
ease the implementation, so you need to implement only one method.
Basic form mapper needs to only specify which Symfony form type to use:
<?php
namespace AppBundle\Parameters\FormMapper;
use Netgen\BlockManager\Parameters\Form\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class GoogleAnalyticsDateMapper extends Mapper
{
/**
* Returns the form type for the parameter.
*
* @return string
*/
public function getFormType()
{
return TextType::class;
}
}
The purpose of other methods in MapperInterface
is detailed below:
mapOptions
If your parameter type has custom options which need to be forwarded to Symfony form type, you can use this method to do so. For example, if you implemented a custom Symfony form type for your Google Analytics date, you could transfer yourmin_year
option to the Symfony form, so it does not allow specifying any year lower than what is defined in your option.
handleForm
This method is a generic method which receives the form built from the information in
getFormType
andmapOptions
methods and makes it possible to do anything you wish with the form, like attaching custom data mappers or data transformers, adding event listeners to the form and so on.Basically, anything you can do in Symfony form type class with a form field, you can do here too.
Registering the Symfony services¶
To activate both the parameter type and the form mapper, you need to specify them as Symfony services.
Parameter type service needs to have a
netgen_block_manager.parameters.parameter_type
tag in its service
definition, while the form mapper needs to have a
netgen_block_manager.parameters.form.mapper
tag, together with the type
attribute whose value is equal to the parameter type identifier.
Our parameter type and form mapper service definitions should look like this:
services:
app.parameters.parameter_type.ga_date:
class: AppBundle\Parameters\ParameterType\GoogleAnalyticsDateType
tags:
- { name: netgen_block_manager.parameters.parameter_type }
app.parameters.form.mapper.ga_date:
class: AppBundle\Parameters\FormMapper\GoogleAnalyticsDateMapper
tags:
- { name: netgen_block_manager.parameters.form.mapper, type: ga_date }
Creating custom value types¶
In Netgen Layouts, (block) item is a generic concept in a way that blocks do not (and should not) care what kind of items are put inside the blocks. To achieve this, a block item is a wrapper around a value object which comes from your CMS. For example, in eZ Platform integration, Netgen Layouts supports two types of values: eZ location and eZ content.
To be able to create block items from your own domain objects, you need to create and register your own custom value type. Value type has three purposes:
- Loading of your domain object from the CMS by its ID by using a value loader
- Handling the domain objects provided by your custom query types by using a value converter
- Generating the URL to the domain object by using a value URL builder
Registering a new value type¶
To be able to use your domain objects inside Netgen Layouts as block items, you need to register a new value type in the configuration. To do so, you need to provide a unique identifier for your value type and a human readable name:
netgen_block_manager:
items:
value_types:
my_value_type:
name: 'My value type'
This configuration registers a new value type in the system with
my_value_type
identifier.
Implementing a value loader¶
A value loader is an object responsible for loading your domain object by its
ID. It is an implementation of Netgen\BlockManager\Item\ValueLoaderInterface
which provides a single method called load
. This method takes the ID of the
domain object and should simply return the object once loaded or throw an
exception if the object could not be loaded.
For example:
<?php
namespace AppBundle\Item\ValueLoader;
use Netgen\BlockManager\Item\ValueLoaderInterface;
class MyValueTypeLoader implements ValueLoaderInterface
{
/**
* Loads the value from provided ID.
*
* @param int|string $id
*
* @throws \Netgen\BlockManager\Exception\Item\ItemException If value cannot be loaded
*
* @return mixed
*/
public function load($id)
{
try {
return $this->myBackend->loadMyObject($id);
} catch (Exception $e) {
throw new ItemException(
sprintf('Object with ID "%s" could not be loaded.', $id),
0,
$e
);
}
}
}
Once implemented, you need to register the loader in Symfony DI container:
app.block_manager.value_loader.my_value_type:
class: AppBundle\Item\ValueLoader\MyValueTypeLoader
tags:
- { name: netgen_block_manager.item.value_loader, value_type: my_value_type }
Notice that the service is tagged with netgen_block_manager.item.value_loader
DI tag which has a value_type
attribute. This attribute needs to have a
value equal to your value type identifier.
Implementing Content Browser support¶
To be able to actually select the items from the CMS and add them to your blocks, you also need to implement a Netgen Content Browser backend.
To automatically recognize which backend is responsible for which value types, you need to make sure that the identifier of the item in the Netgen Content Browser backend you implemented is the same as the identifier of the value type you configured above.
Implementing a value converter¶
As you’re probably aware, query types need not worry themselves about returning PHP objects specific to Netgen Layouts to work. Instead, they simply return domain objects which are then converted by Netgen Layouts into block items.
Converting the domain objects to Netgen Layouts items is done through so called
value converters and every value type needs to have a value converter
implemented. Value converter should implement
Netgen\BlockManager\Item\ValueConverterInterface
, which provides methods
that return the data used by Netgen Layouts to work with block items, like the
ID of the object, name and if the object is considered visible in your CMS.
Method supports
should return if the value converter supports the given
object. Usually, you will check if the provided object is of correct interface.
This makes it possible to handle different types of value objects in the same
value converter. For example, in eZ Platform, Content
and ContentInfo
are two different objects that represent the same piece of content in the CMS,
but with different usecases in mind.
Method getValueType
should simply return the identifier of the value type
you choose when activating the value type in the configuration.
An example implementation of a value converter might look something like this:
<?php
namespace AppBundle\Item\ValueConverter;
use App\MyValue;
use Netgen\BlockManager\Item\ValueConverterInterface;
class MyValueTypeConverter implements ValueConverterInterface
{
/**
* Returns if the converter supports the object.
*
* @param mixed $object
*
* @return bool
*/
public function supports($object)
{
return $object instanceof MyValue;
}
/**
* Returns the value type for this object.
*
* @param mixed $object
*
* @return string
*/
public function getValueType($object)
{
return 'my_value_type';
}
/**
* Returns the object ID.
*
* @param \App\MyValue $object
*
* @return int|string
*/
public function getId($object)
{
return $object->id;
}
/**
* Returns the object name.
*
* @param \App\MyValue $object
*
* @return string
*/
public function getName($object)
{
return $object->name;
}
/**
* Returns if the object is visible.
*
* @param \App\MyValue $object
*
* @return bool
*/
public function getIsVisible($object)
{
return $object->isVisible();
}
}
Once implemented, you need to register the converter in Symfony DI container and
tag it with netgen_block_manager.item.value_converter
tag:
app.block_manager.value_converter.my_value_type_content:
class: AppBundle\Item\ValueConverter\MyValueTypeConverter
tags:
- { name: netgen_block_manager.item.value_converter }
Implementing a value URL builder¶
To generate the links to your domain objects in your blocks, you can use
ngbm_item_path
Twig function in your Twig templates. This function
internally forwards the URL generation to the correct value URL builder based
on the value type of the item. To generate the URL for your value type, simply
implement the Netgen\BlockManager\Item\ValueUrlBuilderInterface
, which
provides a single method called getUrl
responsible to generate the URL.
Note
getUrl
method should return the full path to the item, including the
starting slash, not just a slug.
An example implementation might use the Symfony router and generate the URL based on the object ID:
<?php
namespace AppBundle\Item\ValueUrlBuilder;
use Netgen\BlockManager\Item\ValueUrlBuilderInterface;
class MyValueTypeUrlBuilder implements ValueUrlBuilderInterface
{
/**
* Returns the object URL. Take note that this is not a slug,
* but a full path, i.e. starting with /.
*
* @param mixed $object
*
* @return string
*/
public function getUrl($object)
{
return $this->router->generate(
'my_custom_route',
array(
'id' => $object->id,
)
);
}
}
Once implemented, you need to register the URL builder in Symfony DI container:
app.block_manager.value_url_builder.my_value_type:
class: AppBundle\Item\ValueUrlBuilder\MyValueTypeUrlBuilder
tags:
- { name: netgen_block_manager.item.value_url_builder, value_type: my_value_type }
Notice that the service is tagged with
netgen_block_manager.item.value_url_builder
DI tag which has a
value_type
attribute. This attribute needs to have a value equal to your
value type identifier.
Implementing item templates¶
Once a custom value type is implemented, it’s time to implement Twig templates that will be used to render the item that holds the value.
Just like with block templates, for rendering an item, you need to implement two templates, one for backend (Block Manager app) and one for frontend.
Implementing a backend template¶
A backend template, or rather, template for Block Manager app is simple. It
receives the item in question in item
variable and can be used to render
the item name and item image. The basic structure of the template looks like
this:
<div class="image">
<img src="/path/to/image.jpg" />
</div>
<div class="name">
<p><a href="{{ ngbm_item_path(item) }}" target="_blank" rel="noopener noreferrer">{{ item.name }}</a></p>
</div>
Rendering an item name and URL works for all items, as long as you implemented proper value URL builders and converters. Rendering an image is left for you, as often it requires additional steps in contrast to just outputting the image path.
Registering the backend template is done via the view config:
netgen_block_manager:
view:
item_view:
api:
my_value:
template: "@App/api/item/view/my_value.html.twig"
match:
item\value_type: my_value
Implementing a frontend template¶
Just as with the backend template, frontend template receives the item in
question via item
variable. Frontend templates depend on your design, so
there’s little sense in providing an example implementation, but once you
implement your frontend template, you can register it with:
netgen_block_manager:
view:
item_view:
default:
my_value:
template: "@App/item/view/my_value.html.twig"
match:
item\value_type: my_value
Creating custom target types¶
Netgen Layouts is shipped with a number of target types to which you can map your layouts to be used in a layout resolving process. These target types are generic, allowing you to attach a layout to a request URI, a Symfony route or their prefixes. In most cases, when working with pure Symfony apps, this is enough.
However, if you wish that your layouts can be mapped to domain objects from your CMS directly, you can create custom target types.
Note
Custom target type can be whatever comes to mind, not only a domain object from your CMS.
Warning
Custom target types are one of the most complicated extension points in Netgen Layouts and creating a custom target type involves creating configuration, templates, translations and quite a bit of code.
Implementing the target type classes¶
Implementing a target type in PHP requires creating at least three Symfony services:
- Target type itself
- Symfony form mapper
- A target handler for every database engine supported in Netgen Layouts
Creating a target type¶
A target type is a PHP class implementing
Netgen\BlockManager\Layout\Resolver\TargetTypeInterface
. The purpose of this
class is twofold:
- Provide Symfony constraints that validate the value of the target when adding it to a mapping
- Extract the value of the target from the request to be used in layout resolving process
The first point is achieved by implementing getConstraints
method, which
should return the array of Symfony validator constraints which should validate
the value. For example, in eZ location target type, these constraints validate
that the ID of the location is a number larger than 0 and that the location with
the provided ID actually exists:
public function getConstraints()
{
return array(
new Constraints\NotBlank(),
new Constraints\Type(array('type' => 'numeric')),
new Constraints\GreaterThan(array('value' => 0)),
new EzConstraints\Location(),
);
}
The second point is achieved by implementing the provideValue
method. This
method takes a request object and should return a value of your target type if
it exists in the request or null
if it doesn’t. For example, eZ location
target type extracts the location from provided request and returns its ID:
public function provideValue(Request $request)
{
$location = $this->contentExtractor->extractLocation($request);
return $location instanceof APILocation ? $location->id : null;
}
The one method that remains to be implemented is the getType
method, which
should return a unique identifier of the target type.
Once this is done, we need to register the target type in the Symfony DIC with
the netgen_block_manager.layout.resolver.target_type
tag:
app.target_type.my_target:
class: AppBundle\Layout\Resolver\TargetType\MyTarget
tags:
- { name: netgen_block_manager.layout.resolver.target_type }
Tip
You can add a priority
attribute to the tag, which allows you to make
your target type considered before others when deciding if the current
request matches one of the targets.
Creating the form mapper¶
To be able to add the target to a mapping or edit the value of an existing
target, you need to provide a form mapper which provides data for generating
Symfony form for your target type. The mapper needs to implement
Netgen\BlockManager\Layout\Resolver\Form\TargetType\MapperInterface
and
there’s also a handy abstract class which you can extend to cut down the number
of methods to define to one: getFormType
, which returns which Symfony form
type should be used to edit the target:
<?php
namespace AppBundle\Layout\Resolver\Form\TargetType\Mapper;
use Netgen\BlockManager\Layout\Resolver\Form\TargetType\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class MyTarget extends Mapper
{
/**
* Returns the form type that will be used to edit the value of this condition type.
*
* @return string
*/
public function getFormType()
{
return TextType::class;
}
}
There are two other methods in the interface:
mapOptions
which makes it possible to provide custom options to the form typehandleForm
which allows you to customize the form in any way you see fit
Finally, you need to register the mapper in the Symfony container with the correct tag and the identifier of the target type:
app.layout.resolver.form.target_type.mapper.my_target:
class: AppBundle\Layout\Resolver\Form\TargetType\Mapper\MyTarget
tags:
- { name: netgen_block_manager.layout.resolver.form.target_type.mapper, target_type: my_target }
Creating target handlers for the database engine¶
Matching the target value from the request to the value stored in the database is done in the database itself. This means that you need to provide a so called target handler for every database engine supported in Netgen Layouts.
The only supported database engine is called “doctrine”, since it uses Doctrine library to communicate with the database.
This target handler needs to implement
Netgen\BlockManager\Persistence\Doctrine\QueryHandler\LayoutResolver\TargetHandler
interface which provides a single method called handleQuery
which takes the
Doctrine query object and the target value and should modify the query in way to
match the provided value.
Stored target value can be accessed in the query with rt.value
so to match a
simple integer, you would implement it like this:
/**
* Handles the query by adding the clause that matches the provided target values.
*
* @param \Doctrine\DBAL\Query\QueryBuilder $query
* @param mixed $value
*/
public function handleQuery(QueryBuilder $query, $value)
{
$query->andWhere(
$query->expr()->in('rt.value', array(':target_value'))
)
->setParameter('target_value', $value, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
}
Finally, the target handler needs to registered in the Symfony container with the correct tag and target type identifier:
app.layout_resolver.target_handler.doctrine.my_target:
class: AppBundle\LayoutResolver\TargetHandler\Doctrine\MyTarget
tags:
- { name: netgen_block_manager.persistence.doctrine.layout_resolver.query_handler.target_handler, target_type: my_target }
Implementing the target type template¶
Target type uses a single template in the value
view context of the
Netgen Layouts view layer to display the value of the target in the admin
interface. Since the target itself usually provides only the scalar identifier
as its value, this template usually needs some logic to display the name of the
target (from your CMS for example). In case of eZ Platform, these templates for
example use Twig functions to load the content and location objects and return
their names and paths:
{% set content_name = ngbm_ezcontent_name(target.value) %}
{{ content_name != null ? content_name : '(INVALID CONTENT)' }}
To register the template in the system, the following configuration is needed
(make sure to use the value
view context):
netgen_block_manager:
view:
rule_target_view:
value:
my_target:
template: "@App/layout_resolver/target/value/my_target.html.twig"
match:
rule_target\type: my_target
Target type translations¶
Each target type uses two translation strings, one in ngbm
and one in
ngbm_admin
catalog. The first one is a generic string which should provide
a human readable name of the target type and should be in the
layout_resolver.target.<target_type_identifier>
format:
The second one is used as a label in administration of interface which states
for which target types is the mapping used and should be in
layout_resolver.rule.target_header.<target_type_identifier>
format:
Creating custom condition types¶
Netgen Layouts is shipped with a couple of condition types which you can use to limit your layout mappings to certain conditions. One example condition type which is built into Netgen Layouts is the query parameter condition type which enables the mapping if some query parameter from the request matches the value stored in the condition. In case of eZ Platform integration, an example condition type which is built in is the siteaccess condition type that activates the mapping only if the siteaccess of the current request matches the condition.
If you wish to map your layouts to targets in some other conditions, like time of day, location of the user, IP address, you name it…, you can create your own condition types.
Warning
Custom condition types are one of the most complicated extension points in Netgen Layouts and creating a custom condition type involves creating configuration, templates, translations and quite a bit of code.
Implementing the condition type classes¶
Implementing a condition type in PHP requires creating two Symfony services:
- Condition type itself
- Symfony form mapper
Creating a condition type¶
A condition type is a PHP class implementing
Netgen\BlockManager\Layout\Resolver\ConditionTypeInterface
. The purpose of
this class is twofold:
- Provide Symfony constraints that validate the value of the condition when adding it to a mapping
- Deciding if the current request matches the provided value of the condition
The first point is achieved by implementing getConstraints
method, which
should return the array of Symfony validator constraints which should validate
the value. For example, in eZ siteaccess condition type, these constraints
validate that all selected siteaccesses are non empty strings and that they
actually exist:
public function getConstraints()
{
return array(
new Constraints\NotBlank(),
new Constraints\Type(array('type' => 'array')),
new Constraints\All(
array(
'constraints' => array(
new Constraints\Type(array('type' => 'string')),
new EzConstraints\SiteAccess(),
),
)
),
);
}
The second point is achieved by implementing the matches
method. This method
takes a request object and based on the data from the request decides if it
matches the provided value. For example, the matches
method of the
eZ siteaccess condition type returns true only if the siteaccess is provided in
the request and is equal to one of the stored values of the condition:
public function matches(Request $request, $value)
{
$siteAccess = $request->attributes->get('siteaccess');
if (!$siteAccess instanceof SiteAccess) {
return false;
}
if (!is_array($value) || empty($value)) {
return false;
}
return in_array($siteAccess->name, $value, true);
}
The one method that remains to be implemented is the getType
method, which
should return a unique identifier of the condition type.
Once this is done, we need to register the condition type in the Symfony DIC
with the netgen_block_manager.layout.resolver.condition_type
tag:
app.condition_type.my_condition:
class: AppBundle\Layout\Resolver\ConditionType\MyCondition
tags:
- { name: netgen_block_manager.layout.resolver.condition_type }
Creating the form mapper¶
To be able to add the condition to a mapping or edit the value of an existing
condition, you need to provide a form mapper which provides data for generating
Symfony form for your condition type. The mapper needs to implement
Netgen\BlockManager\Layout\Resolver\Form\ConditionType\MapperInterface
and
there’s also a handy abstract class which you can extend to cut down the number
of methods to define to one: getFormType
, which returns which Symfony form
type should be used to edit the condition:
<?php
namespace AppBundle\Layout\Resolver\Form\ConditionType\Mapper;
use Netgen\BlockManager\Layout\Resolver\Form\ConditionType\Mapper;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class MyCondition extends Mapper
{
/**
* Returns the form type that will be used to edit the value of this condition type.
*
* @return string
*/
public function getFormType()
{
return TextType::class;
}
}
There are two other methods in the interface:
mapOptions
which makes it possible to provide custom options to the form typehandleForm
which allows you to customize the form in any way you see fit
Finally, you need to register the mapper in the Symfony container with the correct tag and the identifier of the condition type:
app.layout.resolver.form.condition_type.mapper.my_condition:
class: AppBundle\Layout\Resolver\Form\ConditionType\Mapper\MyCondition
tags:
- { name: netgen_block_manager.layout.resolver.form.condition_type.mapper, condition_type: my_condition }
Implementing the condition type template¶
Condition type uses a single template in the value
view context of the
Netgen Layouts view layer to display the value of the condition in the admin
interface. Since the condition itself usually provides only the scalar
identifier as its value, this template usually needs some logic to display the
human readable value of the condition. For example, content type condition from
eZ Platform uses custom Twig functions to display content type names instead of
the identifiers:
{% set content_type_names = [] %}
{% for value in condition.value %}
{% set content_type_names = content_type_names|merge([ngbm_ez_content_type_name(value)]) %}
{% endfor %}
{{ content_type_names|join(', ') }}
To register the template in the system, the following configuration is needed
(make sure to use the value
view context):
netgen_block_manager:
view:
rule_condition_view:
value:
my_condition:
template: "@App/layout_resolver/condition/value/my_condition.html.twig"
match:
rule_condition\type: my_condition
Condition type translations¶
Each condition type uses one translation string in the ngbm
catalog. This is
a generic string which should provide a human readable name of the condition
type and should be in the
layout_resolver.condition.<condition_type_identifier>
format:
Adding parameters to existing blocks¶
Currently, there is no straightforward and official way to add parameters to existing blocks in Netgen Layouts.
However, if really needed, you can always override the class for an existing block definition handler by using a compiler pass in Symfony, or redefining the Symfony service for the handler. Overriden class can then extend from the original handler class and add new parameters or modify the existing ones.
For example, to add a new parameter to a Title block, you would create a new class which extends from the original one:
<?php
namespace AppBundle\Block\BlockDefinition\Handler;
use Netgen\BlockManager\Block\BlockDefinition\Handler\TitleHandler as BaseTitleHandler;
use Netgen\BlockManager\Parameters\ParameterBuilderInterface;
use Netgen\BlockManager\Parameters\ParameterType\TextLineType;
class TitleHandler extends BaseTitleHandler
{
public function buildParameters(ParameterBuilderInterface $builder)
{
parent::buildParameters($builder);
$builder->add('my_param', TextLineType::class);
}
}
You can ofcourse override the getDynamicParameters
method too if needed:
public function getDynamicParameters(Block $block)
{
$params = parent::getDynamicParameters($block);
$params['my_dynamic_param'] = 42;
return $params;
}
After that, you need to create a compiler pass which sets the new class for the handler:
<?php
namespace AppBundle\DependencyInjection\CompilerPass;
use AppBundle\Block\BlockDefinition\Handler\TitleHandler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class TitleHandlerPass implements CompilerPassInterface
{
const SERVICE_NAME = 'netgen_block_manager.block.block_definition.handler.title';
public function process(ContainerBuilder $container)
{
if (!$container->has(static::SERVICE_NAME)) {
return;
}
$container
->findDefinition(static::SERVICE_NAME)
->setClass(TitleHandler::class);
}
}
And finally, you need to register the compiler pass in your bundle class:
<?php
namespace AppBundle;
use AppBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new CompilerPass\TitleHandlerPass());
}
}
- Creating a custom layout type
- Creating a custom block
- Creating a custom container block
- Adding collections to blocks
- Adding custom view types to blocks
- Creating custom query types
- Creating custom parameter types
- Creating custom value types
- Creating custom target types
- Creating custom condition types
- Adding parameters to existing blocks
- Creating a custom layout type
- Creating a custom block
- Creating a custom container block
- Adding collections to blocks
- Adding custom view types to blocks
- Creating custom query types
- Creating custom parameter types
- Creating custom value types
- Creating custom target types
- Creating custom condition types
- Adding parameters to existing blocks
Tips & tricks¶
Tips & tricks¶
How to override existing templates¶
By using the view layer configuration in Netgen Layouts, it is possible to override any template for built in layout types, block view types and block item view types.
Warning
View layer works in a way that first configuration which matches the rules is used for rendering the entity, so take care to properly use Symfony configuration prepending to make sure your rules are matched before the built in ones.
Overriding layout type templates¶
You can override a template for any layout type (including built in ones) with the following example configuration:
netgen_block_manager:
view:
layout_view:
default:
layout_4_override:
template: "@App/layouts/layout_4.html.twig"
match:
layout\type: layout_4
This overrides the layout_4
layout type to use
@App/layouts/layout_4.html.twig
template.
Overriding block view type templates¶
You can override a template for any block view type (including built in ones) with the following example configuration:
netgen_block_manager:
view:
block_view:
default:
simple_title_override:
template: "@App/blocks/title/simple_title.html.twig"
match:
block\definition: title
block\view_type: simple_title
This overrides the simple_title
view type of the title
block definition
to use @App/blocks/title/simple_title.html.twig
template.
Overriding block item view type templates¶
You can override a template for any block item view type (including built in ones) with the following example configuration:
netgen_block_manager:
view:
item_view:
default:
my_item_standard_override:
template: "@App/items/my_item/standard.html.twig"
match:
item\value_type: my_item
item\view_type: standard
This overrides the standard
item view type of the my_item
value type to
use @App/items/my_item/standard.html.twig
template.
Tip
To override item view types for ezcontent
and ezlocation
items,
use the regular eZ Platform content view configuration instead of overriding
the templates in Netgen Layouts.
How to add icons for custom layout types¶
Once you add a custom layout type, you will notice that the icon is missing in Block Manager app and admin interface. This is due to fact that icons are loaded through CSS, rather than through configuration.
In order to add your own icon to Netgen Layouts, you will need to inject a piece of CSS into Netgen Layouts configuration for admin interface and Block Manager app.
Warning
Since CSS classes cannot start with a number, layout type identifiers should not start with a number.
Tip
Since layout type icons are used in several places in admin interface and Block Manager app in different sizes, it is recommended to create your own icons in SVG format, rather than PNG to allow the CSS to resize the icons.
If you wish to add an icon for a layout type with my_layout
identifier in
admin interface, you can use the following CSS:
.layout-icon.my_layout {
background-image: url(/path/to/icon/my_layout.svg);
}
If you wish to add an icon for a layout type with my_layout
identifier in
Block Manager app, you can use the following CSS:
[value="my_layout"] + label:before {
background-image: url(/path/to/icon/my_layout.svg);
}
After that, you need to register your CSS file in configuration:
netgen_block_manager:
admin:
stylesheets:
- '/path/to/bmadmin/style.css'
app:
stylesheets:
- '/path/to/bmapp/style.css'
How to add icons for custom block types¶
Once you add a custom block definition and corresponding block type, you will notice that the icon is missing in Block Manager app. This is due to fact that icons are loaded through CSS, rather than through configuration.
In order to add your own icon to Netgen Layouts, you will need to inject a piece of CSS into Netgen Layouts configuration for Block Manager app.
Warning
Since CSS classes cannot start with a number, block definition and block type identifiers should not start with a number.
If you wish to add an icon for a block type with my_block
identifier in
Block Manager app, you can use the following CSS:
.add-block-btn.icn-my_block .icon {
background-image: url(/path/to/icon/my_layout.svg);
}
After that, you need to register your CSS file in configuration:
netgen_block_manager:
app:
stylesheets:
- '/path/to/bmapp/style.css'
How to quickly render a layout on a single URL¶
Usually, if you wish to render a page with a layout in your CMS, you would need
to create a controller which renders a template which then extends from
ngbm.layoutTemplate
variable for layout to be resolved.
Even worse, you would need to create a page (article, blog post, landing page or something else) in the CMS just to make the URL available in the system.
Creating a Symfony controller and a page in your CMS just to render an URL with a layout can be an overkill, and in those cases, you can use a controller built into Symfony to quickly render a layout on a single URL.
First, you need to create a simple Twig template which you can reuse in cases like this:
{# @App/empty_page.html.twig #}
{% extends ngbm.layoutTemplate %}
After that, create a Symfony route which uses a built in Symfony controller to render the template:
my_cool_page:
path: /my/cool/page
defaults:
_controller: 'FrameworkBundle:Template:template'
template: '@App/empty_page.html.twig'
Finally, you need to add a mapping in Netgen Layouts administration to link one of the layouts to the route you created. Once this is done, you can access the URL you provided in your route config and see the layout rendered in all its glory.
How to inject current eZ location and content in blocks and query types¶
Note
This tutorial is eZ Platform/eZ Publish 5 specific.
Often, your custom block definitions and query types need the current
eZ Platform location or content to work. While you can extract the information
about the current location and content from the Request
object, the
differences between different versions of eZ kernel make it impossible to create
generic block definitions and query types that work on any eZ kernel
available.
That’s why there is a handy Symfony service called Content Provider in Netgen Layouts that can be used to retrieve the current location or content in a generic way. In background, this service uses different implementations of a service that extracts content and locations from the request for every eZ kernel version available.
Note
If using eZ Publish 5, you need to specify a Symfony service alias somewhere in your configuration which makes sure that an eZ 5 version of the extractor is used:
netgen_block_manager.ezpublish.content_extractor:
alias: netgen_block_manager.ezpublish.content_extractor.ez5_request
Service identifier of the Content Provider is
netgen_block_manager.ezpublish.content_provider
and you can inject it into
your block definitions and query types like any other service.
Content Provider exposes two methods: provideLocation
and provideContent
which return the location and content from the current request and null
if
no location or content exist.
As an example, you can use the Content Provider to inject the location and content objects into Twig templates for your blocks:
<?php
namespace AppBundle\Block\BlockDefinition\Handler;
use Netgen\BlockManager\API\Values\Block\Block;
use Netgen\BlockManager\Block\BlockDefinition\BlockDefinitionHandler;
use Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface;
class MyBlockHandler extends BlockDefinitionHandler
{
/**
* @var \Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface
*/
protected $contentProvider;
/**
* Constructor.
*
* @param \Netgen\BlockManager\Ez\ContentProvider\ContentProviderInterface $contentProvider
*/
public function __construct(ContentProviderInterface $contentProvider)
{
$this->contentProvider = $contentProvider;
}
/**
* Returns the array of dynamic parameters provided by this block definition.
*
* @param \Netgen\BlockManager\API\Values\Block\Block $block
*
* @return array
*/
public function getDynamicParameters(Block $block)
{
return array(
'content' => $this->contentProvider->provideContent(),
'location' => $this->contentProvider->provideLocation(),
);
}
}
How to override the templates for Grid block columns¶
Grid block type uses a nifty little trick to specify the templates for its 2, 3, 4 or 6 columns variants which makes it possible to override the template for only one of those variants without the need for overriding the entire grid template.
Basically, it uses a feature from the view layer which makes it possible to inject custom parameters to Twig templates straight from the match configuration rule. By default, if no parameter is provided that specifies the template to use, Grid block uses built in templates for each of the column variants.
As an example, the following configuration would override the template for 4 column variant of a Grid block:
netgen_block_manager:
view:
block_view:
default:
list\grid\override:
template: "@NetgenBlockManager/block/list/grid.html.twig"
parameters:
column_templates:
4: "@App/block/grid/4_columns.html.twig"
match:
block\definition: list
block\view_type: grid
Basically, this rule specifies that we want to use the original grid template
available in Netgen Layouts (@NetgenBlockManager/block/list/grid.html.twig
)
and to also provide a parameter called column_templates
which the original
grid template uses to extract the template names for column variants from.
We only specified that 4 columns template should be overriden by specifying the
key with a value of 4
, but we can easily add more overrides by specifying
more array keys in the column_templates
parameter.
How to specify default value for block parameters¶
As you probably know, you can create a new block in a layout by drag and dropping it from the left sidebar to the layout. This means that there is no way to specify the starting values for the parameters in the block. Instead, the default values provided in the block definition are used.
This is not a problem per se, but it does mean that in cases when you do need different starting values than those provided by the block definition, you need to add a block and immediately edit it in the right sidebar to change the parameter values to more suitable ones. This only slows down layout creation process.
To overcome this, you can specify default values for any block parameter in the configuration. For example, if you want to change the default number of columns of a Grid block, you would use a configuration like this one:
block_types:
grid:
defaults:
parameters:
number_of_columns: 6
From now on, every time you add a Grid block to your layout, it will be created with 6 columns.
How to inject items to blocks manually¶
Usually, block items are provided to blocks with a collection query. But,
sometimes it is useful to provide some specific block items to the block by hand
(meaning in block definition handler). This way, you could reuse block item
templates by calling the ngbm_render_item
function on those items, instead
of duplicating the block item template logic inside the block itself.
To achieve this, you can use a Symfony service from Netgen Layouts called item
builder which is an instance of
Netgen\BlockManager\Item\ItemBuilderInterface
. After that, you can just use
it in your getDynamicParameters
method of the block definition handlers to
build the items and provide them as dynamic parameters to the block.
Note
This service is used by the collection query result building process too, so you will be basically using the same principles Netgen Layouts uses when building items.
The ID of the item builder Symfony service is
netgen_block_manager.item.item_builder
and you can just inject it to your
block definition handlers as usual.
The service provides a single method called build
which receives a single
parameter, your domain object, which will then build the block item. For this to
work, you need to have value converters (instances of
Netgen\BlockManager\Item\ValueConverterInterface
) for any domain object you
wish to build items from.
For example, to build the item from your domain object, you would write
something like this in your getDynamicParameters
method:
$myObject = $this->myBackend->loadMyObject(42);
$item = $this->itemBuilder->build($myObject);
- How to override existing templates
- How to add icons for custom layout types
- How to add icons for custom block types
- How to quickly render a layout on a single URL
- How to inject current eZ location and content in blocks and query types
- How to override the templates for Grid block columns
- How to specify default value for block parameters
- How to inject items to blocks manually
- How to override existing templates
- How to add icons for custom layout types
- How to add icons for custom block types
- How to quickly render a layout on a single URL
- How to inject current eZ location and content in blocks and query types
- How to override the templates for Grid block columns
- How to specify default value for block parameters
- How to inject items to blocks manually