Developing and rendering custom content blocks
This page describes how to define and render custom content blocks that can be grouped to form content compositions in implementations of the Content editor.
Compatibility note Custom content editor and block definitions created in the Magnolia 5 UI framework must be migrated. |
Block types
The Content editor provides predefined block types that you can use in your custom content editor app:
-
text
- Compared toTextFieldDefinition
, thetext
block implements a specialLightRichTextFieldDefinition
class, which features basic text formatting functions:templateId: content-editor:blocks/text icon: text-block label: Text block: implementationClass: info.magnolia.editor.field.LightRichTextFormView properties: text: class: info.magnolia.editor.field.LightRichTextFieldDefinition linkableApps: - pages-app - dam-chooser
-
image
- Configured using the ordinary 6 UIdamLinkField
andtextField
field types:templateId: content-editor:blocks/image icon: file-image label: Image block: properties: image: $type: damLinkField label: Image placeholder: Add image... buttonSelectNewLabel: Select new... imageAltText: label: Alt text $type: textField placeholder: Add alt text... imageCaption: label: Caption placeholder: Add caption... $type: textField imageCredit: label: Credits placeholder: Add credits... $type: textField
-
externalLink
- Utilizing thePeekFieldDefinition
class from the Magnolia Link Unfurl Module, a submodule in the Content Editor module.templateId: link-unfurl:components/externalLink icon: embed label: Embed content block: properties: url: class: info.magnolia.unfurl.ui.PeekFieldDefinition label: Embedded content validators: url: class: info.magnolia.unfurl.ui.UrlValidatorDefinition errorMessage: link-unfurl.components.externalLink.tabUrl.url.validation.errorMessage
-
video
- A combination of the following 6 UI field types:checkBoxField
,comboBoxField
,damLinkField
,expandingTextField
,radioButtonGroupField
,switchableField
andtextField
.
Demo block types
The Magnolia Demo decorates the default Stories app and provides two additional block types you can use:
-
date
-
tour
These block types implement the 6 UI dateField
and linkField
, respectively. To see these blocks, you must have the Magnolia demo modules installed.
Defining a custom content block
To define a custom block, use a YAML definition file and apply the BlockDefinition
class of the Content editor module.
-
Create a YAML file in the
blocks
folder of your module and add the following required definition elements.class: info.magnolia.block.BlockDefinition (1) templateId: <module-name>:<the-path-to-the-block-relative-to-the-module> (2) icon: <icon-name> label: <i18n-label> block: (3) itemProvider: $type: jcrBlockGetIndexedChildNode properties: field1: <field1-property1> <field1-property2> field2: <field2-property1> <field2-property2> etc.
1 The info.magnolia.block.BlockDefinition
class.2 The templateId
of your block.3 A block
node, with a list of fields the content block consists of. Use the properties in the same way as the properties for theCompositeFieldDefinition
. -
Provide a template definition file and a template script for your block in the
templates/blocks
subfolder of your module. -
Optionally, in the
i18n
folder of your module, provide a file withi18n
keys for labels and descriptions of the block’s fields.
Creating a custom block with Magnolia CLI
You can also create a block using Magnolia CLI. For more details, see the CLI create-block plugin.
Example:
npm run mgnl -- create-block "my-block"
Rendering blocks in a FreeMarker script
This section explains how to render block content in a page or a component template.
The cms:block
directive
The Content editor module provides cms:block
, a Magnolia FreeMarker
directive for fetching and
rendering block elements.
The directive expects a node of the type mgnl:block
as argument,
identifies the template definition of the block and calls the associated
template script.
[#assign blocks = cmsfn.children(articleContent, "mgnl:block") /] [#list blocks as block] [@cms.block content=block /] [/#list]
Examples of block rendering
The examples show how to render blocks within a
template script of a page or
component template. Using the Magnolia directive cms.block
, the
template script of the block is executed during the rendering of the
page or component.
-
Get story content:
[#assign articleContent = cmsfn.contentById(content.article, "") /]
-
Get the blocks for that story:
[#assign blocks = cmsfn.children(articleContent, "mgnl:block") /]
-
Retrieve all blocks for a piece of content:
[#if articleContent?hasContent] [#assign blocks = cmsfn.children(articleContent, "mgnl:block") /] [#list blocks as block] [@cms.block content=block /] [/#list] [/#if]
Line 4: The Magnolia directive
cms.block
ensures that the template script associated to the passed block is called to render the block content. -
Retrieve two blocks, for instance to display an excerpt of a story in a list:
[#if articleContent?hasContent] [#assign blocks = cmsfn.children(articleContent, "mgnl:block") /] [#list blocks as block] [#if block?index == 2] [#break] [/#if] [@cms.block content=block /] [/#list] [/#if]
Line 7: The Magnolia directive
cms.block
calls the template script associated to the passed block content.
Content i18n and migration to version 2.1 (and higher)
Internationalization (i18n) of content is supported since version 2.1
of the Content Editor module.
If you are upgrading from Magnolia 6.2 or from a 1.x version of the Content Editor, please mind the migration procedures below to enable i18n in the Stories app, as well as in other Content Editor based apps you may have.
Compatibility of content and block definitions
Block definitions and data structures created in the older versions of the module must be migrated.
Flat vs nested content structure
The data model has changed for internationalized stories. Whereas in versions 1.3.x
and 2.0.x
of the module, the mgnl:block
elements are stored in a flat node structure,
stories └── story1 ├── 0 └── 1
in the i18n-supported version (2.1
and higher), the nodes are locale-nested under intermediate nodes of type mgnl:contentNode
, named blocks_de
and blocks
in this example:
stories └── story1 └── blocks_de │ ├── 0_de │ └── 1_de └── blocks ├── 0 └── 1
This must be reflected in your MultiJcrBlockDefinition
, where you need to add and enable the i18n
property.
Instead of the CurrentItemProvider
, the CompatibleBlockProvider
is set as the default provider, which can resolve both flat and nested block nodes. You do not need to declare it in your block definition.
- Example definition
-
blocks: label: Blocks $type: multiJcrBlock i18n: true blocks: - text
This applies only to the 2.0.x block definitions. Block definitions created for version 1.3.x (5 UI) of the module are not compatible with the 2.1 version and must be fully migrated.
|
Migrating content
There are two ways you can migrate the non-i18n blocks to the i18n-compatible hierarchy: using a version handler or a Groovy script.
We strongly recommend you have the latest version of the Content Editor before migrating your content. In versions prior to 2.1.3, the |
Version handler
When upgrading the Stories app submodule to version 2.1
or higher, all block nodes in the stories
workspace will be moved to intermediate nodes, see the MigrateBlockToIntermediateParentTask
task.
Groovy script
You can run the migration task in the Groovy app, especially in case a block node is stored in another workspace.
- Example Groovy script
import info.magnolia.editor.setup.MigrateBlockToIntermediateParentTask
import info.magnolia.module.InstallContextImpl
import info.magnolia.module.ModuleRegistryImpl
import info.magnolia.objectfactory.Components
import javax.jcr.Session
Session session = MgnlContext.getJCRSession("stories");
task = new MigrateBlockToIntermediateParentTask("stories", "/", "blocks");
task.execute(Components.newInstance(InstallContextImpl.class));
session.save();
The parameters in the MigrateBlockToIntermediateParentTask
:
-
stories
- workspace name -
/
- path -
blocks
- name of the intermediate node, the name of themultiJcrBlock
field.