Content Widgets
Content Widgets enable you to render any html text generated by your custom PHP code, such as a marketing banner or a contact form.
Create a Content Widget Type
We are going to illustrate how to create a content widget type to render the copyright. It should have an option to control the display format (short or long).
You can create a content widget type in four steps outlined below.
1. Extend AbstractContentWidgetType
To implement a new content widget, create a class that stores content widget type configuration and renders the content widget. The class should extend AbstractContentWidgetType.
namespace Acme\Bundle\CopyrightBundle\ContentWidget;
use Oro\Bundle\CMSBundle\ContentWidget\AbstractContentWidgetType;
use Oro\Bundle\CMSBundle\Entity\ContentWidget;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
class CopyrightContentWidgetType extends AbstractContentWidgetType
{
public static function getName(): string
{
return 'copyright';
}
public function getLabel(): string
{
return 'acme.copyright.content_widget.copyright.label';
}
public function getSettingsForm(ContentWidget $contentWidget, FormFactoryInterface $formFactory): ?FormInterface
{
return $formFactory->createBuilder(FormType::class)
->add('isShort', CheckboxType::class, ['label' => 'acme.copyright.settings.is_short.label', 'required' => false])
->getForm();
}
public function getDefaultTemplate(ContentWidget $contentWidget, Environment $twig): string
{
return $twig->render('@AcmeCopyright/CopyrightContentWidget/widget.html.twig', $contentWidget->getSettings());
}
}
Note
When you want to use an existing form type in you content widget type:
public function getSettingsForm(ContentWidget $contentWidget, FormFactoryInterface $formFactory): ?FormInterface
{
return $formFactory->create(FormType::class);
}
The form type has the following code:
<?php
namespace Acme\Bundle\CopyrightBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Copyright content widget form type.
*/
class CopyrightContentWidgetType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'isShort',
CheckboxType::class,
['label' => 'acme.copyright.settings.is_short.label', 'required' => false]
);
}
}
It should be registered in a service container with the oro_cms.content_widget.type tag.
services:
Acme\Bundle\CopyrightBundle\ContentWidget\CopyrightContentWidgetType:
tags:
- {name: 'oro_cms.content_widget.type'}
Note
When autoconfiguration is enabled, tagging the service manually is unnecessary.
services:
_defaults:
autoconfigure: true
Acme\Bundle\CopyrightBundle\ContentWidget\CopyrightContentWidgetType: ~
Add translations to strings in a template.
acme.copyright:
content_widget:
copyright:
label: 'Copyright'
settings:
is_short.label: 'Short'
2. Create a Template to Render the Widget in the Storefront
Create a template to render the content widget in the storefront.
{%- set copyright = isShort ? 'acme.copyright.text.short' : 'acme.copyright.text.default' -%}
<copy>{{ copyright|trans({ '%year%': 'now'|date('Y') }) }}</copy>
Add translations to strings in the template.
acme.copyright:
text:
default: '(c) %year%. All rights reserved'
short: '(c) %year%'
3. (Optionally) Render the Widget Info in the Back-Office
3.1 Create a Template
{% import '@OroUI/macros.html.twig' as UI %}
{{ UI.renderProperty('acme.copyright.settings.is_short.label'|trans, settings.isShort ? 'Yes'|trans : 'No'|trans) }}
3.2 Implement the getAdditionalInformationBlock Method in the Content Widget Type
protected function getAdditionalInformationBlock(ContentWidget $contentWidget, Environment $twig): string
{
return $twig->render(
'@AcmeCopyright/CopyrightContentWidget/view.html.twig',
['settings' => $contentWidget->getSettings()]
);
}
Note
To pass additional data to the template, you can override getBackOfficeViewSubBlocks method. The example below illustrates how to add two blocks with two subblocks in each block.
namespace Acme\Bundle\CopyrightBundle\ContentWidget;
use Oro\Bundle\CMSBundle\ContentWidget\AbstractContentWidgetType;
use Oro\Bundle\CMSBundle\Entity\ContentWidget;
use Twig\Environment;
/**
* Type for the copyright widgets.
*/
class CopyrightContentWidgetType extends AbstractContentWidgetType
{
public function getBackOfficeViewSubBlocks(ContentWidget $contentWidget, Environment $twig): array
{
return [
[
'title' => 'oro.cms.contentwidget.sections.additional_information_block1.label',
'subblocks' => [
[
'data' => [
$twig->render(
'@AcmeCopyright/CopyrightContentWidget/acme_template1.html.twig',
['settings' => $contentWidget->getSettings()]
),
]
],
[
'data' => [
$twig->render(
'@AcmeCopyright/CopyrightContentWidget/acme_template2.html.twig',
['settings' => $contentWidget->getSettings()]
),
]
],
]
],
[
'title' => 'oro.cms.contentwidget.sections.additional_information_block2.label',
'subblocks' => [
[
'data' => [
$twig->render(
'@AcmeCopyright/CopyrightContentWidget/acme_template3.html.twig',
['settings' => $contentWidget->getSettings()]
),
]
],
[
'data' => [
$twig->render(
'@AcmeCopyright/CopyrightContentWidget/acme_template4.html.twig',
['settings' => $contentWidget->getSettings()]
),
]
],
]
],
];
}
}
4. (Optionally) Pass Custom Data to the Template when Rendering Widget in the Storefront
Override getWidgetData method in the Content Widget Type.
namespace Acme\Bundle\CopyrightBundle\ContentWidget;
use Oro\Bundle\CMSBundle\ContentWidget\AbstractContentWidgetType;
use Oro\Bundle\CMSBundle\Entity\ContentWidget;
use Oro\Bundle\ProductBundle\Entity\Product;
/**
* Type for the copyright widgets.
*/
class CopyrightContentWidgetType extends AbstractContentWidgetType
{
...
public function getWidgetData(ContentWidget $contentWidget): array
{
// For example, fetch the product from entity manager to pass it to the template
$product = $this->doctrine->getManagerForClass(Product::class)
->find(Product::class, $contentWidget->getSettings()['productId']);
return ['contentWidget' => $contentWidget, 'product' => $product];
}
}
5. (Optionally) Add Content Widget Templates
It is possible to provide multiple templates for some content widget types. This allows the user to select which template to use when creating a content widget instance.
If there is a least one template defined, a list of all templates collected from all themes for this widget type is displayed on the content widget create/edit form drop-down.
During rendering in the storefront, if the template selected by the user is not available in the current theme, the widget is rendered using its default template (set in the getDefaultTemplate method). To add a new layout template for a widget type, follow the steps below:
5.1 Add Template Definition to Theme Configuration
Create a widgets.yml
file for certain theme (e.g., default) in the config folder Resources/views/layouts/default/config/widgets.yml
:
layouts:
copyright:
first: 'acme.copyright.content_widget.copyright.label'
copyright
stands for the widget type to which template is added.first
key represents a particular theme with its name (translation key) as a value.
5.2 Add Layout Update File and Template
Create a layout update file in the appropriate content widget folder inside the desired theme.
Keep in mind that all widget layout update files should follow a naming convention: content_widget/{your_unique_widget_type_name}
, e.g.,: Resources/views/layouts/default/content_widget/copyright/content_widget.yml
.
layout:
actions:
- '@setBlockTheme':
themes: 'content_widget.html.twig'
...
Follow the same steps with templates for the layout update with customized markup Resources/views/layouts/default/content_widget/copyright/content_widget.html.twig
:
{% block _copyright_content_widget_layout_name_widget %}
<p>{{ block_widget(block) }}</p>
{% endblock %}
The widget template for the Copyright widget should be available after clearing the cache. To define templates for other themes, apply the same actions making sure you place files in the appropriate theme folders and follow the naming conventions.
Now an administrator can create content widgets of a new type from the UI by following steps outlined in the Content Widgets User Guide user documentation.
Support Content Widgets in Custom Content
To support widgets in any text, you should apply the render_content filter in a twig template.
For example:
{{ entity.content | oro_html_sanitize | render_content }}
Display Label for Content Widget in Layout
Make sure that you have completed the data for the labels field of the content widget. Then, in layout, you will have the next variables defaultLabel and labels. You can display these variables in the following ways:
layout:
actions:
- '@setOption':
id: layoutBlockId
optionName: label
optionValue:
'=data["defaultLabel"]'
# or
'=data["locale"].getLocalizedValue(data["labels"])'
...