diff --git a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php index fe2723c91c..b536709ab5 100644 --- a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php +++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php @@ -37,7 +37,7 @@ public function title(SectionStorageInterface $section_storage) { * @return array * A render array. */ - public function layout(SectionStorageInterface $section_storage, LanguageInterface $language) { + public function layout(SectionStorageInterface $section_storage, LanguageInterface $language = NULL) { return [ '#type' => 'layout_builder', '#section_storage' => $section_storage, diff --git a/core/modules/layout_builder/src/Element/LayoutBuilder.php b/core/modules/layout_builder/src/Element/LayoutBuilder.php index 32399783e8..9bd813415c 100644 --- a/core/modules/layout_builder/src/Element/LayoutBuilder.php +++ b/core/modules/layout_builder/src/Element/LayoutBuilder.php @@ -382,9 +382,6 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s /** * Determines if the component is translatable. * - * @todo determine how handle other settings that need to be translated - * such as the inline blocks use case. - * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * @param \Drupal\layout_builder\SectionComponent $component @@ -396,11 +393,13 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s protected function componentHasTranslatableConfiguration(SectionStorageInterface $section_storage, SectionComponent $component) { if ($section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation()) { $plugin = $component->getPlugin(); - $contexts = $section_storage->getContexts(); if ($plugin instanceof LayoutBuilderTranslatablePluginInterface) { return $plugin->hasTranslatableConfiguration(); } elseif ($plugin instanceof ConfigurableInterface) { + // For all plugins that do not implement + // LayoutBuilderTranslatablePluginInterface only allow label + // translation. $configuration = $plugin->getConfiguration(); return !empty($configuration['label_display']) && !empty($configuration['label']); } diff --git a/core/modules/layout_builder/src/EventSubscriber/ComponentPluginTranslate.php b/core/modules/layout_builder/src/EventSubscriber/ComponentPluginTranslate.php index e56100c649..234d165c08 100644 --- a/core/modules/layout_builder/src/EventSubscriber/ComponentPluginTranslate.php +++ b/core/modules/layout_builder/src/EventSubscriber/ComponentPluginTranslate.php @@ -3,11 +3,10 @@ namespace Drupal\layout_builder\EventSubscriber; use Drupal\Component\Plugin\ConfigurableInterface; -use Drupal\Core\Entity\FieldableEntityInterface; -use Drupal\Core\Entity\TranslatableInterface; use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent; use Drupal\layout_builder\LayoutBuilderEvents; -use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage; +use Drupal\layout_builder\LayoutEntityHelperTrait; +use Drupal\layout_builder\TranslatableSectionStorageInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -15,6 +14,8 @@ */ class ComponentPluginTranslate implements EventSubscriberInterface { + use LayoutEntityHelperTrait; + /** * {@inheritdoc} */ @@ -37,17 +38,13 @@ public function onBuildRender(SectionComponentBuildRenderArrayEvent $event) { return; } - /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ $entity = $contexts['layout_builder.entity']->getContextValue(); - if ($entity instanceof FieldableEntityInterface && $entity instanceof TranslatableInterface && !$entity->isDefaultTranslation() && $entity->hasField(OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME)) { - $configuration = $plugin->getConfiguration(); - if (!$entity->get(OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME)->isEmpty()) { - $translated_layout_configuration = $entity->get(OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME)->get(0)->getValue(); - if (isset($translated_layout_configuration['value']['components'][$component->getUuid()])) { - $translated_plugin_configuration = $translated_layout_configuration['value']['components'][$component->getUuid()]; - $translated_plugin_configuration += $configuration; - $plugin->setConfiguration($translated_plugin_configuration); - } + $configuration = $plugin->getConfiguration(); + $section_storage = $this->getSectionStorageForEntity($entity); + if ($section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation()) { + if ($translated_plugin_configuration = $section_storage->getTranslatedComponentConfiguration($component->getUuid())) { + $translated_plugin_configuration += $configuration; + $plugin->setConfiguration($translated_plugin_configuration); } } } diff --git a/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php b/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php index c5572e3802..422cf1fac6 100644 --- a/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php +++ b/core/modules/layout_builder/src/Form/BlockPluginTranslationForm.php @@ -27,6 +27,8 @@ class BlockPluginTranslationForm extends PluginFormBase implements ContainerInje protected $currentLangcode; /** + * The translated configuration for the plugin. + * * @var array */ protected $translatedConfiguration; diff --git a/core/modules/layout_builder/src/Plugin/Block/InlineBlockTranslationForm.php b/core/modules/layout_builder/src/Form/InlineBlockTranslationForm.php similarity index 78% rename from core/modules/layout_builder/src/Plugin/Block/InlineBlockTranslationForm.php rename to core/modules/layout_builder/src/Form/InlineBlockTranslationForm.php index 7e4fb66965..cff79484bf 100644 --- a/core/modules/layout_builder/src/Plugin/Block/InlineBlockTranslationForm.php +++ b/core/modules/layout_builder/src/Form/InlineBlockTranslationForm.php @@ -1,17 +1,17 @@ entityTypeManager = $entity_type_manager; + $this->entityRepository = $entity_repository; } /** @@ -56,7 +65,8 @@ public function __construct($current_langcode, EntityTypeManagerInterface $entit public static function create(ContainerInterface $container) { return new static( $container->get('language_manager')->getCurrentLanguage()->getId(), - $container->get('entity_type.manager') + $container->get('entity_type.manager'), + $container->get('entity.repository') ); } @@ -64,6 +74,19 @@ public static function create(ContainerInterface $container) { * {@inheritdoc} */ protected function getEntity() { + if (!empty($this->translatedConfiguration)) { + if (!empty($this->translatedConfiguration['block_serialized'])) { + return unserialize($this->translatedConfiguration['block_serialized']); + } + elseif (!empty($this->translatedConfiguration['block_revision_id'])) { + /** @var \Drupal\block_content\BlockContentInterface $entity */ + $entity = $this->entityTypeManager->getStorage('block_content')->loadRevision($this->translatedConfiguration['block_revision_id']); + $entity = $this->entityRepository->getActive('block_content', $entity->id()); + if ($entity->hasTranslation($this->currentLangcode)) { + return $entity->getTranslation($this->currentLangcode); + } + } + } $configuration = $this->plugin->getConfiguration(); if (!empty($configuration['block_serialized'])) { return unserialize($configuration['block_serialized']); @@ -71,6 +94,7 @@ protected function getEntity() { elseif (!empty($configuration['block_revision_id'])) { /** @var \Drupal\block_content\BlockContentInterface $entity */ $entity = $this->entityTypeManager->getStorage('block_content')->loadRevision($configuration['block_revision_id']); + $entity = $this->entityRepository->getActive('block_content', $entity->id()); if ($entity->hasTranslation($this->currentLangcode)) { return $entity->getTranslation($this->currentLangcode); } @@ -159,9 +183,8 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s $complete_form_state = $form_state instanceof SubformStateInterface ? $form_state->getCompleteFormState() : $form_state; $form_display->extractFormValues($block, $block_form, $complete_form_state); - $configuration = $this->plugin->getConfiguration(); - $configuration['block_serialized'] = serialize($block); - $this->plugin->setConfiguration($configuration); + $form_state->setValue('block_serialized', serialize($block)); + $form_state->unsetValue('block_form'); } } diff --git a/core/modules/layout_builder/src/Form/LayoutBuilderEntityViewDisplayForm.php b/core/modules/layout_builder/src/Form/LayoutBuilderEntityViewDisplayForm.php index 788f0b55e8..56112b6912 100644 --- a/core/modules/layout_builder/src/Form/LayoutBuilderEntityViewDisplayForm.php +++ b/core/modules/layout_builder/src/Form/LayoutBuilderEntityViewDisplayForm.php @@ -70,7 +70,6 @@ public function form(array $form, FormStateInterface $form_state) { '#access' => $is_enabled, ]; - if ($this->sectionStorage instanceof TranslatableSectionStorageInterface && $this->sectionStorage->isTranslatable()) { $form['layout_translations'] = [ '#type' => 'details', @@ -82,7 +81,7 @@ public function form(array $form, FormStateInterface $form_state) { if ($language->isDefault()) { continue; } - $url = $this->sectionStorage->getLayoutBuilderUrl(); + $url = $this->sectionStorage->getLayoutBuilderUrl(); $form['layout_translations'][$language->getId()] = [ '#type' => 'link', '#title' => $language->getName(), diff --git a/core/modules/layout_builder/src/Form/OverridesEntityForm.php b/core/modules/layout_builder/src/Form/OverridesEntityForm.php index fc37d4048b..125526542f 100644 --- a/core/modules/layout_builder/src/Form/OverridesEntityForm.php +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php @@ -88,32 +88,24 @@ protected function init(FormStateInterface $form_state) { // Allow modules to choose if they are relevant to the layout form. $this->moduleHandler->alter('layout_builder_overrides_entity_form_display', $display); - // Add the widget for Layout Builder after the alter. + // Add the widget for Layout Builder after the alter. + if ($this->sectionStorage instanceof TranslatableSectionStorageInterface && !$this->sectionStorage->isDefaultTranslation()) { + $display->setComponent(OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME, [ + 'type' => 'layout_builder_widget', + 'weight' => -10, + 'settings' => [], + ]); + } + else { $display->setComponent(OverridesSectionStorage::FIELD_NAME, [ 'type' => 'layout_builder_widget', 'weight' => -10, 'settings' => [], ]); - - - - $this->setFormDisplay($display, $form_state); - } - - /** - * @inheritDoc - */ - public function buildEntity(array $form, FormStateInterface $form_state) { - $entity = parent::buildEntity($form, $form_state); - if ($this->sectionStorage instanceof TranslatableSectionStorageInterface && !$this->sectionStorage->isDefaultTranslation()) { - // @todo get sections from unchanged entity? You shouldn't override layout on non-default - // should TRANSLATED_CONFIGURATION_FIELD_NAME have it's own form and widget? - $entity->set(OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME, [$this->sectionStorage->getTranslatedConfiguration()]); } - return $entity; + $this->setFormDisplay($display, $form_state); } - /** * {@inheritdoc} */ diff --git a/core/modules/layout_builder/src/InlineBlockEntityOperations.php b/core/modules/layout_builder/src/InlineBlockEntityOperations.php index 55eab3b75e..a2cc6d4270 100644 --- a/core/modules/layout_builder/src/InlineBlockEntityOperations.php +++ b/core/modules/layout_builder/src/InlineBlockEntityOperations.php @@ -2,6 +2,7 @@ namespace Drupal\layout_builder; +use Drupal\Core\Block\BlockManagerInterface; use Drupal\Core\Database\Connection; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityInterface; @@ -41,6 +42,13 @@ class InlineBlockEntityOperations implements ContainerInjectionInterface { */ protected $entityTypeManager; + /** + * The block plugin manager. + * + * @var \Drupal\Core\Block\BlockManagerInterface + */ + protected $blockManager; + /** * Constructs a new EntityOperations object. * @@ -65,7 +73,7 @@ class InlineBlockEntityOperations implements ContainerInjectionInterface { * - The $database parameter is unused and should be removed. * Deprecate in https://www.drupal.org/node/3031492. */ - public function __construct(EntityTypeManagerInterface $entityTypeManager, InlineBlockUsage $usage, Connection $database, SectionStorageManagerInterface $section_storage_manager = NULL) { + public function __construct(EntityTypeManagerInterface $entityTypeManager, InlineBlockUsage $usage, Connection $database, SectionStorageManagerInterface $section_storage_manager = NULL, BlockManagerInterface $block_manager) { $this->entityTypeManager = $entityTypeManager; $this->blockContentStorage = $entityTypeManager->getStorage('block_content'); $this->usage = $usage; @@ -74,6 +82,7 @@ public function __construct(EntityTypeManagerInterface $entityTypeManager, Inlin $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage'); } $this->sectionStorageManager = $section_storage_manager; + $this->blockManager = $block_manager; } /** @@ -84,7 +93,8 @@ public static function create(ContainerInterface $container) { $container->get('entity_type.manager'), $container->get('inline_block.usage'), $container->get('database'), - $container->get('plugin.manager.layout_builder.section_storage') + $container->get('plugin.manager.layout_builder.section_storage'), + $container->get('plugin.manager.block') ); } @@ -188,8 +198,18 @@ public function handlePreSave(EntityInterface $entity) { $new_revision = TRUE; } + $section_storage = $this->getSectionStorageForEntity($entity); foreach ($this->getInlineBlockComponents($sections) as $component) { - $this->saveInlineBlockComponent($entity, $component, $new_revision, $duplicate_blocks); + if ($section_storage instanceof TranslatableSectionStorageInterface && !$section_storage->isDefaultTranslation()) { + $translated_component_configuration = $section_storage->getTranslatedComponentConfiguration($component->getUuid()); + if (isset($translated_component_configuration['block_serialized'])) { + $this->saveTranslatedInlineBlock($entity, $component->getUuid(), $translated_component_configuration, $new_revision); + } + + } + else { + $this->saveInlineBlockComponent($entity, $component, $new_revision, $duplicate_blocks); + } } } $this->removeUnusedForEntityOnSave($entity); @@ -281,4 +301,30 @@ protected function saveInlineBlockComponent(EntityInterface $entity, SectionComp $component->setConfiguration($post_save_configuration); } + /** + * Saves a translated inline block. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity with the layout. + * @param string $component_uuid + * The component UUID. + * @param array $translated_component_configuration + * The translated component configuration. + * @param bool $new_revision + * Whether a new revision of the block should be created. + */ + protected function saveTranslatedInlineBlock($entity, $component_uuid, $translated_component_configuration, $new_revision) { + /** @var \Drupal\block_content\BlockContentInterface $block */ + $block = unserialize($translated_component_configuration['block_serialized']); + /** @var \Drupal\layout_builder\Plugin\Block\InlineBlock $plugin */ + $plugin = $this->blockManager->createInstance('inline_block:' . $block->bundle(), $translated_component_configuration); + $plugin->saveBlockContent($new_revision); + $configuration = $plugin->getConfiguration(); + unset($translated_component_configuration['block_serialized']); + $translated_component_configuration['block_revision_id'] = $configuration['block_revision_id']; + /** @var \Drupal\layout_builder\TranslatableSectionStorageInterface $section_storage */ + $section_storage = $this->getSectionStorageForEntity($entity); + $section_storage->setTranslatedComponentConfiguration($component_uuid, $translated_component_configuration); + } + } diff --git a/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php b/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php index 44157c427b..7c05656750 100644 --- a/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php +++ b/core/modules/layout_builder/src/Plugin/Block/InlineBlock.php @@ -9,6 +9,7 @@ use Drupal\Core\Block\BlockBase; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\EntityDisplayRepositoryInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\SubformStateInterface; @@ -26,7 +27,7 @@ * category = @Translation("Inline blocks"), * deriver = "Drupal\layout_builder\Plugin\Derivative\InlineBlockDeriver", * forms = { - * "layout_builder_translation" = "\Drupal\layout_builder\Plugin\Block\InlineBlockTranslationForm", + * "layout_builder_translation" = "\Drupal\layout_builder\Form\InlineBlockTranslationForm", * }, * ) * @@ -65,6 +66,13 @@ class InlineBlock extends BlockBase implements ContainerFactoryPluginInterface, */ protected $isNew = TRUE; + /** + * The entity repository. + * + * @var \Drupal\Core\Entity\EntityRepositoryInterface + */ + protected $entityRepository; + /** * Constructs a new InlineBlock. * @@ -78,8 +86,10 @@ class InlineBlock extends BlockBase implements ContainerFactoryPluginInterface, * The entity type manager service. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository * The entity display repository. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository, EntityRepositoryInterface $entity_repository) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityTypeManager = $entity_type_manager; @@ -87,6 +97,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition if (!empty($this->configuration['block_revision_id']) || !empty($this->configuration['block_serialized'])) { $this->isNew = FALSE; } + $this->entityRepository = $entity_repository; } /** @@ -98,7 +109,8 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('entity_type.manager'), - $container->get('entity_display.repository') + $container->get('entity_display.repository'), + $container->get('entity.repository') ); } @@ -226,6 +238,7 @@ protected function getEntity() { } elseif (!empty($this->configuration['block_revision_id'])) { $entity = $this->entityTypeManager->getStorage('block_content')->loadRevision($this->configuration['block_revision_id']); + $entity = $this->entityRepository->getActive('block_content', $entity->id()); $this->blockContent = $entity; } else { diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutTranslationItem.php b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutTranslationItem.php index 8691ff17d4..08f14e7532 100644 --- a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutTranslationItem.php +++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutTranslationItem.php @@ -2,12 +2,10 @@ namespace Drupal\layout_builder\Plugin\Field\FieldType; -use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemBase; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\TypedData\DataDefinition; -use Drupal\layout_builder\Section; /** * Plugin implementation of the 'layout_section' field type. @@ -64,4 +62,5 @@ public static function schema(FieldStorageDefinitionInterface $field_definition) return $schema; } + } diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldWidget/LayoutBuilderWidget.php b/core/modules/layout_builder/src/Plugin/Field/FieldWidget/LayoutBuilderWidget.php index 6bd40f7809..18b8046e1e 100644 --- a/core/modules/layout_builder/src/Plugin/Field/FieldWidget/LayoutBuilderWidget.php +++ b/core/modules/layout_builder/src/Plugin/Field/FieldWidget/LayoutBuilderWidget.php @@ -5,6 +5,8 @@ use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\WidgetBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage; +use Drupal\layout_builder\TranslatableSectionStorageInterface; /** * A widget to display the layout form. @@ -15,6 +17,7 @@ * description = @Translation("A field widget for Layout Builder."), * field_types = { * "layout_section", + * "layout_translation" * }, * multiple_values = TRUE, * ) @@ -46,8 +49,19 @@ public function extractFormValues(FieldItemListInterface $items, array $form, Fo if (!$form_state->isValidationComplete()) { return; } - - $items->setValue($this->getSectionStorage($form_state)->getSections()); + $field_name = $this->fieldDefinition->getName(); + $section_storage = $this->getSectionStorage($form_state); + if ($field_name === OverridesSectionStorage::FIELD_NAME) { + $items->setValue($section_storage->getSections()); + } + elseif ($field_name === OverridesSectionStorage::TRANSLATED_CONFIGURATION_FIELD_NAME && $section_storage instanceof TranslatableSectionStorageInterface) { + // The translated configuration is stored in single value field because it + // stores configuration for components in all sections. + $items->set(0, $section_storage->getTranslatedConfiguration()); + } + else { + throw new \LogicException("Widget used with unexpected field, $field_name for section storage: " . $section_storage->getStorageType()); + } } /** diff --git a/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php b/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php index c7a0edc155..eaf3bae7b0 100644 --- a/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php +++ b/core/modules/layout_builder/src/TranslatableSectionStorageInterface.php @@ -32,7 +32,9 @@ public function isDefaultTranslation(); * Sets the translated component configuration. * * @param string $uuid + * The component UUID. * @param array $configuration + * The component's translated configuration. */ public function setTranslatedComponentConfiguration($uuid, array $configuration); @@ -40,9 +42,10 @@ public function setTranslatedComponentConfiguration($uuid, array $configuration) * Gets the translated component configuration. * * @param string $uuid + * The component UUID. * * @return array - * The component configuration. + * The component's translated configuration. */ public function getTranslatedComponentConfiguration($uuid); @@ -50,6 +53,7 @@ public function getTranslatedComponentConfiguration($uuid); * Get the translated configuration for the layout. * * @return array + * The translated configuration for the layout. */ public function getTranslatedConfiguration(); diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php index 3d9a18279e..30466f3e0a 100644 --- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php +++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTranslationTest.php @@ -112,18 +112,15 @@ public function testLayoutPerTranslation() { } - /** * Tests that access is denied to a layout translation if there is override. */ public function testLayoutTranslationNoOverride() { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); $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); diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php index 7188e44577..5aefad604e 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/InlineBlockTranslationTest.php @@ -42,7 +42,6 @@ protected function setUp() { */ public function testInlineBlockContentTranslation() { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', @@ -72,6 +71,7 @@ public function testInlineBlockContentTranslation() { $this->drupalGet('node/1'); $assert_session->pageTextContains('Block en label'); $assert_session->pageTextContains('Block en body'); + $block_id = $this->getLatestBlockEntityId(); // Create a translation. $add_translation_url = Url::fromRoute("entity.node.content_translation_add", [ @@ -96,6 +96,11 @@ public function testInlineBlockContentTranslation() { ); $this->assertSaveLayout(); + $this->assertEquals($block_id, $this->getLatestBlockEntityId(), 'A new block was not created.'); + $this->blockStorage->resetCache([$block_id]); + /** @var \Drupal\block_content\BlockContentInterface $block */ + $block = $this->blockStorage->load($block_id); + $this->assertFalse($block->hasTranslation('it'), 'A block translation was not created when only the label was translatable.'); // Enable translation for block_content type 'bundle_with_section_field'. \Drupal::service('content_translation.manager')->setEnabled('block_content', 'basic', TRUE); @@ -104,38 +109,130 @@ public function testInlineBlockContentTranslation() { $this->drupalGet('it/node/1/layout'); $this->assertNonTranslationActionsRemoved(); - $this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Translate block'); - $textarea = $assert_session->waitForElement('css', '[name="settings[block_form][body][0][value]"]'); - $this->assertNotEmpty($textarea); - $this->assertEquals('Block en body', $textarea->getValue()); - $textarea->setValue('Block it body'); + $this->updateTranslatedBlock('Block it label', 'Block en body', 'Block updated it label', 'Block it body'); - $label_input = $assert_session->elementExists('css', '#drupal-off-canvas [name="settings[translated_label]"]'); - $this->assertNotEmpty($label_input); - $this->assertEquals('Block it label', $label_input->getValue()); - $label_input->setValue('Block Updated it label'); - $page->pressButton('Translate'); + $this->assertEquals($block_id, $this->getLatestBlockEntityId(), 'A new block was not created.'); + $this->blockStorage->resetCache([$block_id]); + /** @var \Drupal\block_content\BlockContentInterface $block */ + $block = $this->blockStorage->load($block_id); + $this->assertFalse($block->hasTranslation('it'), 'A block translation was not created before the layout was saved.'); - $this->assertNoElementAfterWait('#drupal-off-canvas'); - $assert_session->assertWaitOnAjaxRequest(); + $this->assertSaveLayout(); + $this->assertEquals($block_id, $this->getLatestBlockEntityId(), 'A new block was not created'); + $this->blockStorage->resetCache([$block_id]); + /** @var \Drupal\block_content\BlockContentInterface $block */ + $block = $this->blockStorage->load($block_id); + $this->assertTrue($block->hasTranslation('it'), 'A block translation was created when the layout was saved.'); + $block_translation = $block->getTranslation('it'); + $this->assertEquals('Block it body', $block_translation->get('body')->get(0)->getValue()['value'], 'The translated block body field was created correctly.'); + $assert_session->addressEquals('it/node/1'); $assert_session->pageTextContains('Block it body'); - $assert_session->pageTextContains('Block Updated it label'); + $assert_session->pageTextContains('Block updated it label'); $assert_session->pageTextNotContains('Block en body'); $assert_session->pageTextNotContains('Block en label'); + + // Confirm that the default translation was not effected. + $this->drupalGet('node/1'); + $assert_session->pageTextNotContains('Block it body'); + $assert_session->pageTextNotContains('Block updated it label'); + $assert_session->pageTextContains('Block en body'); + $assert_session->pageTextContains('Block en label'); + + // Update the translation inline block again. + $this->drupalGet('it/node/1/layout'); + $this->updateTranslatedBlock('Block updated it label', 'Block it body', 'Block newer updated it label', 'Block updated it body'); $this->assertSaveLayout(); + $this->assertEquals($block_id, $this->getLatestBlockEntityId(), 'A new block was not created.'); + $this->blockStorage->resetCache([$block_id]); + $block = $this->blockStorage->load($block_id); + $block_translation = $block->getTranslation('it'); + $this->assertEquals('Block updated it body', $block_translation->get('body')->get(0)->getValue()['value'], 'The translated block body field was created correctly.'); + $assert_session->addressEquals('it/node/1'); - $assert_session->pageTextContains('Block it body'); - $assert_session->pageTextContains('Block Updated it label'); + $assert_session->pageTextContains('Block updated it body'); + $assert_session->pageTextContains('Block newer updated it label'); $assert_session->pageTextNotContains('Block en body'); $assert_session->pageTextNotContains('Block en label'); + // Confirm that the default translation was not effected. $this->drupalGet('node/1'); - $assert_session->pageTextNotContains('Block it body'); - $assert_session->pageTextNotContains('Block Updated it label'); + $assert_session->pageTextNotContains('Block updated it body'); + $assert_session->pageTextNotContains('Block newer updated it label'); $assert_session->pageTextContains('Block en body'); $assert_session->pageTextContains('Block en label'); + + // Update the default translation's version of the block. + $this->drupalGet('node/1/layout'); + $this->configureInlineBlock('Block en body', 'Block updated en body'); + $this->assertSaveLayout(); + + $assert_session->addressEquals('node/1'); + $assert_session->pageTextNotContains('Block updated it body'); + $assert_session->pageTextNotContains('Block newer updated it label'); + $assert_session->pageTextContains('Block updated en body'); + $assert_session->pageTextContains('Block en label'); + + // Confirm that the translation was not effected. + $this->drupalGet('it/node/1'); + $assert_session->pageTextContains('Block updated it body'); + $assert_session->pageTextContains('Block newer updated it label'); + $assert_session->pageTextNotContains('Block updated en body'); + $assert_session->pageTextNotContains('Block en label'); + + // Update the translation block after updating default translation block. + $this->drupalGet('it/node/1/layout'); + $this->updateTranslatedBlock('Block newer updated it label', 'Block updated it body', 'Block even newer updated it label', 'Block newer updated it body'); + $this->assertSaveLayout(); + + $assert_session->addressEquals('it/node/1'); + $assert_session->pageTextContains('Block newer updated it body'); + $assert_session->pageTextContains('Block even newer updated it label'); + $assert_session->pageTextNotContains('Block updated en body'); + $assert_session->pageTextNotContains('Block en label'); + + $this->drupalGet('node/1'); + $assert_session->pageTextNotContains('Block newer updated it body'); + $assert_session->pageTextNotContains('Block even newer updated it label'); + $assert_session->pageTextContains('Block updated en body'); + $assert_session->pageTextContains('Block en label'); + } + + /** + * Update a translation inline block. + * + * @param string $existing_label + * The inline block's existing label. + * @param string $existing_body + * The inline block's existing body field value. + * @param string $new_label + * The new label. + * @param string $new_body + * The new body field value. + */ + protected function updateTranslatedBlock($existing_label, $existing_body, $new_label, $new_body) { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + $this->clickContextualLink(static::INLINE_BLOCK_LOCATOR, 'Translate block'); + $textarea = $assert_session->waitForElement('css', '[name="settings[block_form][body][0][value]"]'); + $this->assertNotEmpty($textarea); + $this->assertEquals($existing_body, $textarea->getValue()); + $textarea->setValue($new_body); + + $label_input = $assert_session->elementExists('css', '#drupal-off-canvas [name="settings[label]"]'); + $this->assertNotEmpty($label_input); + $this->assertEquals($existing_label, $label_input->getValue()); + $label_input->setValue($new_label); + $page->pressButton('Translate'); + + $this->assertNoElementAfterWait('#drupal-off-canvas'); + $assert_session->assertWaitOnAjaxRequest(); + + $assert_session->pageTextContains($new_label); + $assert_session->pageTextContains($new_label); + $assert_session->pageTextNotContains($existing_label); + $assert_session->pageTextNotContains($existing_body); } } diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/JavascriptTranslationTestTrait.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/JavascriptTranslationTestTrait.php index 639e90a2a4..0b2e054e7f 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/JavascriptTranslationTestTrait.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/JavascriptTranslationTestTrait.php @@ -3,7 +3,7 @@ namespace Drupal\Tests\layout_builder\FunctionalJavascript; /** - * Common functions for test Layout Builder with translations. + * Common functions for testing Layout Builder with translations. */ trait JavascriptTranslationTestTrait {