diff --git a/core/modules/layout_builder/layout_builder.links.contextual.yml b/core/modules/layout_builder/layout_builder.links.contextual.yml
index bcf2a9cf06..4cff0d197b 100644
--- a/core/modules/layout_builder/layout_builder.links.contextual.yml
+++ b/core/modules/layout_builder/layout_builder.links.contextual.yml
@@ -17,3 +17,14 @@ layout_builder_block_remove:
class: ['use-ajax']
data-dialog-type: dialog
data-dialog-renderer: off_canvas
+
+layout_builder_block_translate:
+ title: 'Translate block'
+ route_name: 'layout_builder.translate_block'
+ group: 'layout_builder_block_translation'
+ options:
+ attributes:
+ class: ['use-ajax']
+ data-dialog-type: dialog
+ data-dialog-renderer: off_canvas
+
diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module
index 277f81a87c..e3f2e13199 100644
--- a/core/modules/layout_builder/layout_builder.module
+++ b/core/modules/layout_builder/layout_builder.module
@@ -25,6 +25,7 @@
use Drupal\layout_builder\InlineBlockEntityOperations;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessResult;
+use Drupal\layout_builder\Form\BlockPluginTranslationForm;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
/**
@@ -323,10 +324,24 @@ function layout_builder_entity_translation_create(EntityInterface $translation)
$translatable_fields = $translation->getTranslatableFields();
if (array_key_exists(OverridesSectionStorage::FIELD_NAME, $translatable_fields)) {
// When creating a new translation do not copy untranslated sections.
- // Until the user explicitly creates an layout translation the translated
- // layout will use the untranslated layout.
- // @see \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildSections()
+ // Currently totally independent overrides per language are not supported.
$translation->set(OverridesSectionStorage::FIELD_NAME, []);
}
}
}
+
+/**
+ * Implements hook_block_alter().
+ *
+ * Ensures every block plugin definition has an 'layout_builder_translation'
+ * form specified.
+ */
+function layout_builder_block_alter(&$definitions) {
+ foreach ($definitions as &$definition) {
+ // If a block plugin does not define its own 'layout_builder_translation'
+ // form use the one provided by this module.
+ if (!isset($definition['forms']['layout_builder_translation'])) {
+ $definition['forms']['layout_builder_translation'] = BlockPluginTranslationForm::class;
+ }
+ }
+}
diff --git a/core/modules/layout_builder/layout_builder.routing.yml b/core/modules/layout_builder/layout_builder.routing.yml
index b436055378..f034a5a1b2 100644
--- a/core/modules/layout_builder/layout_builder.routing.yml
+++ b/core/modules/layout_builder/layout_builder.routing.yml
@@ -6,6 +6,7 @@ layout_builder.choose_section:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -19,6 +20,7 @@ layout_builder.add_section:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -36,6 +38,7 @@ layout_builder.configure_section:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -49,6 +52,7 @@ layout_builder.remove_section:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -63,6 +67,7 @@ layout_builder.choose_block:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -77,6 +82,7 @@ layout_builder.add_block:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -90,6 +96,7 @@ layout_builder.choose_inline_block:
_title: 'Add a new Inline Block'
requirements:
_permission: 'configure any layout'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -101,6 +108,21 @@ layout_builder.update_block:
defaults:
_form: '\Drupal\layout_builder\Form\UpdateBlockForm'
_title: 'Configure block'
+ requirements:
+ _permission: 'configure any layout'
+ _layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
+ options:
+ _admin_route: TRUE
+ parameters:
+ section_storage:
+ layout_builder_tempstore: TRUE
+
+layout_builder.translate_block:
+ path: '/layout_builder/translate/block/{section_storage_type}/{section_storage}/{delta}/{region}/{uuid}'
+ defaults:
+ _form: '\Drupal\layout_builder\Form\TranslateBlockForm'
+ _title: 'Translate block'
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
@@ -117,6 +139,7 @@ layout_builder.remove_block:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
@@ -136,6 +159,7 @@ layout_builder.move_block:
requirements:
_permission: 'configure any layout'
_layout_builder_access: 'view'
+ _layout_builder_translation_access: 'view'
options:
_admin_route: TRUE
parameters:
diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml
index 65fb66a895..6f1f87571f 100644
--- a/core/modules/layout_builder/layout_builder.services.yml
+++ b/core/modules/layout_builder/layout_builder.services.yml
@@ -6,6 +6,10 @@ services:
class: Drupal\layout_builder\Access\LayoutBuilderAccessCheck
tags:
- { name: access_check, applies_to: _layout_builder_access }
+ access_check.entity.layout_builder_translation_access:
+ class: Drupal\layout_builder\Access\LayoutBuilderTranslationAccessCheck
+ tags:
+ - { name: access_check, applies_to: _layout_builder_translation_access }
access_check.entity.layout:
class: Drupal\layout_builder\Access\LayoutSectionAccessCheck
tags:
@@ -37,6 +41,10 @@ services:
arguments: ['@current_user']
tags:
- { name: event_subscriber }
+ layout_builder.translate_block_component_subscriber:
+ class: Drupal\layout_builder\EventSubscriber\ComponentPluginLabelTranslate
+ tags:
+ - { name: event_subscriber }
logger.channel.layout_builder:
parent: logger.channel_base
arguments: ['layout_builder']
diff --git a/core/modules/layout_builder/src/Access/LayoutBuilderTranslationAccessCheck.php b/core/modules/layout_builder/src/Access/LayoutBuilderTranslationAccessCheck.php
new file mode 100644
index 0000000000..f3df40112a
--- /dev/null
+++ b/core/modules/layout_builder/src/Access/LayoutBuilderTranslationAccessCheck.php
@@ -0,0 +1,37 @@
+isDefaultTranslation()));
+ if ($access instanceof RefinableCacheableDependencyInterface) {
+ $access->addCacheableDependency($section_storage);
+ }
+ return $access;
+ }
+}
diff --git a/core/modules/layout_builder/src/Element/LayoutBuilder.php b/core/modules/layout_builder/src/Element/LayoutBuilder.php
index df34477ab1..9b05fe9d26 100644
--- a/core/modules/layout_builder/src/Element/LayoutBuilder.php
+++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php
@@ -2,6 +2,7 @@
namespace Drupal\layout_builder\Element;
+use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Core\Ajax\AjaxHelperTrait;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -12,8 +13,9 @@
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\OverridesSectionStorageInterface;
-use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
+use Drupal\layout_builder\SectionComponent;
use Drupal\layout_builder\SectionStorageInterface;
+use Drupal\layout_builder\TranslatableSectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -106,6 +108,7 @@ public function preRender($element) {
*/
protected function layout(SectionStorageInterface $section_storage) {
$this->prepareLayout($section_storage);
+ $is_translation = $section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation();
$output = [];
if ($this->isAjax()) {
@@ -115,17 +118,27 @@ protected function layout(SectionStorageInterface $section_storage) {
}
$count = 0;
for ($i = 0; $i < $section_storage->count(); $i++) {
- $output[] = $this->buildAddSectionLink($section_storage, $count);
+ if (!$is_translation) {
+ $output[] = $this->buildAddSectionLink($section_storage, $count);
+ }
+
$output[] = $this->buildAdministrativeSection($section_storage, $count);
$count++;
}
- $output[] = $this->buildAddSectionLink($section_storage, $count);
+ if (!$is_translation) {
+ $output[] = $this->buildAddSectionLink($section_storage, $count);
+ }
+
$output['#attached']['library'][] = 'layout_builder/drupal.layout_builder';
$output['#type'] = 'container';
$output['#attributes']['id'] = 'layout-builder';
$output['#attributes']['class'][] = 'layout-builder';
// Mark this UI as uncacheable.
$output['#cache']['max-age'] = 0;
+
+ // @todo Add message if not components have translate links!
+ // "There are no settings to translate"
+
return $output;
}
@@ -139,17 +152,20 @@ protected function prepareLayout(SectionStorageInterface $section_storage) {
// If the layout has pending changes, add a warning.
if ($this->layoutTempstoreRepository->has($section_storage)) {
$this->messenger->addWarning($this->t('You have unsaved changes.'));
+ if ($section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation()) {
+ // @todo Copy in any change from the default translation and then
+ // reapply any translated labels where the original labels has not
+ // changed. This should avoid data loss if the layout has been
+ // updated since this layout override has started. This probably also
+ // needs to be done on save to avoid overriding the layout if it was
+ // save since the last time this page was opened.
+ }
}
// If the layout is an override that has not yet been overridden, copy the
// sections from the corresponding default.
elseif ($section_storage instanceof OverridesSectionStorageInterface && !$section_storage->isOverridden()) {
- if ($section_storage->isTranslatable() && !$section_storage->isDefaultTranslation()) {
- $source_storage = $section_storage->getDefaultTranslationSectionStorage();
- }
- else {
- $source_storage = $section_storage->getDefaultSectionStorage();
- }
- foreach ($source_storage->getSections() as $section) {
+ $sections = $section_storage->getDefaultSectionStorage()->getSections();
+ foreach ($sections as $section) {
$section_storage->appendSection($section);
}
$this->layoutTempstoreRepository->set($section_storage);
@@ -237,7 +253,8 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
$storage_type = $section_storage->getStorageType();
$storage_id = $section_storage->getStorageId();
$section = $section_storage->getSection($delta);
-
+ $is_translation = $section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation();
+ $contextual_link_group = $is_translation ? 'layout_builder_block_translation' : 'layout_builder_block';
$layout = $section->getLayout();
$build = $section->toRenderArray($this->getAvailableContexts($section_storage), TRUE);
$layout_definition = $layout->getPluginDefinition();
@@ -246,46 +263,56 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
foreach ($layout_definition->getRegions() as $region => $info) {
if (!empty($build[$region])) {
foreach (Element::children($build[$region]) as $uuid) {
- $build[$region][$uuid]['#attributes']['class'][] = 'draggable';
+ if (!$is_translation) {
+ $build[$region][$uuid]['#attributes']['class'][] = 'draggable';
+ }
+ $component = $section->getComponent($uuid);
+
+ $needs_translation_link = $this->defaultComponentHasTranslatableSettings($section_storage, $component);
+
$build[$region][$uuid]['#attributes']['data-layout-block-uuid'] = $uuid;
- $build[$region][$uuid]['#contextual_links'] = [
- 'layout_builder_block' => [
- 'route_parameters' => [
- 'section_storage_type' => $storage_type,
- 'section_storage' => $storage_id,
- 'delta' => $delta,
- 'region' => $region,
- 'uuid' => $uuid,
+ if (!$is_translation || $needs_translation_link) {
+ $build[$region][$uuid]['#contextual_links'] = [
+ $contextual_link_group => [
+ 'route_parameters' => [
+ 'section_storage_type' => $storage_type,
+ 'section_storage' => $storage_id,
+ 'delta' => $delta,
+ 'region' => $region,
+ 'uuid' => $uuid,
+ ],
],
- ],
- ];
+ ];
+ }
}
}
- $build[$region]['layout_builder_add_block']['link'] = [
- '#type' => 'link',
- // Add one to the current delta since it is zero-indexed.
- '#title' => $this->t('Add Block in section @section, @region region', ['@section' => $delta + 1, '@region' => $region_labels[$region]]),
- '#url' => Url::fromRoute('layout_builder.choose_block',
- [
- 'section_storage_type' => $storage_type,
- 'section_storage' => $storage_id,
- 'delta' => $delta,
- 'region' => $region,
- ],
- [
- 'attributes' => [
- 'class' => [
- 'use-ajax',
- 'layout-builder__link',
- 'layout-builder__link--add',
- ],
- 'data-dialog-type' => 'dialog',
- 'data-dialog-renderer' => 'off_canvas',
+ if (!$is_translation) {
+ $build[$region]['layout_builder_add_block']['link'] = [
+ '#type' => 'link',
+ // Add one to the current delta since it is zero-indexed.
+ '#title' => $this->t('Add Block in section @section, @region region', ['@section' => $delta + 1, '@region' => $region_labels[$region]]),
+ '#url' => Url::fromRoute('layout_builder.choose_block',
+ [
+ 'section_storage_type' => $storage_type,
+ 'section_storage' => $storage_id,
+ 'delta' => $delta,
+ 'region' => $region,
],
- ]
- ),
- ];
+ [
+ 'attributes' => [
+ 'class' => [
+ 'use-ajax',
+ 'layout-builder__link',
+ 'layout-builder__link--add',
+ ],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas',
+ ],
+ ]
+ ),
+ ];
+ }
$build[$region]['layout_builder_add_block']['#type'] = 'container';
$build[$region]['layout_builder_add_block']['#attributes'] = ['class' => ['layout-builder__add-block']];
$build[$region]['layout_builder_add_block']['#weight'] = 1000;
@@ -300,50 +327,81 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
$build['#attributes']['data-layout-delta'] = $delta;
$build['#attributes']['class'][] = 'layout-builder__layout';
- return [
+ $section_container = [
'#type' => 'container',
'#attributes' => [
'class' => ['layout-builder__section'],
],
- 'remove' => [
- '#type' => 'link',
- '#title' => $this->t('Remove section @section', ['@section' => $delta + 1]),
- '#url' => Url::fromRoute('layout_builder.remove_section', [
- 'section_storage_type' => $storage_type,
- 'section_storage' => $storage_id,
- 'delta' => $delta,
- ]),
- '#attributes' => [
- 'class' => [
- 'use-ajax',
- 'layout-builder__link',
- 'layout-builder__link--remove',
+ 'layout-builder__section' => $build,
+ ];
+ if (!$is_translation) {
+ $section_container += [
+ 'remove' => [
+ '#type' => 'link',
+ '#title' => $this->t('Remove section @section', ['@section' => $delta + 1]),
+ '#url' => Url::fromRoute('layout_builder.remove_section', [
+ 'section_storage_type' => $storage_type,
+ 'section_storage' => $storage_id,
+ 'delta' => $delta,
+ ]),
+ '#attributes' => [
+ 'class' => [
+ 'use-ajax',
+ 'layout-builder__link',
+ 'layout-builder__link--remove',
+ ],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas',
],
- 'data-dialog-type' => 'dialog',
- 'data-dialog-renderer' => 'off_canvas',
],
- ],
- 'configure' => [
- '#type' => 'link',
- '#title' => $this->t('Configure section'),
- '#access' => $layout instanceof PluginFormInterface,
- '#url' => Url::fromRoute('layout_builder.configure_section', [
- 'section_storage_type' => $storage_type,
- 'section_storage' => $storage_id,
- 'delta' => $delta,
- ]),
- '#attributes' => [
- 'class' => [
- 'use-ajax',
- 'layout-builder__link',
- 'layout-builder__link--configure',
+ 'configure' => [
+ '#type' => 'link',
+ '#title' => $this->t('Configure section'),
+ '#access' => $layout instanceof PluginFormInterface,
+ '#url' => Url::fromRoute('layout_builder.configure_section', [
+ 'section_storage_type' => $storage_type,
+ 'section_storage' => $storage_id,
+ 'delta' => $delta,
+ ]),
+ '#attributes' => [
+ 'class' => [
+ 'use-ajax',
+ 'layout-builder__link',
+ 'layout-builder__link--configure',
+ ],
+ 'data-dialog-type' => 'dialog',
+ 'data-dialog-renderer' => 'off_canvas',
],
- 'data-dialog-type' => 'dialog',
- 'data-dialog-renderer' => 'off_canvas',
],
- ],
- 'layout-builder__section' => $build,
- ];
+ ];
+ }
+ return $section_container;
+ }
+
+ /**
+ * Determines if the component in the default translation is translatable.
+ *
+ * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
+ * The section storage.
+ * @param \Drupal\layout_builder\SectionComponent $component
+ * The component to check.
+ *
+ * @return bool
+ * TRUE if the default component has translatable settings, otherwise FALSE.
+ */
+ protected function defaultComponentHasTranslatableSettings(SectionStorageInterface $section_storage, SectionComponent $component) {
+ if ($section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation()) {
+ foreach ($section_storage->getDefaultTranslationSections() as $default_translation_section) {
+ if ($default_component = $default_translation_section->getComponent($component->getUuid())) {
+ $plugin = $default_component->getPlugin();
+ if ($plugin instanceof ConfigurableInterface) {
+ $configuration = $plugin->getConfiguration();
+ return !empty($configuration['label_display']) && !empty($configuration['label']);
+ }
+ }
+ }
+ }
+ return FALSE;
}
}
diff --git a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
index 31233cdfc3..c7ecf6d844 100644
--- a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
@@ -13,7 +13,6 @@
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\layout_builder\OverridesSectionStorageInterface;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
@@ -202,6 +201,7 @@ protected function addSectionField($entity_type_id, $bundle, $field_name) {
'type' => 'layout_section',
'locked' => TRUE,
]);
+ $field_storage->setTranslatable(FALSE);
$field_storage->save();
}
@@ -289,11 +289,6 @@ protected function buildSections(FieldableEntityInterface $entity) {
$cacheability = new CacheableMetadata();
$storage = $this->sectionStorageManager()->findByContext($contexts, $cacheability);
- if ($storage instanceof OverridesSectionStorageInterface) {
- if ($storage->isTranslatable() && !$storage->isDefaultTranslation() && !$storage->isOverridden()) {
- $storage = $storage->getDefaultTranslationSectionStorage();
- }
- }
$build = [];
if ($storage) {
diff --git a/core/modules/layout_builder/src/EventSubscriber/ComponentPluginLabelTranslate.php b/core/modules/layout_builder/src/EventSubscriber/ComponentPluginLabelTranslate.php
new file mode 100644
index 0000000000..88fcf6e124
--- /dev/null
+++ b/core/modules/layout_builder/src/EventSubscriber/ComponentPluginLabelTranslate.php
@@ -0,0 +1,46 @@
+getPlugin();
+ $contexts = $event->getContexts();
+ if (!$plugin instanceof ConfigurableInterface && !isset($contexts['@language.current_language_context:language_interface'])) {
+ return;
+ }
+
+ /** @var \Drupal\Core\Language\Language $language */
+ $language = $contexts['@language.current_language_context:language_interface']->getContextValue();
+ $langcode = $language->getId();
+ $configuration = $plugin->getConfiguration();
+ if (isset($configuration['label']) && isset($configuration['layout_builder_translations'][$langcode]['label'])) {
+ $configuration['label'] = $configuration['layout_builder_translations'][$langcode]['label'];
+ $plugin->setConfiguration($configuration);
+ }
+ }
+
+}
diff --git a/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php b/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php
new file mode 100644
index 0000000000..93ba86a782
--- /dev/null
+++ b/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php
@@ -0,0 +1,84 @@
+current_language = $current_language;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('language_manager')->getCurrentLanguage()->getId()
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+ if ($this->plugin instanceof ConfigurableInterface) {
+ $configuration = $this->plugin->getConfiguration();
+ $form['translated_label'] = [
+ '#title' => $this->t('Translated label'),
+ '#type' => 'textfield',
+ '#default_value' => isset($configuration['layout_builder_translations'][$this->current_language]['label']) ? $configuration['layout_builder_translations'][$this->current_language]['label'] : $configuration['label'],
+ '#required' => TRUE,
+ ];
+ }
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+ if ($this->plugin instanceof ConfigurableInterface) {
+ $configuration = $this->plugin->getConfiguration();
+ $configuration['layout_builder_translations'][$this->current_language]['label'] = $form_state->getValue('translated_label');
+ $this->plugin->setConfiguration($configuration);
+ }
+ }
+
+ /**
+ *
+ * @return \Drupal\Core\Plugin\PluginFormInterface
+ */
+ protected function getConfigureForm() {
+ /** @var \Drupal\Core\Plugin\PluginFormFactoryInterface $form_factory */
+ $form_factory = \Drupal::service('plugin_form.factory');
+ return $form_factory->createInstance($this->plugin, 'configure');
+ }
+
+}
diff --git a/core/modules/layout_builder/src/Form/TranslateBlockForm.php b/core/modules/layout_builder/src/Form/TranslateBlockForm.php
new file mode 100644
index 0000000000..67d845fe6c
--- /dev/null
+++ b/core/modules/layout_builder/src/Form/TranslateBlockForm.php
@@ -0,0 +1,63 @@
+t('Save');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ return 'layout_builder_block_translation';
+ }
+
+ /**
+ * Builds the block form.
+ *
+ * @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 $uuid
+ * The UUID of the block being updated.
+ *
+ * @return array
+ * The form array.
+ */
+ public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
+ $component = $section_storage->getSection($delta)->getComponent($uuid);
+ return $this->doBuildForm($form, $form_state, $section_storage, $delta, $component);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getPluginForm(BlockPluginInterface $block) {
+ if ($block instanceof PluginWithFormsInterface) {
+ return $this->pluginFormFactory->createInstance($block, 'layout_builder_translation');
+ }
+ return $block;
+ }
+
+}
diff --git a/core/modules/layout_builder/src/InlineBlockEntityOperations.php b/core/modules/layout_builder/src/InlineBlockEntityOperations.php
index 4f0648ba64..55eab3b75e 100644
--- a/core/modules/layout_builder/src/InlineBlockEntityOperations.php
+++ b/core/modules/layout_builder/src/InlineBlockEntityOperations.php
@@ -7,7 +7,6 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\RevisionableInterface;
-use Drupal\Core\Entity\TranslatableInterface;
use Drupal\layout_builder\Plugin\Block\InlineBlock;
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -177,7 +176,6 @@ public function handlePreSave(EntityInterface $entity) {
// duplicated.
$duplicate_blocks = TRUE;
}
-
$new_revision = FALSE;
if ($entity instanceof RevisionableInterface) {
// If the parent entity will have a new revision create a new revision
@@ -283,28 +281,4 @@ protected function saveInlineBlockComponent(EntityInterface $entity, SectionComp
$component->setConfiguration($post_save_configuration);
}
- /**
- * Determines if an entity is a new translation.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity to check.
- *
- * @return bool
- * TRUE if the entity has a new layout translation, otherwise FALSE.
- */
- private function hasNewLayoutTranslation(EntityInterface $entity) {
-
- if (isset($entity->original) && $entity instanceof TranslatableInterface && $entity->isTranslatable() && !$entity->isDefaultTranslation()) {
- // Get the unchanged translation. $entity->original will not work here.
- // @todo Confirm ^
- $unchanged_translation = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id())->getTranslation($entity->language()->getId());
- assert($entity->language()->getId() == $unchanged_translation->language()->getId());
- $sections = $this->getEntitySections($unchanged_translation);
- if (empty($sections)) {
- return TRUE;
- }
- }
- return FALSE;
- }
-
}
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
index 9c222871d8..6b9954d036 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
@@ -3,9 +3,7 @@
namespace Drupal\layout_builder\Plugin\SectionStorage;
use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeInterface;
@@ -17,12 +15,11 @@
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
use Drupal\layout_builder\OverridesSectionStorageInterface;
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
-use Drupal\layout_builder\TranslatableOverridesSectionStorageInterface;
+use Drupal\layout_builder\TranslatableSectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RouteCollection;
@@ -51,7 +48,7 @@
* experimental modules and development releases of contributed modules.
* See https://www.drupal.org/core/experimental for more information.
*/
-class OverridesSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, TranslatableOverridesSectionStorageInterface, SectionStorageLocalTaskProviderInterface {
+class OverridesSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, OverridesSectionStorageInterface, TranslatableSectionStorageInterface, SectionStorageLocalTaskProviderInterface {
/**
* The field name used by this storage.
@@ -366,11 +363,7 @@ public function save() {
public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
$default_section_storage = $this->getDefaultSectionStorage();
$result = AccessResult::allowedIf($default_section_storage->isLayoutBuilderEnabled())->addCacheableDependency($default_section_storage);
- $entity = $this->getEntity();
- $result->addCacheableDependency($entity);
- if ($entity instanceof TranslatableInterface && !$entity->isDefaultTranslation()) {
- $result = $result->andIf(AccessResult::allowedIf($this->isTranslatable()));
- }
+ $result = $result->andIf(AccessResult::allowedIf($this->isDefaultTranslation() || $this->isOverridden()))->addCacheableDependency($this);
return $return_as_object ? $result : $result->isAllowed();
}
@@ -381,18 +374,7 @@ public function isApplicable(RefinableCacheableDependencyInterface $cacheability
$default_section_storage = $this->getDefaultSectionStorage();
$cacheability->addCacheableDependency($default_section_storage)->addCacheableDependency($this);
// Check that overrides are enabled and have at least one section.
- if ($default_section_storage->isOverridable()) {
- if ($this->isOverridden()) {
- return TRUE;
- }
- if ($this->isTranslatable() && !$this->isDefaultTranslation()) {
- $default_translation_section_storage = $this->getDefaultTranslationSectionStorage();
- if ($default_translation_section_storage instanceof OverridesSectionStorageInterface) {
- return $default_translation_section_storage->isOverridden();
- }
- }
- }
- return FALSE;
+ return $default_section_storage->isOverridable() && $this->isOverridden();
}
/**
@@ -411,8 +393,9 @@ public function isOverridden() {
public function isTranslatable() {
$entity = $this->getEntity();
if ($entity instanceof TranslatableInterface) {
- $translatable_fields = $entity->getTranslatableFields(FALSE);
- return array_key_exists(static::FIELD_NAME, $translatable_fields) && $entity->isTranslatable();
+ return $entity->isTranslatable();
+ /*$translatable_fields = $entity->getTranslatableFields(FALSE);
+ return array_key_exists(static::FIELD_NAME, $translatable_fields) && $entity->isTranslatable();*/
}
return FALSE;
}
@@ -426,43 +409,22 @@ public function isDefaultTranslation() {
$entity = $this->getEntity();
return $entity->isDefaultTranslation();
}
- return FALSE;
+ // @todo If not translatable should we always consider this the default
+ // translation?
+ return TRUE;
}
/**
* {@inheritdoc}
*/
- public function getDefaultTranslationSectionStorage() {
+ public function getDefaultTranslationSections() {
if ($this->isTranslatable()) {
- if ($this->isDefaultTranslation()) {
- return $this;
- }
- else {
- /** @var \Drupal\Core\Entity\TranslatableInterface $entity */
- $entity = $this->getEntity();
- $untranslated_entity = $entity->getUntranslated();
- // @todo Expand to work for all view modes in
- // https://www.drupal.org/node/2907413.
- $view_mode = 'full';
- // Retrieve the actual view mode from the returned view display as the
- // requested view mode may not exist and a fallback will be used.
- $view_mode = LayoutBuilderEntityViewDisplay::collectRenderDisplay($entity, $view_mode)->getMode();
- $contexts = [
- 'view_mode' => new Context(ContextDefinition::create('string'), $view_mode),
- 'entity' => EntityContext::fromEntity($untranslated_entity),
- 'display' => EntityContext::fromEntity(EntityViewDisplay::collectRenderDisplay($untranslated_entity, 'full')),
- ];
- $label = new TranslatableMarkup('@entity being viewed', [
- '@entity' => $untranslated_entity->getEntityType()->getSingularLabel(),
- ]);
- $contexts['layout_builder.entity'] = EntityContext::fromEntity($untranslated_entity, $label);
-
-
- $cacheability = new CacheableMetadata();
- return $this->sectionStorageManager->findByContext($contexts, $cacheability);
- }
+ /** @var \Drupal\Core\Entity\TranslatableInterface $entity */
+ $entity = $this->getEntity();
+ $untranslated_entity = $entity->getUntranslated();
+ return $untranslated_entity->get(static::FIELD_NAME)->getSections();
}
- return NULL;
+ return [];
}
}
diff --git a/core/modules/layout_builder/src/TranslatableOverridesSectionStorageInterface.php b/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php
similarity index 70%
rename from core/modules/layout_builder/src/TranslatableOverridesSectionStorageInterface.php
rename to core/modules/layout_builder/src/TranslatableSectionStorageInterface.php
index d20cd7c042..d9326afe53 100644
--- a/core/modules/layout_builder/src/TranslatableOverridesSectionStorageInterface.php
+++ b/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php
@@ -10,7 +10,7 @@
* experimental modules and development releases of contributed modules.
* See https://www.drupal.org/core/experimental for more information.
*/
-interface TranslatableOverridesSectionStorageInterface extends OverridesSectionStorageInterface {
+interface TranslatableSectionStorageInterface {
/**
* Indicates if the layout is translatable.
@@ -29,11 +29,11 @@ public function isTranslatable();
public function isDefaultTranslation();
/**
- * Gets the default translation section storage.
+ * Gets the layout default translation sections.
*
- * @return \Drupal\layout_builder\SectionStorageInterface
- * The section storage used by the default translation.
+ * @return \Drupal\layout_builder\Section[]
+ * A sequentially and numerically keyed array of section objects.
*/
- public function getDefaultTranslationSectionStorage();
+ public function getDefaultTranslationSections();
}
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
index ebf2c83e8f..841d4867ea 100644
--- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php
@@ -18,6 +18,7 @@ class LayoutBuilderTranslationTest extends ContentTranslationTestBase {
*/
public static $modules = [
'content_translation',
+ 'contextual',
'entity_test',
'layout_builder',
'block',
@@ -57,9 +58,10 @@ public function testLayoutPerTranslation() {
$assert_session->pageTextNotContains('The translated field value');
$assert_session->pageTextContains('The untranslated field value');
+ // If there is not a layout override the layout translation is not
+ // accessible.
$this->drupalGet($translated_layout_url);
- $assert_session->pageTextNotContains('The untranslated field value');
- $assert_session->pageTextContains('The translated field value');
+ $assert_session->pageTextContains('Access denied');
// Ensure that the tempstore varies per-translation.
$this->drupalGet($layout_url);
@@ -77,7 +79,7 @@ public function testLayoutPerTranslation() {
// Confirm the tempstore for the translated layout is not affected.
$this->drupalGet($translated_layout_url);
- $assert_session->pageTextNotContains('Powered by Drupal');
+ $assert_session->pageTextContains('Access denied');
$this->drupalGet($layout_url);
$assert_session->pageTextContains('Powered by Drupal');
@@ -95,25 +97,25 @@ public function testLayoutPerTranslation() {
$assert_session->pageTextContains('The translated field value');
$assert_session->pageTextContains('Powered by Drupal');
- // The translated entity's unaltered layout still persists in the tempstore.
+ // The translate layout is not available.
$this->drupalGet($translated_layout_url);
+ $assert_session->pageTextNotContains('Access denied');
$assert_session->pageTextNotContains('The untranslated field value');
$assert_session->pageTextContains('The translated field value');
- $assert_session->pageTextNotContains('Powered by Drupal');
+ $assert_session->pageTextContains('Powered by Drupal');
$assert_session->buttonExists('Save layout');
- $page->pressButton('Save layout');
- $assert_session->addressEquals($translated_entity_url);
- $assert_session->pageTextNotContains('The untranslated field value');
- $assert_session->pageTextContains('The translated field value');
- $assert_session->pageTextNotContains('Powered by Drupal');
+ // Confirm that links do not exist to change the layout.
+ $assert_session->linkNotExists('Add Section');
+ $assert_session->linkNotExists('Add Block');
+ $assert_session->linkNotExists('Remove section');
}
/**
- * Tests creating an override on a translation without an existing override.
+ * Tests that access is denied to a layout translation if there is override.
*/
- public function testLayoutTranslationFromDefault() {
+ public function testLayoutTranslationNoOverride() {
$assert_session = $this->assertSession();
$page = $this->getSession()->getPage();
@@ -131,52 +133,10 @@ public function testLayoutTranslationFromDefault() {
$assert_session->pageTextNotContains('The untranslated field value');
$assert_session->pageTextContains('The translated field value');
+ // If there is not a layout override the layout translation is not
+ // accessible.
$this->drupalGet($translated_layout_url);
- $assert_session->pageTextNotContains('The untranslated field value');
- $assert_session->pageTextContains('The translated field value');
-
- // Adjust the layout of the translated entity.
- $assert_session->linkExists('Add Block');
- $this->clickLink('Add Block');
- $assert_session->linkExists('Powered by Drupal');
- $this->clickLink('Powered by Drupal');
- $page->pressButton('Add Block');
-
- $assert_session->pageTextContains('Powered by Drupal');
-
- // Confirm the tempstore for the translated layout is not affected.
- $this->drupalGet($layout_url);
- $assert_session->pageTextNotContains('Powered by Drupal');
-
- $this->drupalGet($translated_layout_url);
- $assert_session->pageTextContains('Powered by Drupal');
- $assert_session->buttonExists('Save layout');
- $page->pressButton('Save layout');
-
- // Ensure that the layout is on the translated entity.
- $this->drupalGet($translated_entity_url);
- $assert_session->pageTextNotContains('The untranslated field value');
- $assert_session->pageTextContains('The translated field value');
- $assert_session->pageTextContains('Powered by Drupal');
-
- $this->drupalGet($entity_url);
- $assert_session->pageTextNotContains('The translated field value');
- $assert_session->pageTextContains('The untranslated field value');
- $assert_session->pageTextNotContains('Powered by Drupal');
-
- // The untranslated entity's unaltered layout still persists in the
- // tempstore.
- $this->drupalGet($layout_url);
- $assert_session->pageTextContains('The untranslated field value');
- $assert_session->pageTextNotContains('The translated field value');
- $assert_session->pageTextNotContains('Powered by Drupal');
- $assert_session->buttonExists('Save layout');
- $page->pressButton('Save layout');
-
- $assert_session->addressEquals($entity_url);
- $assert_session->pageTextContains('The untranslated field value');
- $assert_session->pageTextNotContains('The translated field value');
- $assert_session->pageTextNotContains('Powered by Drupal');
+ $assert_session->pageTextContains('Access denied');
}
}
diff --git a/core/modules/layout_builder/tests/src/Functional/UntranslatableLayoutTest.php b/core/modules/layout_builder/tests/src/Functional/UntranslatableLayoutTest.php
deleted file mode 100644
index daf54b7de5..0000000000
--- a/core/modules/layout_builder/tests/src/Functional/UntranslatableLayoutTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-setUpViewDisplay();
- $this->setUpEntities();
-
- $field_config = FieldConfig::loadByName($this->entityTypeId, $this->bundle, OverridesSectionStorage::FIELD_NAME);
- $field_config->setTranslatable(FALSE);
- $field_config->save();
- }
-
- /**
- * Tests that layout translations are not available.
- */
- public function testLayoutTranslationDenied() {
- $assert_session = $this->assertSession();
-
- $entity_url = $this->entity->toUrl('canonical')->toString();
- $language = \Drupal::languageManager()->getLanguage($this->langcodes[2]);
- $translated_entity_url = $this->entity->toUrl('canonical', ['language' => $language])->toString();
- $layout_url = $entity_url . '/layout';
- $translated_layout_url = $translated_entity_url . '/layout';
-
- $this->drupalGet($entity_url);
- $assert_session->pageTextNotContains('The translated field value');
- $assert_session->pageTextContains('The untranslated field value');
- $assert_session->linkExists('Layout');
-
- $this->drupalGet($translated_entity_url);
- $assert_session->pageTextNotContains('The untranslated field value');
- $assert_session->pageTextContains('The translated field value');
- $assert_session->linkNotExists('Layout');
-
- $this->drupalGet($layout_url);
- $assert_session->pageTextNotContains('Access denied');
-
- $this->drupalGet($translated_layout_url);
- $assert_session->pageTextContains('Access denied');
- }
-
-}
diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php
deleted file mode 100644
index d177753665..0000000000
--- a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php
+++ /dev/null
@@ -1,253 +0,0 @@
-save();
-
- // Enable translation for the node type 'bundle_with_section_field'.
- \Drupal::service('content_translation.manager')->setEnabled('node', 'bundle_with_section_field', TRUE);
- drupal_static_reset();
- \Drupal::entityTypeManager()->clearCachedDefinitions();
- \Drupal::service('router.builder')->rebuild();
- \Drupal::service('entity.definition_update_manager')->applyUpdates();
-
- $this->rebuildContainer();
- }
-
- /**
- * Tests that inline blocks works with content translation.
- */
- public function testInlineBlockContentTranslation() {
- $assert_session = $this->assertSession();
-
- $this->drupalLogin($this->drupalCreateUser([
- 'access contextual links',
- 'configure any layout',
- 'administer node display',
- 'administer node fields',
- 'translate bundle_with_section_field node',
- 'create content translations',
- ]));
-
- // Allow layout overrides.
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[enabled]' => TRUE],
- 'Save'
- );
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[allow_custom]' => TRUE],
- 'Save'
- );
-
- // Add a new inline block to the original node.
- $this->drupalGet('node/1/layout');
- $this->addInlineBlockToLayout('Block en', 'Block en body');
- $this->assertSaveLayout();
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('Block en');
- $assert_session->pageTextContains('Block en body');
-
- // Create a translation.
- $add_translation_url = Url::fromRoute("entity.node.content_translation_add", [
- 'node' => 1,
- 'source' => 'en',
- 'target' => 'it',
- ]);
- $this->drupalPostForm($add_translation_url, [
- 'title[0][value]' => 'The translated node title',
- 'body[0][value]' => 'The translated node body',
- ], 'Save');
-
- // Update the translate node's inline block.
- $this->drupalGet('it/node/1/layout');
- $this->configureInlineBlock('Block en body', 'Block it body');
- $this->assertSaveLayout();
-
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('Block en body');
- $assert_session->pageTextNotContains('Block it body');
-
- $this->drupalGet('it/node/1');
- $assert_session->pageTextContains('Block it body');
- $assert_session->pageTextNotContains('Block en body');
-
- // Update the original node's inline block.
- $this->drupalGet('node/1/layout');
- $this->configureInlineBlock('Block en body', 'Block en body updated');
- $this->assertSaveLayout();
-
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('Block en body updated');
- $assert_session->pageTextNotContains('Block it body');
-
- $this->drupalGet('it/node/1');
- $assert_session->pageTextContains('Block it body');
- $assert_session->pageTextNotContains('Block en body updated');
- }
-
- /**
- * Tests that an translated entity can override the layout from default.
- */
- public function testInlineBlockContentTranslationOverrideFromDefault() {
- $assert_session = $this->assertSession();
-
- $this->drupalLogin($this->drupalCreateUser([
- 'access contextual links',
- 'configure any layout',
- 'administer node display',
- 'administer node fields',
- 'translate bundle_with_section_field node',
- 'create content translations',
- ]));
-
- // Allow layout overrides.
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[enabled]' => TRUE],
- 'Save'
- );
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[allow_custom]' => TRUE],
- 'Save'
- );
-
- // Create a translation.
- $add_translation_url = Url::fromRoute("entity.node.content_translation_add", [
- 'node' => 1,
- 'source' => 'en',
- 'target' => 'it',
- ]);
- $this->drupalPostForm($add_translation_url, [
- 'title[0][value]' => 'The translated node title',
- 'body[0][value]' => 'The translated node body',
- ], 'Save');
-
- // Add an inline block to the default layout.
- $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
- $this->clickLink('Manage layout');
- $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display/default/layout');
- $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
- $this->assertSaveLayout();
-
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('The DEFAULT block body');
- $this->drupalGet('it/node/1');
- $assert_session->pageTextContains('The DEFAULT block body');
-
- // Override the translated node's layout.
- $this->drupalGet('it/node/1/layout');
- $this->configureInlineBlock('The DEFAULT block body', 'Overridden block body');
- $this->assertSaveLayout();
-
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('The DEFAULT block body');
- $assert_session->pageTextNotContains('Overridden block body');
- $this->drupalGet('it/node/1');
- $assert_session->pageTextContains('Overridden block body');
- $assert_session->pageTextNotContains('The DEFAULT block body');
- }
-
- /**
- * Tests deleting an translated entity with inline block.
- */
- public function testDeletingTranslatedEntityWithInlineBlock() {
- /** @var \Drupal\Core\Cron $cron */
- $cron = \Drupal::service('cron');
- /** @var \Drupal\layout_builder\InlineBlockUsage $usage */
- $usage = \Drupal::service('inline_block.usage');
-
- $assert_session = $this->assertSession();
-
- $this->drupalLogin($this->drupalCreateUser([
- 'access contextual links',
- 'configure any layout',
- 'administer node display',
- 'administer node fields',
- 'translate bundle_with_section_field node',
- 'create content translations',
- 'delete content translations',
- ]));
-
- // Allow layout overrides.
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[enabled]' => TRUE],
- 'Save'
- );
- $this->drupalPostForm(
- static::FIELD_UI_PREFIX . '/display/default',
- ['layout[allow_custom]' => TRUE],
- 'Save'
- );
-
- // Create a translation.
- $add_translation_url = Url::fromRoute("entity.node.content_translation_add", [
- 'node' => 1,
- 'source' => 'en',
- 'target' => 'it',
- ]);
- $this->drupalPostForm($add_translation_url, [
- 'title[0][value]' => 'The translated node title',
- 'body[0][value]' => 'The translated node body',
- ], 'Save');
-
- // Override the translated node's layout.
- $this->drupalGet('it/node/1/layout');
- $this->addInlineBlockToLayout('Block it title', 'Block it body');
- $this->assertSaveLayout();
- $it_block = $this->getLatestBlockEntityId();
- $this->assertCount(1, $this->blockStorage->loadMultiple());
-
- // Add an inline block to the original node.
- $this->drupalGet('node/1/layout');
- $this->addInlineBlockToLayout('Block en title', 'Block en body');
- $this->assertSaveLayout();
- $this->assertCount(2, $this->blockStorage->loadMultiple());
-
- // Remove the translation.
- $delete_translation_url = Url::fromRoute('entity.node.content_translation_delete', [
- 'node' => 1,
- 'language' => 'it',
- ]);
- $this->drupalGet($delete_translation_url);
- $this->drupalPostForm(NULL, [], 'Delete Italian translation');
-
- $cron->run();
-
- $this->blockStorage->resetCache([$it_block]);
- $this->assertEmpty($this->blockStorage->load($it_block));
- $this->assertCount(1, $this->blockStorage->loadMultiple());
- $this->assertEmpty($usage->getUsage($it_block));
-
- $this->drupalGet('node/1');
- $assert_session->pageTextContains('Block en body');
- }
-
-}