Unverified Commit 02f93254 authored by larowlan's avatar larowlan

Issue #2922033 by tim.plunkett, mtodor, larowlan, xjm, pameeela, EclipseGc,...

Issue #2922033 by tim.plunkett, mtodor, larowlan, xjm, pameeela, EclipseGc, samuel.mortenson: Use the Layout Builder for EntityViewDisplays
parent 1455cb13
......@@ -369,7 +369,7 @@ field.formatter.settings.entity_reference_label:
type: boolean
label: 'Link label to the referenced entity'
block.settings.field_block:*:*:
block.settings.field_block:*:*:*:
type: block_settings
mapping:
formatter:
......
......@@ -5,3 +5,42 @@ core.entity_view_display.*.*.*.third_party.layout_builder:
allow_custom:
type: boolean
label: 'Allow a customized layout'
sections:
type: sequence
sequence:
type: layout_builder.section
layout_builder.section:
type: mapping
label: 'Layout section'
mapping:
layout_id:
type: string
label: 'Layout ID'
layout_settings:
type: layout_plugin.settings.[%parent.layout_id]
label: 'Layout settings'
components:
type: sequence
label: 'Components'
sequence:
type: layout_builder.component
layout_builder.component:
type: mapping
label: 'Component'
mapping:
configuration:
type: block.settings.[id]
region:
type: string
label: 'Region'
uuid:
type: uuid
label: 'UUID'
weight:
type: integer
label: 'Weight'
additional:
type: ignore
label: 'Additional data'
......@@ -7,3 +7,5 @@ core: 8.x
dependencies:
- layout_discovery
- contextual
# @todo Discuss removing in https://www.drupal.org/project/drupal/issues/2935999.
- field_ui
<?php
/**
* @file
* Contains install and update functions for Layout Builder.
*/
use Drupal\Core\Cache\Cache;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\Section;
/**
* Implements hook_install().
*/
function layout_builder_install() {
$displays = LayoutBuilderEntityViewDisplay::loadMultiple();
/** @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface[] $displays */
foreach ($displays as $display) {
// Create the first section from any existing Field Layout settings.
$field_layout = $display->getThirdPartySettings('field_layout');
if (isset($field_layout['id'])) {
$field_layout += ['settings' => []];
$display->appendSection(new Section($field_layout['id'], $field_layout['settings']));
}
// Sort the components by weight.
$components = $display->get('content');
uasort($components, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
foreach ($components as $name => $component) {
$display->setComponent($name, $component);
}
$display->save();
}
// Clear the rendered cache to ensure the new layout builder flow is used.
// While in many cases the above change will not affect the rendered output,
// the cacheability metadata will have changed and should be processed to
// prepare for future changes.
Cache::invalidateTags(['rendered']);
}
......@@ -5,17 +5,14 @@
* Provides hook implementations for Layout Builder.
*/
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\FieldConfigInterface;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplayStorage;
use Drupal\layout_builder\Form\LayoutBuilderEntityViewDisplayForm;
/**
* Implements hook_help().
......@@ -52,17 +49,18 @@ function layout_builder_entity_type_alter(array &$entity_types) {
$entity_type->setLinkTemplate('layout-builder', $entity_type->getLinkTemplate('canonical') . '/layout');
}
}
$entity_types['entity_view_display']
->setClass(LayoutBuilderEntityViewDisplay::class)
->setStorageClass(LayoutBuilderEntityViewDisplayStorage::class)
->setFormClass('edit', LayoutBuilderEntityViewDisplayForm::class);
}
/**
* Removes the Layout Builder field both visually and from the #fields handling.
*
* This prevents any interaction with this field. It is rendered directly
* in layout_builder_entity_view_alter().
*
* @internal
* Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityFormDisplayEditForm.
*/
function _layout_builder_hide_layout_field(array &$form) {
function layout_builder_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
// Hides the Layout Builder field. It is rendered directly in
// \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildMultiple().
unset($form['fields']['layout_builder__layout']);
$key = array_search('layout_builder__layout', $form['#fields']);
if ($key !== FALSE) {
......@@ -71,140 +69,23 @@ function _layout_builder_hide_layout_field(array &$form) {
}
/**
* Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityFormDisplayEditForm.
*/
function layout_builder_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
_layout_builder_hide_layout_field($form);
}
/**
* Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityViewDisplayEditForm.
*/
function layout_builder_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
$display = $form_state->getFormObject()->getEntity();
$entity_type = \Drupal::entityTypeManager()->getDefinition($display->getTargetEntityTypeId());
_layout_builder_hide_layout_field($form);
// @todo Expand to work for all view modes in
// https://www.drupal.org/node/2907413.
if (!in_array($display->getMode(), ['full', 'default'], TRUE)) {
return;
}
$form['layout'] = [
'#type' => 'details',
'#open' => TRUE,
'#title' => t('Layout options'),
'#tree' => TRUE,
];
// @todo Unchecking this box is a destructive action, this should be made
// clear to the user in https://www.drupal.org/node/2914484.
$form['layout']['allow_custom'] = [
'#type' => 'checkbox',
'#title' => t('Allow each @entity to have its layout customized.', [
'@entity' => $entity_type->getSingularLabel(),
]),
'#default_value' => $display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE),
];
$form['#entity_builders'][] = 'layout_builder_form_entity_view_display_edit_entity_builder';
}
/**
* Entity builder for layout options on the entity view display form.
*
* @see layout_builder_form_entity_view_display_edit_form_alter()
*/
function layout_builder_form_entity_view_display_edit_entity_builder($entity_type_id, EntityViewDisplayInterface $display, &$form, FormStateInterface &$form_state) {
$new_value = (bool) $form_state->getValue(['layout', 'allow_custom'], FALSE);
$display->setThirdPartySetting('layout_builder', 'allow_custom', $new_value);
}
/**
* Implements hook_ENTITY_TYPE_presave().
*/
function layout_builder_entity_view_display_presave(EntityViewDisplayInterface $display) {
$original_value = isset($display->original) ? $display->original->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) : FALSE;
$new_value = $display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE);
if ($original_value !== $new_value) {
$entity_type_id = $display->getTargetEntityTypeId();
$bundle = $display->getTargetBundle();
if ($new_value) {
layout_builder_add_layout_section_field($entity_type_id, $bundle);
}
elseif ($field = FieldConfig::loadByName($entity_type_id, $bundle, 'layout_builder__layout')) {
$field->delete();
}
}
}
/**
* Adds a layout section field to a given bundle.
*
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle
* The bundle.
* @param string $field_name
* (optional) The name for the layout section field. Defaults to
* 'layout_builder__layout'.
*
* @return \Drupal\field\FieldConfigInterface
* A layout section field.
* Implements hook_field_config_insert().
*/
function layout_builder_add_layout_section_field($entity_type_id, $bundle, $field_name = 'layout_builder__layout') {
$field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
if (!$field) {
$field_storage = FieldStorageConfig::loadByName($entity_type_id, $field_name);
if (!$field_storage) {
$field_storage = FieldStorageConfig::create([
'entity_type' => $entity_type_id,
'field_name' => $field_name,
'type' => 'layout_section',
]);
$field_storage->save();
}
$field = FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => $bundle,
'label' => t('Layout'),
]);
$field->save();
}
return $field;
function layout_builder_field_config_insert(FieldConfigInterface $field_config) {
// Clear the sample entity for this entity type and bundle.
/** @var \Drupal\Core\TempStore\SharedTempStore $tempstore */
$tempstore = \Drupal::service('tempstore.shared')->get('layout_builder.sample_entity');
$tempstore->delete($field_config->getTargetEntityTypeId() . '.' . $field_config->getTargetBundle());
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
/**
* Implements hook_entity_view_alter().
* Implements hook_field_config_delete().
*/
function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
if ($display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) && !$entity->layout_builder__layout->isEmpty()) {
$contexts = \Drupal::service('context.repository')->getAvailableContexts();
// @todo Use EntityContextDefinition after resolving
// https://www.drupal.org/node/2932462.
$contexts['layout_builder.entity'] = new Context(new ContextDefinition("entity:{$entity->getEntityTypeId()}", new TranslatableMarkup('@entity being viewed', ['@entity' => $entity->getEntityType()->getLabel()])), $entity);
$sections = $entity->layout_builder__layout->getSections();
foreach ($sections as $delta => $section) {
$build['_layout_builder'][$delta] = $section->toRenderArray($contexts);
}
// If field layout is active, that is all that needs to be removed.
if (\Drupal::moduleHandler()->moduleExists('field_layout') && isset($build['_field_layout'])) {
unset($build['_field_layout']);
return;
}
/** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */
$field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions($display->getTargetEntityTypeId(), $display->getTargetBundle());
// Remove all display-configurable fields.
foreach (array_keys($display->getComponents()) as $name) {
if ($name !== 'layout_builder__layout' && isset($field_definitions[$name]) && $field_definitions[$name]->isDisplayConfigurable('view')) {
unset($build[$name]);
}
}
}
function layout_builder_field_config_delete(FieldConfigInterface $field_config) {
// Clear the sample entity for this entity type and bundle.
/** @var \Drupal\Core\TempStore\SharedTempStore $tempstore */
$tempstore = \Drupal::service('tempstore.shared')->get('layout_builder.sample_entity');
$tempstore->delete($field_config->getTargetEntityTypeId() . '.' . $field_config->getTargetBundle());
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
......@@ -9,6 +9,8 @@ services:
layout_builder.routes:
class: Drupal\layout_builder\Routing\LayoutBuilderRoutes
arguments: ['@entity_type.manager', '@entity_field.manager']
tags:
- { name: event_subscriber }
layout_builder.route_enhancer:
class: Drupal\layout_builder\Routing\LayoutBuilderRouteEnhancer
tags:
......@@ -18,6 +20,9 @@ services:
arguments: ['@layout_builder.tempstore_repository', '@class_resolver']
tags:
- { name: paramconverter, priority: 10 }
layout_builder.section_storage_param_converter.defaults:
class: Drupal\layout_builder\Routing\SectionStorageDefaultsParamConverter
arguments: ['@entity.manager']
layout_builder.section_storage_param_converter.overrides:
class: Drupal\layout_builder\Routing\SectionStorageOverridesParamConverter
arguments: ['@entity.manager']
......
......@@ -4,8 +4,8 @@
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CalculatedCacheContextInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\layout_builder\OverridesSectionStorageInterface;
/**
* Determines whether Layout Builder is active for a given entity type or not.
......@@ -49,7 +49,7 @@ public function getContext($entity_type_id = NULL) {
}
$display = $this->getDisplay($entity_type_id);
return ($display && $display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE)) ? '1' : '0';
return ($display && $display->isOverridable()) ? '1' : '0';
}
/**
......@@ -72,15 +72,15 @@ public function getCacheableMetadata($entity_type_id = NULL) {
*
* @param string $entity_type_id
* The entity type ID.
* @param string $view_mode
* (optional) The view mode that should be used to render the entity.
*
* @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface|null
* @return \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface|null
* The entity view display, if it exists.
*/
protected function getDisplay($entity_type_id, $view_mode = 'full') {
protected function getDisplay($entity_type_id) {
if ($entity = $this->routeMatch->getParameter($entity_type_id)) {
return EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
if ($entity instanceof OverridesSectionStorageInterface) {
return $entity->getDefaultSectionStorage();
}
}
}
......
......@@ -3,11 +3,13 @@
namespace Drupal\layout_builder\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\OverridesSectionStorageInterface;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -30,14 +32,24 @@ class LayoutBuilderController implements ContainerInjectionInterface {
*/
protected $layoutTempstoreRepository;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* LayoutBuilderController constructor.
*
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
* The layout tempstore repository.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
*/
public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, MessengerInterface $messenger) {
$this->layoutTempstoreRepository = $layout_tempstore_repository;
$this->messenger = $messenger;
}
/**
......@@ -45,7 +57,8 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('layout_builder.tempstore_repository')
$container->get('layout_builder.tempstore_repository'),
$container->get('messenger')
);
}
......@@ -101,9 +114,16 @@ public function layout(SectionStorageInterface $section_storage, $is_rebuilding
* Indicates if the layout is rebuilding.
*/
protected function prepareLayout(SectionStorageInterface $section_storage, $is_rebuilding) {
// For a new layout, begin with a single section of one column.
// Only add sections if the layout is new and empty.
if (!$is_rebuilding && $section_storage->count() === 0) {
$sections = [];
// If this is an empty override, copy the sections from the corresponding
// default.
if ($section_storage instanceof OverridesSectionStorageInterface) {
$sections = $section_storage->getDefaultSectionStorage()->getSections();
}
// For an empty layout, begin with a single section of one column.
if (!$sections) {
$sections[] = new Section('layout_onecol');
}
......@@ -172,7 +192,7 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
$section = $section_storage->getSection($delta);
$layout = $section->getLayout();
$build = $section->toRenderArray($this->getAvailableContexts($section_storage));
$build = $section->toRenderArray($this->getAvailableContexts($section_storage), TRUE);
$layout_definition = $layout->getPluginDefinition();
foreach ($layout_definition->getRegions() as $region => $info) {
......@@ -277,6 +297,14 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
public function saveLayout(SectionStorageInterface $section_storage) {
$section_storage->save();
$this->layoutTempstoreRepository->delete($section_storage);
if ($section_storage instanceof OverridesSectionStorageInterface) {
$this->messenger->addMessage($this->t('The layout override has been saved.'));
}
else {
$this->messenger->addMessage($this->t('The layout has been saved.'));
}
return new RedirectResponse($section_storage->getCanonicalUrl()->setAbsolute()->toString());
}
......@@ -291,6 +319,9 @@ public function saveLayout(SectionStorageInterface $section_storage) {
*/
public function cancelLayout(SectionStorageInterface $section_storage) {
$this->layoutTempstoreRepository->delete($section_storage);
$this->messenger->addMessage($this->t('The changes to the layout have been discarded.'));
return new RedirectResponse($section_storage->getCanonicalUrl()->setAbsolute()->toString());
}
......
<?php
namespace Drupal\layout_builder\Entity;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\Core\Entity\EntityInterface;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
/**
* Provides storage for entity view display entities that have layouts.
*
* @internal
* Layout Builder is currently experimental and should only be leveraged by
* experimental modules and development releases of contributed modules.
* See https://www.drupal.org/core/experimental for more information.
*/
class LayoutBuilderEntityViewDisplayStorage extends ConfigEntityStorage {
/**
* {@inheritdoc}
*/
protected function mapToStorageRecord(EntityInterface $entity) {
$record = parent::mapToStorageRecord($entity);
if (!empty($record['third_party_settings']['layout_builder']['sections'])) {
$record['third_party_settings']['layout_builder']['sections'] = array_map(function (Section $section) {
return $section->toArray();
}, $record['third_party_settings']['layout_builder']['sections']);
}
return $record;
}
/**
* {@inheritdoc}
*/
protected function mapFromStorageRecords(array $records) {
foreach ($records as $id => &$record) {
if (!empty($record['third_party_settings']['layout_builder']['sections'])) {
$sections = &$record['third_party_settings']['layout_builder']['sections'];
foreach ($sections as $section_delta => $section) {
$sections[$section_delta] = new Section(
$section['layout_id'],
$section['layout_settings'],
array_map(function (array $component) {
return (new SectionComponent(
$component['uuid'],
$component['region'],
$component['configuration'],
$component['additional']
))->setWeight($component['weight']);
}, $section['components'])
);
}
}
}
return parent::mapFromStorageRecords($records);
}
}
<?php
namespace Drupal\layout_builder\Entity;
use Drupal\Core\Entity\Display\EntityDisplayInterface;
use Drupal\layout_builder\SectionStorageInterface;
/**
* Provides an interface for entity displays that have layout.
*
* @internal
* Layout Builder is currently experimental and should only be leveraged by
* experimental modules and development releases of contributed modules.
* See https://www.drupal.org/core/experimental for more information.
*/
interface LayoutEntityDisplayInterface extends EntityDisplayInterface, SectionStorageInterface {
/**
* Determines if the display allows custom overrides.
*
* @return bool
* TRUE if custom overrides are allowed, FALSE otherwise.
*/
public function isOverridable();
/**
* Sets the display to allow or disallow overrides.
*
* @param bool $overridable
* TRUE if the display should allow overrides, FALSE otherwise.
*
* @return $this
*/
public function setOverridable($overridable = TRUE);
}
......@@ -6,6 +6,8 @@
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\OverridesSectionStorageInterface;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionStorageInterface;
......@@ -16,7 +18,7 @@
*
* @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
*/
class LayoutSectionItemList extends FieldItemList implements SectionStorageInterface {
class LayoutSectionItemList extends FieldItemList implements SectionStorageInterface, OverridesSectionStorageInterface {
/**
* {@inheritdoc}
......@@ -27,6 +29,7 @@ public function insertSection($delta, Section $section) {
$item = $this->createItem($delta);
$item->section = $section;
// @todo Use https://www.drupal.org/node/66183 once resolved.
$start = array_slice($this->list, 0, $delta);
$end = array_slice($this->list, $delta);
$this->list = array_merge($start, [$item], $end);
......@@ -91,7 +94,7 @@ public function getContexts() {
/**
* {@inheritdoc}
*/
public function getStorageType() {
public static function getStorageType() {
return 'overrides';
}
......@@ -131,6 +134,13 @@ public function getLayoutBuilderUrl() {
return $this->getEntity()->toUrl('layout-builder');
}
/**
* {@inheritdoc}
*/
public function getDefaultSectionStorage() {
return LayoutBuilderEntityViewDisplay::collectRenderDisplay($this->getEntity(), 'default');
}
/**
* {@inheritdoc}
*/
......
......@@ -2,8 +2,9 @@
namespace Drupal\layout_builder\Form;
use Drupal\layout_builder\Section;
use Drupal\Core\Form\FormStateInterface;
use Drupal\layout_builder\SectionComponent;
use Drupal\layout_builder\SectionStorageInterface;
/**
* Provides a form to add a block.
......@@ -27,10 +28,32 @@ protected function submitLabel() {
}
/**
* {@inheritdoc}
* Builds the form for the block.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
* The section storage being configured.
* @param int $delta
* The delta of the section.
* @param string $region
* The region of the block.
* @param string|null $plugin_id
* The plugin ID of the block to add.
*
* @return array
* The form array.
*/
protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
$section->appendComponent(new SectionComponent($uuid, $region, $configuration));
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL, $delta = NULL, $region = NULL, $plugin_id = NULL) {
// Only generate a new component once per form submission.
if (!$component = $form_state->getTemporaryValue('layout_builder__component')) {
$component = new SectionComponent($this->uuidGenerator->generate(), $region, ['id' => $plugin_id]);
$section_storage->getSection($delta)->appendComponent($component);
$form_state->setTemporaryValue('layout_builder__component', $component);
}
return $this->doBuildForm($form, $form_state, $section_storage, $delta, $component);
}
}
......@@ -17,7 +17,7 @@
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Drupal\layout_builder\Controller\LayoutRebuildTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
use Drupal\layout_builder\SectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -59,7 +59,7 @@ abstract class ConfigureBlockFormBase extends FormBase {
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuid;
protected $uuidGenerator;
/**
* The plugin form manager.
......@@ -82,6 +82,13 @@ abstract class ConfigureBlockFormBase extends FormBase {
*/
protected $region;
/**
* The UUID of the component.
*
* @var string
*/
protected $uuid;
/**
* The section storage.
*
......@@ -109,7 +116,7 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore
$this->layoutTempstoreRepository = $layout_tempstore_repository;
$this->contextRepository = $context_repository;
$this->blockManager = $block_manager;
$this->uuid = $uuid;
$this->uuidGenerator = $uuid;
$this->classResolver = $class_resolver;